1 local getopt
= require('getopt')
2 local cmds
= require('commands')
3 local lib14a
= require('read14a')
4 local utils
= require('utils')
5 local ansicolors
= require('ansicolors')
7 -- Refactored iceman, 2019
9 author
= 'Martin Holst Swende & Asper'
12 This script will automatically recognize and dump full content of a NFC NDEF Initialized tag; non-initialized tags will be ignored.
14 It also write the dump to an eml-file <uid>.eml.
16 (The difference between an .eml-file and a .bin-file is that the eml file contains
17 ASCII representation of the hex-data, with linebreaks between 'rows'. A .bin-file contains the
18 raw data, but when saving into that for, we lose the information about how the memory is structured.
19 For example: 24 bytes could be 6 blocks of 4 bytes, or vice versa.
20 Therefore, the .eml is better to use file when saving dumps.)
24 1. script run hf_ndef_dump
27 script run hf_ndef_dump [-h] [-d] [-v]
32 -v verbose output (from ndef parsing)
36 local DEBUG
= true -- the debug flag
37 local band
= bit32
.band
38 local rshift
= bit32
.rshift
40 -- A debug printout-function
41 local function dbg(args
)
42 if not DEBUG
then return end
43 if type(args
) == 'table' then
54 -- This is only meant to be used when errors occur
55 local function oops(err
)
57 core
.clearCommandBuffer()
67 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
69 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
71 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
75 -- Sends an instruction to do nothing, only disconnect
77 local command
= Command
:newMIX
{cmd
= cmds
.CMD_HF_ISO14443A_READER
, arg1
= 0,}
78 -- We can ignore the response here, no ACK is returned for this command
79 -- Check /armsrc/iso14443a.c, ReaderIso14443a() for details
80 return command
:sendMIX(true)
84 local function getblockdata(response
)
86 return nil, 'No response from device'
89 local count
, cmd
, arg0
, arg1
, arg2
, data
= bin
.unpack('LLLLH40', response
)
91 return data
:sub(1, 32)
94 return nil, "Couldn't read block"
96 ---_ Gets data from a block
97 -- @return {block, block+1, block+2, block+3} if successful
98 -- @return nil, errormessage if unsuccessful
99 local function getBlock(blockno
)
101 local c
= Command
:newMIX
{cmd
= cmds
.CMD_HF_MIFAREU_READBL
, arg1
= blockno
, data
= 0}
102 block
, err
= getblockdata(c
:sendMIX(false))
103 if not block
then return oops(err
) end
106 return nil, ('Expected at least 16 bytes, got %d - this tag is not NDEF-compliant'):format(string.len(data
))
108 -- Now, parse out the block data
109 -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155
110 -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC
111 b0
= string.sub(block
, 1, 8)
112 b1
= string.sub(block
, 9, 16)
113 b2
= string.sub(block
, 17, 24)
114 b3
= string.sub(block
, 25, 32)
115 return {b0
, b1
, b2
, b3
}
119 local function main( args
)
121 print( string.rep('--',20) )
122 print( string.rep('--',20) )
124 local err
, data
, data2
, k
, v
, i
126 -- Read the parameters
127 for o
, a
in getopt
.getopt(args
, 'hdv') do
128 if o
== 'h' then return help() end
129 if o
== 'd' then DEBUG
= true end
130 if o
== 'v' then verbose
= 1 end
133 -- First of all, connect
134 info
, err
= lib14a
.read(true, true)
139 core
.clearCommandBuffer()
141 if info
.name
:match("Ultralight") then
142 print('[=] Found a tag')
145 return oops('[!] Not a Ultralightbased card. This script reads NDEF formatted UL/NTAGS')
148 -- Info contained within the tag (block 0 example)
149 -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155
150 -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC
151 -- MM?? ???? ???? ???? ???? ???? NNVV SS?? ----
152 -- M = Manufacturer info
153 -- N = NDEF-Structure-Compliant (if value is E1)
154 -- V = NFC Forum Specification version (if 10 = v1.0)
156 -- First, get blockt 3 byte 2
157 local blocks
, err
= getBlock(0)
162 -- Block 3 contains number of blocks
163 local b3
= utils
.ConvertHexToBytes(blocks
[4]);
164 local t5tarea
= b3
[3] * 8
165 local t5tarea_blocks
= t5tarea
/ 4;
168 if b3
[1] ~= 0xE1 then
170 return oops('[!] This tag is not NDEF-Compliant')
173 -- Reuse existing info
174 local blockData
= {blocks
[1], blocks
[2], blocks
[3], blocks
[4]}
176 --[[ Due to the infineon my-d move bug
177 (if I send 30 0F i receive block0f+block00+block01+block02 insted of block0f+block10+block11+block12)
178 the only way to avoid this is to send the read command as many times as block numbers
179 removing bytes from 5 to 18 from each answer.
181 print('[=] Dumping data...')
182 for i
= 4, t5tarea_blocks
- 1, 1 do
183 blocks
, err
= getBlock(i
)
188 table.insert(blockData
, blocks
[1])
193 print('[=] --- Tag NDEF Message info')
194 print('[=] '.. string.rep('--', 50) )
195 print('[=] Type : ', info
.name
)
196 print('[=] UID : ', info
.uid
)
197 print('[=] Manufacturer : ', info
.manufacturer
)
198 print('[=] Capacity Container : '.. blockData
[4])
199 print(('[=] %02X : NDEF Magic Number'):format(b3
[1]) )
201 local vLow
= band(b3
[2], 0xF)
202 local vHi
= band(rshift(b3
[2], 4), 0xF)
203 print(('[=] %02X : version %d.%d supported by tag'):format(b3
[2], vHi
, vLow
) )
205 print(('[=] %02X : Physical Memory Size: %d bytes'):format(b3
[3], t5tarea
) )
206 if b3
[3] == 0x96 then
207 print((' %02X : NDEF Memory Size: %d bytes'):format(b3
[3], 48))
208 elseif b3
[3] == 0x12 then
209 print((' %02X : NDEF Memory Size: %d bytes'):format(b3
[3], 144))
210 elseif b3
[3] == 0x3E then
211 print((' %02X : NDEF Memory Size: %d bytes'):format(b3
[3], 496))
212 elseif b3
[3] == 0x6D then
213 print((' %02X : NDEF Memory Size: %d bytes'):format(b3
[3], 872))
216 local rLow
= band(b3
[4], 0xF)
217 local rHi
= band(rshift(b3
[4], 4), 0xF)
221 wstr
= 'Write access granted without any security'
222 elseif rLow
== 0x0F then
223 wstr
= 'No write access granted at all'
231 rstr
= 'Read access granted without any security'
234 print( ('[=] %02X : %s / %s'):format(b3
[4], rstr
, wstr
))
236 print('[=] '.. string.rep('--', 50) )
237 local ndefdata
= table.concat(blockData
, '', 5)
238 core
.ndefparse(t5tarea
, verbose
, ndefdata
)
239 print('[=] '.. string.rep('--', 50) )
242 print('[=] Tag dump')
243 print('|---|-------------------|')
244 for k
,v
in ipairs(blockData
) do
246 -- print(string.format('Block %02x: %02x %02x %02x %02x', k-1, string.byte(v, 1,4)))
247 print(string.format(' %02x | %s', k
-1, v
) )
249 print('|---|-------------------|')
251 local filename
, err
= utils
.WriteDumpFile(info
.uid
, blockData
)
252 if err
then return oops(err
) end
254 print(string.format('[+] Dumped data into %s', filename
))