Good day everyone,
I'm trying to experiment with winscard.dll for smart card interaction.
But seem i'm not clever enough to understand how to use fews apis.
Here is my code:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc ; Win32 API
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\winscard.inc ; SmartCard API
includelib \masm32\lib\winscard.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm ; need that one too as i use chr$()
.data
SzReader db "HID Global OMNIKEY 3x21 Smart Card Reader 0",0
.data?
SzphContext DWORD ?
SzComActiveProtocol DWORD ?
SzCardHandle DWORD ?
chReaderLen DWORD ?
atrlen DWORD ?
SzpdwState DWORD ?
SzpdwProtocol DWORD ?
SzpbAtr LPBYTE 32 dup(?)
SzpbAtrlen DWORD 32 dup(?)
SzReaderlen DWORD 32 dup(?)
.code
start:
invoke SCardEstablishContext,SCARD_SCOPE_USER,NULL,NULL,addr SzphContext
invoke SCardConnect,SzphContext,addr SzReader,SCARD_SHARE_SHARED,SCARD_PROTOCOL_T0 or SCARD_PROTOCOL_T1,addr SzCardHandle,addr SzComActiveProtocol
invoke SCardGetAttrib,SzCardHandle,07A007h,addr SzpbAtr,addr SzpbAtrlen; 7A007h: SCARD_ATTR_MAXINPUT (maximum size of an APDU supported by the reader).
.if eax == ERROR_NOT_SUPPORTED ;One or more of the supplied parameters could not be properly interpreted.
invoke MessageBox,NULL,chr$("Could not determinate reader maximum input length"),chr$("problem"),MB_ICONINFORMATION
.endif
; Provides the current status of a smart card in a reader.
;LONG SCardStatusA(
; SCARDHANDLE hCard, (Reference value returned from SCardConnect.)
; LPSTR mszReaderNames, (List of display names (multiple string) by which the currently connected reader is known.)
; LPDWORD pcchReaderLen, (On input, supplies the length of the szReaderName buffer. On output, receives the actual length (in characters) of the reader name list, including the trailing NULL character)
; LPDWORD pdwState, (Current state of the smart card in the reader. Upon success, it receives state indicators)
; LPDWORD pdwProtocol, (Current protocol, if any. The returned value is meaningful only if the returned value of pdwState is SCARD_SPECIFICMODE.)
; LPBYTE pbAtr, (Pointer to a 32-byte buffer that receives the ATR string from the currently inserted card, if available.)
; LPDWORD pcbAtrLen (On input, supplies the length of the pbAtr buffer. On output, receives the number of bytes in the ATR string (32 bytes maximum).)
;);
invoke SCardStatus,SzCardHandle,addr SzReader,addr SzReaderlen,addr SzpdwState,addr SzpdwProtocol,addr SzpbAtr,addr SzpbAtrlen
invoke SCardDisconnect,SzCardHandle,SCARD_LEAVE_CARD
invoke ExitProcess,0
end start
SCardGetAttrib return me eax=32 (ERROR_NOT_SUPPORTED), i reproduced a call to SCardGetAttrib with cardpeak (a software) and i got the same error code with this dwAttrId.
I saw also this thread (https://stackoverflow.com/questions/17672965/getting-smart-card-serial-number-scardgetattrib-returns-error-50) on stackoverflow and after reading the answer i guess my code is ok? at least that this behavior might be normal. But if someone can check that my convention is good?
My problem is more with SCardStatus, something is wrong with my buffers, i should normally expect to have in pbAtr: 3B F9 13 00 00 81 31 FE 45 4A 43 4F 50 32 34 32 52 33 A2 but all i got are zeros
You should check the return values: on my machine, I get SCARD_E_NO_SERVICE:
//
// The Smart card resource manager is not running.
//
#define SCARD_E_NO_SERVICE ((DWORD)0x8010001DL)
SCardConnect
eax 6
SzCardHandle 0
SCardGetAttrib
eax 6
SzCardHandle 0
SCardStatus
eax 6
SzCardHandle 0
SzReader 72
SzReaderlen 0
SzpdwState 0
SzpdwProtocol 0
SzpbAtr 0
SzpbAtrlen 0
invoke SCardEstablishContext,SCARD_SCOPE_USER,NULL,NULL,addr SzphContext
invoke SCardConnect,SzphContext,addr SzReader,SCARD_SHARE_SHARED,SCARD_PROTOCOL_T0 or SCARD_PROTOCOL_T1,addr SzCardHandle,addr SzComActiveProtocol
deb 4, "SCardConnect", eax, SzCardHandle
invoke SCardGetAttrib,SzCardHandle,07A007h,addr SzpbAtr,addr SzpbAtrlen; 7A007h: SCARD_ATTR_MAXINPUT (maximum size of an APDU supported by the reader).
deb 4, "SCardGetAttrib", eax, SzCardHandle
Hi jj2007,
the handles are good when connected to a smart card reader and when a smart card is inside.
EA010000 (SzCardHandle, hCard)
00401010 |. 68 6C304000 PUSH ; /Arg6 = scard.40306C, pdwActiveProtocol
00401015 |. 68 70304000 PUSH ; |Arg5 = scard.403070, phCard
0040101A |. 6A 03 PUSH ; |Arg4 = 3, dwPreferredProtocols
0040101C |. 6A 02 PUSH ; |Arg3 = 2, dwShareMode
0040101E |. 68 00304000 PUSH ; |Arg2 = ASCII "HID Global OMNIKEY 3x21 Smart Card Reader 0", szReader
00401023 |. FF35 68304000 PUSH ; |Arg1 = CD010000, hContext
00401029 |. E8 70000000 CALL ; \winscard.SCardConnectA
0040102E |. 68 04314000 PUSH ; /Arg4 = scard.403104, pcbAttrLen
00401033 |. 68 84304000 PUSH ; |Arg3 = scard.403084, pbAttr
00401038 |. 68 07A00700 PUSH ; |Arg2 = 7A007, dwAttrId
0040103D |. FF35 70304000 PUSH ; |Arg1 = EA010000, hCard
00401043 |. E8 68000000 CALL ; \winscard.SCardGetAttrib
00401048 |. 83F8 32 CMP
0040104B |. 75 13 JNE
0040104D |. 6A 40 PUSH ; /Type = MB_OK|MB_ICONASTERISK|MB_DEFBUTTON1|MB_APPLMODAL
0040104F |. 68 60304000 PUSH ; |Caption = "problem"
00401054 |. 68 2C304000 PUSH ; |Text = "Could not determinate reader maximum input length"
00401059 |. 6A 00 PUSH ; |hOwner = NULL
0040105B |. E8 5C000000 CALL ; \USER32.MessageBoxA
00401060 |> 68 04314000 PUSH ; /Arg7 = scard.403104, pcbAtrLen
00401065 |. 68 84304000 PUSH ; |Arg6 = scard.403084, pbAtr
0040106A |. 68 80304000 PUSH ; |Arg5 = scard.403080, pdwProtocol
0040106F |. 68 7C304000 PUSH ; |Arg4 = scard.40307C, pdwState
00401074 |. 68 84314000 PUSH ; |Arg3 = scard.403184, pcchReaderLen
00401079 |. 68 00304000 PUSH ; |Arg2 = ASCII "HID Global OMNIKEY 3x21 Smart Card Reader 0", szReader
0040107E |. FF35 70304000 PUSH ; |Arg1 = EA010000, hCard
00401084 |. E8 2D000000 CALL ; \winscard.SCardStatusA
If it wasn't good i would get the error code SCARD_E_INVALID_HANDLE, but here i got everywhere SCARD_S_SUCCESS (no prob) after invokes.
I still think there is a problem with the last arguments addr SzpbAtr,addr SzpbAtrlen, it should be related to this i suppose, i'm unsure of these twos as SzpbAtr is lpbytes; i don't know if i'm doing it good, or if it looks incorrect of the C++ Syntax example (https://docs.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardstatusa) in msdn, but my doubts are on this, i may be wrong.
Hello,
I have rebuild your code in windows 10.No error in my computer ,try it and see what happen
I've modified Yves' code a little bit, so that it builds with the Masm32 SDK, but no luck, I get runtime errors. Probably because I don't have a smart card reader :biggrin:
Does it work for people who do have a smard card reader? Source & exe attached.
Hi guys,
both of your example works for me, and it express the same behavior as my code.
After calling SCardStatusA this is what i have on the memory dump:
SzpdwState: 06 (SCARD_SPECIFIC)
SzpdwProtocol: 02 (meaning SCARD_PROTOCOL_T1 is in use)
SzpbAtr: 00 (?)
SzpbAtrlen: 12 (SzpbAtr has a buffer size of 18 zeros?)
SzReaderlen: 2D
I'm looking at cardpeak under a debugger again to understand the differences, i figured out with a hw breakpoint on their atr buffer that it does SCardGetStatusChangeA before SCardStatus. (but they have already the handle as they call SCardEstablishContext at initialization to after do a SCardListReadersA)
And that seem to return the atr, i don't understand where did that come from, it seem not for that and they havent called SCardStatus yet? https://docs.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardgetstatuschangea
After looking at cardpeak code, this is done on pcsc_driver.c https://github.com/L1L1/cardpeek/blob/master/drivers/pcsc_driver.c#L141
I tried to translate it to masm, i've added this line after invoke SCardEstablishContext:
invoke SCardGetStatusChange,SzphContext,INFINITE,SCARD_STATE_UNAWARE,1
But i get the error code 80100004 (SCARD_E_INVALID_PARAMETER)
why does it fail?
Handle is ok?
SzphContext is a pointer to one properly filled SCARD_READERSTATE structure?
yes handle SzphContext (the handle of the smart card reader got from previous call on SCardEstablishContext) is correct, i can see it while debugging.
00401010 |. 6A 01 PUSH 1 ; /Arg4 = 1 (1)
00401012 |. 6A 00 PUSH 0 ; |Arg3 = 0 (SCARD_STATE_UNAWARE)
00401014 |. 6A FF PUSH -1 ; |Arg2 = -1 (INFINITE)
00401016 |. FF35 683 PUSH DWORD PTR DS:[403068] ; |Arg1 = CD010000 (SzphContext)
0040101C |. E8 A5000 CALL <JMP.&winscard.SCardGetStatusC ; \winscard.SCardGetStatusChangeA
It is rgReaderStates who should point to SCARD_READERSTATE?, in my case it is: SCARD_STATE_UNAWARE (0x0000), looks good too in the debugger
edit: oh my bad.. rgReaderStates is array so it start at zero.
invoke SCardGetStatusChange,SzphContext,INFINITE,SCARD_STATE_UNAWARE,0
now it return SCARD_S_SUCCESS but i still don't see any trace of my atr inside SzpbAtr after calling SCardStatus hmm...
I see two examples on SOF (https://stackoverflow.com/questions/11294638/how-to-use-scardgetstatuschange-correctly-on-windows-8) with different types of parameters:
SCardGetStatusChange(
context.get(),
INFINITE,
readersstaterange.begin(),
lib::rng::size_cast<DWORD>(readersstaterange.size())
M$ doc looks like this: (https://docs.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardgetstatuschangea):
SCardGetStatusChangeA(
SCARDCONTEXT hContext,
DWORD dwTimeout,
LPSCARD_READERSTATEA rgReaderStates,
DWORD cReaders
)
... where rgReaderStates is "An array of SCARD_READERSTATE structures". You put the SCARD_STATE_UNAWARE constant, i.e. zero, but your #readers is 1. Have you tried 0?
dumpbin on the obj can help
00000026: 68 00 00 00 00 push offset _SzphContext
0000002B: 6A 00 push 0
0000002D: 6A 00 push 0
0000002F: 6A 00 push 0
00000031: E8 00 00 00 00 call _SCardEstablishContext@16
00000036: 68 00 00 00 00 push offset _SzComActiveProtocol
0000003B: 68 00 00 00 00 push offset _SzCardHandle
00000040: 6A 03 push 3
00000042: 6A 02 push 2
00000044: 68 00 00 00 00 push offset _SzReader
00000049: FF 35 00 00 00 00 push dword ptr [_SzphContext]
0000004F: E8 00 00 00 00 call _SCardConnectA@24
00000054: 68 00 00 00 00 push offset _SzpbAtrlen
00000059: 68 00 00 00 00 push offset _SzpbAtr
0000005E: 68 07 A0 07 00 push 7A007h
00000063: FF 35 00 00 00 00 push dword ptr [_SzCardHandle]
00000069: E8 00 00 00 00 call _SCardGetAttrib@16
0000006E: 83 F8 32 cmp eax,32h
00000071: 75 13 jne 00000086
00000073: 6A 40 push 40h
00000075: 68 00 00 00 00 push offset _??000D
0000007A: 68 00 00 00 00 push offset _??000C
0000007F: 6A 00 push 0
00000081: E8 00 00 00 00 call _MessageBoxA@16
00000086: 68 00 00 00 00 push offset _SzpbAtrlen
0000008B: 68 00 00 00 00 push offset _SzpbAtr
00000090: 68 00 00 00 00 push offset _SzpdwProtocol
00000095: 68 00 00 00 00 push offset _SzpdwState
0000009A: 68 00 00 00 00 push offset _SzReaderlen
0000009F: 68 00 00 00 00 push offset _SzReader
000000A4: FF 35 00 00 00 00 push dword ptr [_SzCardHandle]
000000AA: E8 00 00 00 00 call _SCardStatusA@28
000000AF: 6A 00 push 0
000000B1: FF 35 00 00 00 00 push dword ptr [_SzCardHandle]
000000B7: E8 00 00 00 00 call _SCardDisconnect@8
000000BC: 68 00 00 00 00 push offset _firstreverse
000000C1: 68 00 00 00 00 push offset _??000E
000000C6: E8 00 00 00 00 call _printf
000000CB: 83 C4 08 add esp,8
000000CE: E8 00 00 00 00 call __getch
000000D3: C3 ret
a bit late but solved.
needed simply to give SzpbAtrlen something else than zero.
mov SzpbAtrlen,20
invoke SCardStatus,SzCardHandle,addr SzReader,addr SzReaderlen,addr SzpdwState,addr SzpdwProtocol,addr SzpbAtr,addr SzpbAtrlen
works and return the smart card atr as well as it's size on SzpbAtrlen :thumbsup: