Search code examples
vbaapdumifaresmartcard-readerpcsc

Which APDU to use to read/write records on MIFARE Ultralight NFC tag?


I am creating a VBA software to read and write NFC tags. I managed to communicate with the tag to obtain basic data (for example I got its ID). But when I send an APDU for a READ RECORD command, it does not work. I suppose I need to send a SELECT FILE command before; but I think I need the correct DF for this.

I tried the "00 B2 01 05 00" for the READ RECORD command and "00 A4 00 00 00" for the SELECT FILE command.

VBA
Sub test()
Dim hContext As Long
Dim hCard As Long
Dim retval As Long
Dim readers As String * 256
Dim groups As String * 256
Dim activeprotocol As Long
Dim readerlen As Long
Dim scard_protocol_t0_or_t1 As Long
Dim scard_share_shared As Long
Dim APDU() As Byte
Dim recvbuf() As Byte
Dim recvbuff(256) As ByteArray
Dim recvlen As Long
Dim iosendreq As SCARD_IO_REQUEST
Dim iorecvreq As SCARD_IO_REQUEST
Dim bytRecvAttr As ByteArray
Dim LenAPDU As Long

scard_protocol_t0_or_t1 = 1 Or 2
scard_share_shared = 2 '2  1

retval = SCardEstablishContext(SCARD_SCOPE_USER, 0, 0, hContext)
If retval <> 0 Then
    MsgBox "erreur n." & CStr(retval)
End If

readerlen = 256

retval = SCardListReaders(hContext, groups, readers, readerlen)
If retval <> 0 Then
    MsgBox "erreur n. " & CStr(retval)
End If


Do While hCard = 0
    retval = SCardConnect(hContext, readers, scard_share_shared,         scard_protocol_t0_or_t1, hCard, activeprotocol)
    DoEvents
Loop
If retval <> 0 Then MsgBox "erreur n. " & CStr(retval)

recvlen = 256


Do While State < 2
    retval = SCardStatus(hCard, readers, readerlen, State,     scard_protocol_t0_or_t1, recvbuff(0), recvlen)
    If retval <> 0 Then MsgBox "Erreur n. " & CStr(retval)
    DoEvents
Loop


'retval = SCardGetAttrib(hCard, SCARD_ATTR_ICC_PRESENCE, bytRecvAttr, lRecLen)
'If retval <> 0 Then MsgBox "Erreur n. " & CStr(retval)



iosendreq.dwProtocol = activeprotocol
iosendreq.dwPciLength = Len(iosendreq)
iorecvreq.dwProtocol = activeprotocol
iorecvreq.dwPciLength = Len(iorecvreq)


'recherche de file
ReDim APDU(7)
APDU(0) = &H0    'cla  80
APDU(1) = &HA4  'INs  B2-read record
APDU(2) = &H4   'P1 1-PREMIER RECORD
APDU(3) = &H0   'P2   5-tous les records
APDU(4) = &H2   'longueur de data
APDU(5) = &H0   'a changer pour trouver ID du DF
APDU(6) = &H0   'a changer pour trouver ID du DF
APDU(7) = &H0   'Le


LenAPDU = (UBound(APDU) + 1)
If APDU(LenAPDU - 1) = 0 Then
    ReDim recvbuf(255)
Else
    ReDim recvbuf(APDU(LenAPDU - 1) + 2)
End If
recvlen = UBound(recvbuf) + 1

'this is a loop to test the different DFs
For x = 0 To 255
    For y = 1 To 255
        APDU(5) = x
        APDU(6) = y
        retval = SCardTransmit(hCard, iosendreq, APDU(0), LenAPDU, iorecvreq, recvbuf(0), recvlen)
        If recvbuf(4) > 0 Or recvlen > 2 Then Exit For
    Next
Next

Debug.Print Hex(recvbuf(0)) & "-" & Hex(recvbuf(1)) & "-" & Hex(recvbuf(2)) & "-" & Hex(recvbuf(3)) & "-" & Hex(recvbuf(4)) & "-" & Hex(recvbuf(5)) & "-" & Hex(recvbuf(6)) & "-" & Hex(recvbuf(7)) & " " & recvlen & " CLA:" & APDU(0) & " INS:" & APDU(1) & " P1:" & APDU(2) & " P2:" & APDU(3)


If retval <> 0 Then MsgBox "Erreur n. " & CStr(Hex(retval))

retval = SCardDisconnect(hCard, scad_leave_card)
If retval <> 0 Then MsgBox "erreur n. " & CStr(retval)

retval = SCardReleaseContext(hContext)
If retval <> 0 Then MsgBox "erreur n. " & CStr(retval)

End Sub

Solution

  • MIFARE Ultralight tags do not use APDU commands. Hence, you cannot send any APDUs to the tag itself and the tag does not support the notion of files (EF/DF/MF). Instead, PC/SC (contactless) smartcard readers usually provide a set of APDUs that can be sent to the reader and that the reader translates into MIFARE Ultralight commands. These APDU commands that are processed by the reader typically start with the class byte &HFF.

    If the reader implements the standard PC/SC extensions for access to contactless memory cards, the reader APDU to read from Ultralight tags would look like:

    FF B0 00XX 10
            ^^
             \-------- Block number
    

    Hence, your APDU would look like this:

    ReDim APDU(5)
    APDU(0) = &HFF  'CLA  0xFF = PC/SC READER COMMAND
    APDU(1) = &HB0  'INS  0xB0 = READ BINARY
    APDU(2) = &H00  'P1   0x00
    APDU(3) = &H00  'P2   block number (potentially also in P1 if more than 255)
    APDU(4) = &H10  'Le   0x10 = 16 bytes (= 4 blocks) expected, which is the standard read size for the native Ultralight READ command