|
/*-filename: main.cpp |
|
* |
|
* Description : This is console application that will scan SimCard readers, connect to one of it, |
|
* send an APDU command, and check for the response--written in a single C++ file. |
|
* Author : Yan Syafri H. (yansyaf@gmail.com) |
|
* Date : 13-Februari 2013 |
|
* Reference : SCM SDK |
|
* |
|
* NOTE : Compiler option, do not use Unicode, use "Multi-Byte Character" instead. |
|
*/ |
|
|
|
#include <iostream> |
|
#include "winscard.h" |
|
#include "assert.h" |
|
|
|
#pragma comment(lib, "Winscard.lib") |
|
|
|
#define NUMBER_OF_READERS 4 |
|
#define NAME_LENGTH 100 |
|
#define MAX_INPUT 1024 |
|
#define MAX_OUTPUT 4000 |
|
#define MAX_RESPONSE 2000 |
|
|
|
void GetErrorCode(long ret); |
|
short AToHex(char *mhstr, BYTE *buf, long *InBufferLength); |
|
|
|
int main() { |
|
|
|
SCARD_READERSTATE ReaderState[NUMBER_OF_READERS]; |
|
SCARD_IO_REQUEST IO_Request; |
|
SCARDCONTEXT ContextHandle; |
|
SCARDHANDLE CardHandle; |
|
|
|
long ret; |
|
|
|
PBYTE pInBuffer; |
|
long InBufferLength; |
|
|
|
PBYTE pOutBuffer; |
|
int OutBufferLine; |
|
short line; |
|
|
|
short act_Name; |
|
short ReaderCount; |
|
char ReaderName[NUMBER_OF_READERS][NAME_LENGTH]; |
|
PBYTE pResponseBuffer; |
|
unsigned long ResponseLength; |
|
long ProtocolType; |
|
|
|
/////////////////////////////// SCAN READER //////////////////////////////////// |
|
memset(ReaderName[0], 0, NAME_LENGTH); |
|
memset(ReaderName[1], 0, NAME_LENGTH); |
|
memset(ReaderName[2], 0, NAME_LENGTH); |
|
memset(ReaderName[3], 0, NAME_LENGTH); |
|
|
|
pOutBuffer = (PBYTE) malloc(MAX_OUTPUT); |
|
assert( pOutBuffer ); |
|
OutBufferLine = 0; |
|
|
|
line = 0; |
|
|
|
pResponseBuffer = (PBYTE) malloc( MAX_RESPONSE ); |
|
assert( pResponseBuffer ); |
|
|
|
memset(pResponseBuffer, 0x00, MAX_RESPONSE); |
|
|
|
ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &ContextHandle); |
|
|
|
if (ret != SCARD_S_SUCCESS) { |
|
return(IDCANCEL); |
|
} |
|
|
|
ReaderCount = 0; |
|
ResponseLength = MAX_RESPONSE; |
|
|
|
ret = SCardListReaders(ContextHandle, 0, (char *) pResponseBuffer, &ResponseLength); |
|
|
|
if (ret != SCARD_S_SUCCESS) { |
|
return(IDCANCEL); |
|
} |
|
else { |
|
unsigned int StringLen = 0; |
|
|
|
while ( ResponseLength > StringLen+1) { |
|
strcpy(ReaderName[ReaderCount], (LPCTSTR) pResponseBuffer+StringLen); |
|
DWORD ActiveProtocol = 0; |
|
ret = SCardConnect(ContextHandle, ReaderName[ReaderCount], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &CardHandle, &ActiveProtocol); |
|
if (ret != SCARD_E_UNKNOWN_READER) |
|
ReaderCount++; |
|
if (ret == SCARD_S_SUCCESS) |
|
SCardDisconnect(CardHandle, SCARD_EJECT_CARD); |
|
StringLen += strlen((LPCTSTR) pResponseBuffer+StringLen+1); |
|
StringLen += 2; |
|
} |
|
} |
|
|
|
if (ReaderCount == 0) { |
|
std::cout << "Reader Unavailable" << endl; |
|
return (IDCANCEL); |
|
} |
|
else |
|
{ |
|
std::cout << "List of Detected Reader: " << endl; |
|
for (int i=0; i<ReaderCount; i++) |
|
{ |
|
std::wcout << i << ": " << ReaderName[i] << endl; |
|
} |
|
|
|
/////////////////////////////// CONNECT //////////////////////////////////// |
|
DWORD ActiveProtocol = 0; |
|
|
|
ProtocolType = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; |
|
|
|
//For this example, we use reader index 0 (ReaderName[0]) |
|
ret = SCardConnect(ContextHandle, ReaderName[0], SCARD_SHARE_EXCLUSIVE, ProtocolType, &CardHandle, &ActiveProtocol); |
|
|
|
if (ret != SCARD_S_SUCCESS){ |
|
GetErrorCode(ret); |
|
return 1; |
|
} |
|
|
|
ProtocolType = ActiveProtocol; |
|
|
|
switch (ProtocolType) { |
|
case SCARD_PROTOCOL_T0: |
|
std::cout << "Function SCardConnect ok\nProtocoltype = T0" << endl; |
|
break; |
|
case SCARD_PROTOCOL_T1: |
|
std::cout << "Function SCardConnect ok\nProtocoltype = T1" << endl; |
|
break; |
|
default: |
|
sprintf((char *) pOutBuffer, "Function SCardConnect ok\n%.8x", ActiveProtocol); |
|
break; |
|
} |
|
|
|
OutBufferLine = 0; |
|
|
|
/////////////////////////////// TRANSMIT //////////////////////////////////// |
|
char mhstr[MAX_INPUT]; |
|
char buf[MAX_INPUT/2]; |
|
|
|
PBYTE pInBuffer; |
|
|
|
memset(mhstr, 0, MAX_INPUT); |
|
|
|
sprintf(mhstr , "51020000FF"); //Insert your custom APDU command here! |
|
|
|
if ( !AToHex((char *) &mhstr, (BYTE *) &buf, &InBufferLength) ) { |
|
return 1; |
|
} |
|
|
|
IO_Request.dwProtocol = ProtocolType; |
|
IO_Request.cbPciLength = (DWORD) sizeof(SCARD_IO_REQUEST); |
|
|
|
pInBuffer = (PBYTE) malloc( MAX_INPUT ); |
|
assert( pInBuffer ); |
|
|
|
memcpy(pInBuffer, buf, InBufferLength); |
|
|
|
ResponseLength = MAX_RESPONSE; |
|
|
|
ret = SCardTransmit(CardHandle, &IO_Request, pInBuffer, InBufferLength, 0, pResponseBuffer, &ResponseLength); |
|
|
|
if (ret != SCARD_S_SUCCESS){ |
|
GetErrorCode(ret); |
|
free(pInBuffer); |
|
return 1; |
|
} |
|
|
|
PBYTE pStartAddress = pOutBuffer; |
|
|
|
for (unsigned long i=0; i<ResponseLength; i++) { |
|
sprintf((char *) pOutBuffer, "%.2x ",(BYTE) pResponseBuffer[i]); |
|
pOutBuffer += 3; |
|
if (i > MAX_RESPONSE-3) |
|
break; |
|
} |
|
|
|
pOutBuffer = pStartAddress; |
|
|
|
std::cout << "Response: " << endl; |
|
for (unsigned long i=0; i<ResponseLength; i++) { |
|
std::cout << pOutBuffer[i]; |
|
} |
|
|
|
free(pInBuffer); |
|
|
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void GetErrorCode(long ret) |
|
{ |
|
switch (ret) { |
|
case SCARD_E_CANCELLED: |
|
std::cout << "ERROR: The action was cancelled by an SCardCancel request." << endl; |
|
break; |
|
|
|
case SCARD_E_CANT_DISPOSE: |
|
std::cout << "ERROR: The system could not dispose of the media in the requested manner" << endl; |
|
break; |
|
case SCARD_E_CARD_UNSUPPORTED: |
|
std::cout << "ERROR: The smart card does not meet minimal requirements for support" << endl; |
|
break; |
|
case SCARD_E_DUPLICATE_READER: |
|
std::cout << "ERROR: The reader driver didn't produce a unique reader name" << endl; |
|
break; |
|
case SCARD_E_INSUFFICIENT_BUFFER: |
|
std::cout << "ERROR: The data buffer to receive returned data is too small for the returned data" << endl; |
|
break; |
|
case SCARD_E_INVALID_ATR: |
|
std::cout << "ERROR: An ATR obtained from the registry is not a valid ATR string" << endl; |
|
break; |
|
case SCARD_E_INVALID_HANDLE: |
|
std::cout << "ERROR: The supplied handle was invalid" << endl; |
|
break; |
|
case SCARD_E_INVALID_PARAMETER: |
|
std::cout << "ERROR: One or more of the supplied parameters could not be properly interpreted" << endl; |
|
break; |
|
case SCARD_E_INVALID_TARGET: |
|
std::cout << "ERROR: Registry startup information is missing or invalid" << endl; |
|
break; |
|
case SCARD_E_INVALID_VALUE: |
|
std::cout << "ERROR: One or more of the supplied parameters’ values could not be properly interpreted" << endl; |
|
break; |
|
case SCARD_E_NOT_READY: |
|
std::cout << "ERROR: The reader or card is not ready to accept commands" << endl; |
|
break; |
|
case SCARD_E_NOT_TRANSACTED: |
|
std::cout << "ERROR: An attempt was made to end a non-existent transaction" << endl; |
|
break; |
|
case SCARD_E_NO_MEMORY: |
|
std::cout << "ERROR: Not enough memory available to complete this command" << endl; |
|
break; |
|
case SCARD_E_NO_SERVICE: |
|
std::cout << "ERROR: The Smart card resource manager is not running" << endl; |
|
break; |
|
case SCARD_E_NO_SMARTCARD: |
|
std::cout << "ERROR: The operation requires a smart card but no smart card is currently in the device" << endl; |
|
break; |
|
case SCARD_E_PCI_TOO_SMALL: |
|
std::cout << "ERROR: The PCI Receive buffer was too small" << endl; |
|
break; |
|
case SCARD_E_PROTO_MISMATCH: |
|
std::cout << "ERROR: The requested protocols are incompatible with the protocol currently in use with the card" << endl; |
|
break; |
|
case SCARD_E_READER_UNAVAILABLE: |
|
std::cout << "ERROR: The specified reader is not currently available for use" << endl; |
|
break; |
|
case SCARD_E_READER_UNSUPPORTED: |
|
std::cout << "ERROR: The reader driver does not meet minimal requirements for support" << endl; |
|
break; |
|
case SCARD_E_SERVICE_STOPPED: |
|
std::cout << "ERROR: The Smart card resource manager has shut down" << endl; |
|
break; |
|
case SCARD_E_SHARING_VIOLATION: |
|
std::cout << "ERROR: The card cannot be accessed because of other connections outstanding" << endl; |
|
break; |
|
case SCARD_E_SYSTEM_CANCELLED: |
|
std::cout << "ERROR: The action was cancelled by the system presumably to log off or shut down" << endl; |
|
break; |
|
case SCARD_E_TIMEOUT: |
|
std::cout << "ERROR: The user-specified timeout value has expired" << endl; |
|
break; |
|
case SCARD_E_UNKNOWN_CARD: |
|
std::cout << "ERROR: The specified card name is not recognized" << endl; |
|
break; |
|
case SCARD_E_UNKNOWN_READER: |
|
std::cout << "ERROR: The specified reader name is not recognized" << endl; |
|
break; |
|
case SCARD_F_COMM_ERROR: |
|
std::cout << "ERROR: An internal communications error has been detected" << endl; |
|
break; |
|
case SCARD_F_INTERNAL_ERROR: |
|
std::cout << "ERROR: An internal consistency check failed" << endl; |
|
break; |
|
case SCARD_F_UNKNOWN_ERROR: |
|
std::cout << "ERROR: An internal error has been detected but the source is unknown" << endl; |
|
break; |
|
case SCARD_F_WAITED_TOO_LONG: |
|
std::cout << "ERROR: An internal consistency timer has expired" << endl; |
|
break; |
|
case SCARD_S_SUCCESS: |
|
std::cout << "ERROR: OK" << endl; |
|
break; |
|
case SCARD_W_REMOVED_CARD: |
|
std::cout << "ERROR: The card has been removed so that further communication is not possible" << endl; |
|
break; |
|
case SCARD_W_RESET_CARD: |
|
std::cout << "ERROR: The card has been reset so any shared state information is invalid" << endl; |
|
break; |
|
case SCARD_W_UNPOWERED_CARD: |
|
std::cout << "ERROR: Power has been removed from the card so that further communication is not possible" << endl; |
|
break; |
|
case SCARD_W_UNRESPONSIVE_CARD: |
|
std::cout << "ERROR: The card is not responding to a reset" << endl; |
|
break; |
|
case SCARD_W_UNSUPPORTED_CARD: |
|
std::cout << "ERROR: The reader cannot communicate with the card due to ATR configuration conflicts" << endl; |
|
break; |
|
default: |
|
std::cout << "ERROR: Function returned " << ret << " error code." << endl; |
|
break; |
|
} |
|
} |
|
|
|
short AToHex(char *mhstr, BYTE *buf, long *InBufferLength) |
|
{ |
|
|
|
*InBufferLength = strlen(mhstr); |
|
if (*InBufferLength < 3) { |
|
std::cout << "ERROR: Input not complete!" << endl; |
|
return(0); |
|
} |
|
for (short i=0; i<*InBufferLength; i++) { |
|
if (mhstr[i] == '\n') { |
|
mhstr[i] = 0; |
|
} |
|
else if (((mhstr[i] < '0') || (mhstr[i] > '9')) && |
|
((mhstr[i] != ' ') && (mhstr[i] != ',')) && |
|
((mhstr[i] < 'a') || (mhstr[i] > 'f')) && |
|
((mhstr[i] < 'A') || (mhstr[i] > 'F'))) { |
|
std::cout << "Only hex. input allowed!" << endl; |
|
return(0); |
|
} |
|
} |
|
|
|
char *s; |
|
if (mhstr[1] == ',') { |
|
*InBufferLength = (*InBufferLength - 4)/2; |
|
s = mhstr+4; |
|
} |
|
else { |
|
*InBufferLength = *InBufferLength/2; |
|
s = mhstr; |
|
} |
|
memset(buf, 0, sizeof (buf)); |
|
int i = 0; |
|
|
|
while (isspace(*s)) s++; |
|
|
|
short k = FALSE; |
|
while (*s) { |
|
if (isxdigit(*s)) { |
|
buf[i] <<= 4; |
|
if (isalpha(*s)) |
|
buf[i] |= (tolower(*s) - 0x57); |
|
else |
|
buf[i] |= (*s - 0x30); |
|
|
|
if (k) { |
|
i++; |
|
k= FALSE; |
|
} |
|
else { |
|
k= TRUE; |
|
} |
|
s++; |
|
} |
|
else break; |
|
} |
|
return(1); |
|
} |