text
[RRG-proxmark3.git] / client / luascripts / hf_ndef_dump.lua
blobae071861754d22a7fb0ae1f4ad0f48dd05a77980
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')
6 --
7 -- Refactored iceman, 2019
8 copyright = ''
9 author = 'Martin Holst Swende & Asper'
10 version = 'v1.0.2'
11 desc = [[
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.)
23 example = [[
24 1. script run hf_ndef_dump
26 usage = [[
27 script run hf_ndef_dump [-h] [-d] [-v]
29 arguments = [[
30 -h this help
31 -d debug logging on
32 -v verbose output (from ndef parsing)
36 local DEBUG = true -- the debug flag
37 local band = bit32.band
38 local rshift = bit32.rshift
39 ---
40 -- A debug printout-function
41 local function dbg(args)
42 if not DEBUG then return end
43 if type(args) == 'table' then
44 local i = 1
45 while result[i] do
46 dbg(result[i])
47 i = i+1
48 end
49 else
50 print('###', args)
51 end
52 end
53 ---
54 -- This is only meant to be used when errors occur
55 local function oops(err)
56 print('ERROR:', err)
57 core.clearCommandBuffer()
58 return nil, err
59 end
60 ---
61 -- Usage help
62 local function help()
63 print(copyright)
64 print(author)
65 print(version)
66 print(desc)
67 print(ansicolors.cyan..'Usage'..ansicolors.reset)
68 print(usage)
69 print(ansicolors.cyan..'Arguments'..ansicolors.reset)
70 print(arguments)
71 print(ansicolors.cyan..'Example usage'..ansicolors.reset)
72 print(example)
73 end
75 -- Sends an instruction to do nothing, only disconnect
76 function 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)
81 end
82 ---
84 local function getblockdata(response)
85 if not response then
86 return nil, 'No response from device'
87 end
89 local count, cmd, arg0, arg1, arg2, data = bin.unpack('LLLLH40', response)
90 if arg0 == 1 then
91 return data:sub(1, 32)
92 end
94 return nil, "Couldn't read block"
95 end
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)
100 local block, err
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
105 if #block < 32 then
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
125 local verbose = 0
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)
135 if err then
136 disconnect();
137 return oops(err)
139 core.clearCommandBuffer()
141 if info.name:match("Ultralight") then
142 print('[=] Found a tag')
143 else
144 disconnect()
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)
158 if err then
159 disconnect()
160 return oops(err)
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;
167 -- NDEF compliant?
168 if b3[1] ~= 0xE1 then
169 disconnect()
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.
180 --]]
181 print('[=] Dumping data...')
182 for i = 4, t5tarea_blocks - 1, 1 do
183 blocks, err = getBlock(i)
184 if err then
185 disconnect();
186 return oops(err)
188 table.insert(blockData, blocks[1])
190 -- Deactivate field
191 disconnect()
192 -- Print results
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)
218 local wstr, rstr
220 if rLow == 0 then
221 wstr = 'Write access granted without any security'
222 elseif rLow == 0x0F then
223 wstr = 'No write access granted at all'
224 else
225 wstr = '(RFU)'
228 if rHi ~= 0x00 then
229 rstr = '(RFU)'
230 else
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) )
241 print('')
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))
257 main(args)