How to send commands to smart card reader (and not to the smart card) while no card present?
When you stop "Smart Card" service, does the tool still return a firmware version? If yes, then the tool might use raw IOCTL
commands (DeviceIoControl) to communicate with the driver.
Also take a look at this question. The author says you have to set SCARD_PROTOCOL_UNDEFINED
as protocol parameter:
SCardConnect(hSC,
readerState.szReader,
SCARD_SHARE_DIRECT,
SCARD_PROTOCOL_UNDEFINED,
&hCH,
&dwAP
);
I just tried it and it seems to work for Windows 10 at least. Communication was possible with no card inserted. I did not test for other Windows versions though.
Related videos on Youtube
Ebrahim Ghasemi
Passionate Java Card programmer with +6 years of experience in different security related topics, including cryptography, web application and network penetration testing and also reverse engineering. Having strong background in network traffic analysis, deep packet inspection,networking protocols and high-performance system programming.
Updated on June 04, 2022Comments
-
Ebrahim Ghasemi about 2 years
Preface:
I have a dual interface smart card reader that has some extended capabilities (other than sending APDU commands to card and receiving APDU responses).
For example in its document it is mentioned that you can get firmware version of your reader using following command:
GET_FIRMWARE_VERSION:
FF 69 44 42 05 68 92 00 05 00
In its tool, there is a button for this function and it works fine:
I even sniffed USB port to see what exactly exchanged in the connection between my PC and my reader for this function:
Problem:
I want to get my reader version (and maybe send other extended commands) using other tools or via code, but I must insert a card in the card reader to be able sending commands, otherwise I receive
No Card Present
exception, while I don't want to send commands to card! (The reader tool answers successfully to GET_FIRMWARE_VERSION without any card available in the reader's slots)What I did so far:
1.I tried some tools, including OpenSCTool , PyAPDUTool, and another reader's tool. 2.I wrote following python script to send extended commands.
#--- Importing required modules. import sys import time sys.path.append("D:\\PythonX\\Lib\\site-packages") from smartcard.scard import * import smartcard.util from smartcard.System import readers #---This is the list of commands that we want to send device cmds =[[,0xFF,0x69,0x44,0x42,0x05,0x68,0x92,0x00,0x04,0x00],] #--- Let's to make a connection to the card reader r=readers() print "Available Readers :",r print target_reader = input("--- Select Reader (0, 1 , ...): ") print while(True): try: print "Using :",r[target_reader] reader = r[target_reader] connection=reader.createConnection() connection.connect() break except: print "--- Exception occured! (Wrong reader or No card present)" ans = raw_input("--- Try again? (0:Exit/1:Again/2:Change Reader)") if int(ans)==0: exit() elif int(ans)==2: target_reader = input("Select Reader (0, 1 , ...): ") #--- An struct for APDU responses consist of Data, SW1 and SW2 class stru: def __init__(self): self.data = list() self.sw1 = 0 self.sw2 = 0 resp = stru() def send(cmds): for cmd in cmds: #--- Following 5 line added to have a good format of command in the output. temp = stru() ; temp.data[:]=cmd[:] temp.sw1=12 temp.sw2=32 modifyFormat(temp) print "req: ", temp.data resp.data,resp.sw1,resp.sw2 = connection.transmit(cmd) modifyFormat(resp) printResponse(resp) def modifyFormat(resp): resp.sw1=hex(resp.sw1) resp.sw2=hex(resp.sw2) if (len(resp.sw2)<4): resp.sw2=resp.sw2[0:2]+'0'+resp.sw2[2] for i in range(0,len(resp.data)): resp.data[i]=hex(resp.data[i]) if (len(resp.data[i])<4): resp.data[i]=resp.data[i][0:2]+'0'+resp.data[i][2] def printResponse(resp): print "res: ", resp.data,resp.sw1,resp.sw2 send(cmds) connection.disconnect()
Output:
>>> ================================ RESTART ================================ Available Readers : ['CREATOR CRT-603 (CZ1) CCR RF 0', 'CREATOR CRT-603 (CZ1) CCR SAM 0'] --- Select Reader (0, 1 , ...): 0 Using : CREATOR CRT-603 (CZ1) CCR RF 0 --- Exception occured! (Wrong reader or No card present) --- Try again? (0:Exit/1:Again/2:Change Reader) >>> ================================ RESTART ================================ Available Readers : ['CREATOR CRT-603 (CZ1) CCR RF 0', 'CREATOR CRT-603 (CZ1) CCR SAM 0'] --- Select Reader (0, 1 , ...): 1 Using : CREATOR CRT-603 (CZ1) CCR SAM 0 --- Exception occured! (Wrong reader or No card present) --- Try again? (0:Exit/1:Again/2:Change Reader)
But both have the mentioned problem!
Questions:
1- How to send extended commands to reader while there is no card available?
2- Why I can't see command header in the sniffed data? (Note that, as Header is a pre-designated fixed value for all extended commands, I think the reader tool doesn't send the header with GET_FIRMWARE_VERSION command and it send only the data! but how does it works?)
Update:
Using trial and error I found something useful.
Assumptions:
- Pseudo-APDUs fixed header =
FF 69 44 42
- Pseudo-APDU data field for GET_READER_FIRMWARE_VERSION =
68 92 00 04 00
- Pseudo-APDU data field for CHANGE_SAM_SLOT =
68 92 01 00 03 XX 00 00
(My reader has two SAM slots, soXX
can be01
or02
) - SELECT APDU command =
00 A4 04 00 00
Ok, I wrote the following Java Program:
import java.util.List; import java.util.Scanner; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import javax.smartcardio.TerminalFactory; import javax.xml.bind.DatatypeConverter; public class TestPCSC { public static void main(String[] args) throws CardException { TerminalFactory tf = TerminalFactory.getDefault(); List< CardTerminal> terminals = tf.terminals().list(); System.out.println("Available Readers:"); System.out.println(terminals + "\n"); Scanner scanner = new Scanner(System.in); System.out.print("Which reader do you want to send your commands to? (0 or 1 or ...): "); String input = scanner.nextLine(); int readerNum = Integer.parseInt(input); CardTerminal cardTerminal = (CardTerminal) terminals.get(readerNum); Card connection = cardTerminal.connect("DIRECT"); CardChannel cardChannel = connection.getBasicChannel(); System.out.println("Write your commands in Hex form, without '0x' or Space charaters."); System.out.println("\n---------------------------------------------------"); System.out.println("Pseudo-APDU Mode:"); System.out.println("---------------------------------------------------"); while (true) { System.out.println("Pseudo-APDU command: (Enter 0 to send APDU command)"); String cmd = scanner.nextLine(); if (cmd.equals("0")) { break; } System.out.println("Command : " + cmd); byte[] cmdArray = hexStringToByteArray(cmd); byte[] resp = connection.transmitControlCommand(CONTROL_CODE(), cmdArray); String hex = DatatypeConverter.printHexBinary(resp); System.out.println("Response : " + hex + "\n"); } System.out.println("\n---------------------------------------------------"); System.out.println("APDU Mode:"); System.out.println("---------------------------------------------------"); while (true) { System.out.println("APDU command: (Enter 0 to exit)"); String cmd = scanner.nextLine(); if (cmd.equals("0")) { break; } System.out.println("Command : " + cmd); byte[] cmdArray = hexStringToByteArray(cmd); ResponseAPDU resp = cardChannel.transmit(new CommandAPDU(cmdArray)); byte[] respB = resp.getBytes(); String hex = DatatypeConverter.printHexBinary(respB); System.out.println("Response : " + hex + "\n"); } connection.disconnect(true); } public static int CONTROL_CODE() { String osName = System.getProperty("os.name").toLowerCase(); if (osName.indexOf("windows") > -1) { /* Value used by both MS' CCID driver and SpringCard's CCID driver */ return (0x31 << 16 | 3500 << 2); } else { /* Value used by PCSC-Lite */ return 0x42000000 + 1; } } public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; } }
In the above program, I can send commands to my reader using both
connection.transmitControlCommand
andcardChannel.transmit()
methods. The point is that, all the commands that send to the reader using first method, are assumed as Pseudo-APDU command and I should not use the Psedo-APDU header for them! And all the commands that send to the reader using second method, are assumed as regular APDU commands, so if I need to send Pseudo-APDU commands via second method, I must add the Pseudo-APDU header to it.Let see output for the contact-less reader:
run: Available Readers: [PC/SC terminal ACS ACR122 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR RF 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR SAM 0] Which reader do you want to send your commands to? (0 or 1 or ...): 1 Write your commands in Hex form, without '0x' or Space charaters. --------------------------------------------------- Pseudo-APDU Mode: --------------------------------------------------- Pseudo-APDU command: (Enter 0 to send APDU command) 00A4040000 Command : 00A4040000 Response : 6800 //Based on reader's documents, 0x6800 means "Class byte is not correct" //As I have a regular java card in the RF field of my reader, I conclude that //this response is Reader's response (and not card response) Pseudo-APDU command: (Enter 0 to send APDU command) 6892000400 Command : 6892000400 Response : 433630335F435A375F425F31353038323100039000 Pseudo-APDU command: (Enter 0 to send APDU command) FF694442056892000400 Command : FF694442056892000400 Response : 6800 //Pseudo-APDU commands doesn't work in Pseudo-APDU mode if I add the Pseudo-APDU header to them. Pseudo-APDU command: (Enter 0 to send APDU command) 00A4040000 Command : 00A4040000 Response : 6800 Pseudo-APDU command: (Enter 0 to send APDU command) 0 --------------------------------------------------- APDU Mode: --------------------------------------------------- APDU command: (Enter 0 to exit) 00A4040000 Command : 00A4040000 Response : 6F198408A000000018434D00A50D9F6E061291921101009F6501FF9000 APDU command: (Enter 0 to exit) 6892000400 Command : 6892000400 Response : 6E00 //This is the response of my card. I can't receive Firmware version in APDU mode using this command without Pseudo-APDU header. APDU command: (Enter 0 to exit) FF694442056892000400 Command : FF694442056892000400 Response : 433630335F435A375F425F31353038323100099000 //I successfully received Firmware version in APDU mode using the fixed Pseudo-APDU header. APDU command: (Enter 0 to exit) 00A4040000 Command : 00A4040000 Response : 6F198408A000000018434D00A50D9F6E061291921101009F6501FF9000 APDU command: (Enter 0 to exit) 0 BUILD SUCCESSFUL (total time: 1 minute 36 seconds)
Is there any problem still?
Yes, two problems!:
1-the above program works fine only for its first run. I mean, if I stop running and rerun it, the second method throw an exception:
run: Available Readers: [PC/SC terminal ACS ACR122 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR RF 0, PC/SC terminal CREATOR CRT-603 (CZ1) CCR SAM 0] Which reader do you want to send your commands to? (0 or 1 or ...): 1 Write your commands in Hex form, without '0x' or Space charaters. --------------------------------------------------- Pseudo-APDU Mode: --------------------------------------------------- Pseudo-APDU command: (Enter 0 to send APDU command) 00A4040000 Command : 00A4040000 Response : 6800 Pseudo-APDU command: (Enter 0 to send APDU command) FF694442056892000400 Command : FF694442056892000400 Response : 6800 Pseudo-APDU command: (Enter 0 to send APDU command) 6892000400 Command : 6892000400 Response : 433630335F435A375F425F31353038323100049000 Pseudo-APDU command: (Enter 0 to send APDU command) 00A4040000 Command : 00A4040000 Response : 6800 Pseudo-APDU command: (Enter 0 to send APDU command) 0 --------------------------------------------------- APDU Mode: --------------------------------------------------- APDU command: (Enter 0 to exit) 00A4040000 Command : 00A4040000 Exception in thread "main" javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x16 at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:219) at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90) at TestPCSC.main(TestPCSC.java:58) Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x16 at sun.security.smartcardio.PCSC.SCardTransmit(Native Method) at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:188) ... 2 more Java Result: 1 BUILD SUCCESSFUL (total time: 39 seconds)
As you see above, I can't use second method anymore and I need to power-off the reader and power on it again to make it work fine again.
2-The contact interface (I mean the SAM reader) throw previous exception always! I mean the second method doesn`t work at all (neither first run, nor second and third .... )
Note that I tried different readers, it seems that this is not limited only ti this reader. Some ACS readers also have a similar or exactly the same problem with rerun
Does anyone have any idea?
And as a side question, does Python have any equal methods for sending Pseudo-APDU like Java?
And finally, from the view of Reader, what's the difference between
connection.transmitControlCommand
andcardChannel.transmit()
methods?-
Ebrahim Ghasemi@MaartenBodewes Thank you dear Mr Bodewes. I updated the question. About the reader specific library, I think it is not the point in my case. All the tools that my reader has is a portable executable file (without any .sys file or .dll library). Any way I used
tasklist \M
command in windows command line and it return me ` ntdll.dll, wow64.dll, wow64win.dll, wow64cpu.dll` for this executable file process. -
Maarten BodewesYou should be able to check with a dependency viewer which libraries are used by the different tools.
- Pseudo-APDUs fixed header =
-
Ebrahim Ghasemi over 7 yearsDear Amin, Unfortunately I have no more access to the reader to check it. :)
-
arminb over 7 yearsOh, what a pity.
-
arminb about 7 years@Abraham I updated my answer. I was able to successfully communicate to the reader without a card inserted by passing
SCARD_PROTOCOL_UNDEFINED
as protocol parameter. -
Ebrahim Ghasemi about 7 yearsGreat! Thank you for updating the answer and sharing your experience dear Armin.