]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/ndef_dump.lua
f89f6db26a4a1a4f77f5a1469eb2e8bee3dd2790
[proxmark3-svn] / client / scripts / ndef_dump.lua
1
2 -- Ability to read what card is there
3 local getopt = require('getopt')
4 local cmds = require('commands')
5 local taglib = require('taglib')
6
7 local desc =
8 [[This script will automatically recognize and dump full content of a NFC NDEF Initialized tag; non-initialized tags will be ignored.
9
10 It also write the dump to an eml-file <uid>.eml.
11
12 (The difference between an .eml-file and a .bin-file is that the eml file contains
13 ASCII representation of the hex-data, with linebreaks between 'rows'. A .bin-file contains the
14 raw data, but when saving into that for, we lose the infromation about how the memory is structured.
15 For example: 24 bytes could be 6 blocks of 4 bytes, or vice versa.
16 Therefore, the .eml is better to use file when saving dumps.)
17
18 Arguments:
19 -d debug logging on
20 -h this help
21
22 ]]
23 local example = "script run xxx"
24 local author = "Martin Holst Swende & Asper"
25 ---
26 -- PrintAndLog
27 function prlog(...)
28 -- TODO; replace this with a call to the proper PrintAndLog
29 print(...)
30 end
31 ---
32 -- This is only meant to be used when errors occur
33 function oops(err)
34 prlog("ERROR: ",err)
35 return nil,err
36 end
37
38
39 -- Perhaps this will be moved to a separate library at one point
40 local utils = {
41 --- Writes an eml-file.
42 -- @param uid - the uid of the tag. Used in filename
43 -- @param blockData. Assumed to be on the format {'\0\1\2\3,'\b\e\e\f' ...,
44 -- that is, blockData[row] contains a string with the actual data, not ascii hex representation
45 -- return filename if all went well,
46 -- @reurn nil, error message if unsuccessfull
47 writeDumpFile = function(uid, blockData)
48 local destination = string.format("%s.eml", uid)
49 local file = io.open(destination, "w")
50 if file == nil then
51 return nil, string.format("Could not write to file %s", destination)
52 end
53 local rowlen = string.len(blockData[1])
54
55 for i,block in ipairs(blockData) do
56 if rowlen ~= string.len(block) then
57 prlog(string.format("WARNING: Dumpdata seems corrupted, line %d was not the same length as line 1",i))
58 end
59
60 local formatString = string.format("H%d", string.len(block))
61 local _,hex = bin.unpack(formatString,block)
62 file:write(hex)
63 file:write(0x0A) -- Line feed
64 end
65 file:close()
66 return destination
67 end,
68 }
69
70
71
72 ---
73 -- Usage help
74 function help()
75 prlog(desc)
76 prlog("Example usage")
77 prlog(example)
78 end
79
80 function debug(...)
81 if DEBUG then
82 prlog("debug:", ...)
83 end
84 end
85
86
87 local function show(data)
88 if DEBUG then
89 local formatString = ("H%d"):format(string.len(data))
90 local _,hexdata = bin.unpack(formatString, data)
91 debug("Hexdata" , hexdata)
92 end
93 end
94 --- Fire up a connection with a tag, return uid
95 -- @return UID if successfull
96 -- @return nil, errormessage if unsuccessfull
97
98 local function open()
99 debug("Opening connection")
100 core.clearCommandBuffer()
101 local x = string.format("hf 14a raw -r -p -s")
102 debug(x)
103 core.console(x)
104 debug("done")
105 data, err = waitCmd(true)
106 if err then return oops(err) end
107 show(data)
108 local formatString = ("H%d"):format(string.len(data))
109 local _,uid = bin.unpack(formatString, data)
110 return uid
111 end
112 --- Shut down tag communication
113 -- return no return values
114 local function close()
115 debug("Closing connection")
116 core.clearCommandBuffer()
117 local x = string.format("hf 14a raw -r")
118 debug(x)
119 core.console(x)
120 debug("done")
121 --data, err = waitCmd(true)
122 --data, err = waitCmd(false)
123
124 end
125
126
127 ---_ Gets data from a block
128 -- @return {block, block+1, block+2, block+3} if successfull
129 -- @return nil, errormessage if unsuccessfull
130 local function getBlock(block)
131 local data, err
132
133 core.clearCommandBuffer()
134
135 local x = string.format("hf 14a raw -r -c -p 30 %02x", block)
136 debug(x)
137 core.console(x)
138 debug("done")
139 -- By now, there should be an ACK waiting from the device, since
140 -- we used the -r flag (don't read response).
141
142 data, err = waitCmd(false)
143 if err then return oops(err) end
144 show(data)
145
146 if string.len(data) < 18 then
147 return nil, ("Expected at least 18 bytes, got %d - this tag is not NDEF-compliant"):format(string.len(data))
148 end
149 -- Now, parse out the block data
150 -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155
151 -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC
152 b0 = string.sub(data,1,4)
153 b1 = string.sub(data,5,8)
154 b2 = string.sub(data,9,12)
155 b3 = string.sub(data,13,16)
156 return {b0,b1,b2,b3}
157 end
158
159
160 --- This function is a lua-implementation of
161 -- cmdhf14a.c:waitCmd(uint8_t iSelect)
162 function waitCmd(iSelect)
163 local response = core.WaitForResponseTimeout(cmds.CMD_ACK,1000)
164 if response then
165 local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',response)
166
167 local iLen = arg0
168 if iSelect then iLen = arg1 end
169 debug(("Received %i octets (arg0:%d, arg1:%d)"):format(iLen, arg0, arg1))
170 if iLen == 0 then return nil, "No response from tag" end
171 local recv = string.sub(response,count, iLen+count-1)
172 return recv
173 end
174 return nil, "No response from device"
175 end
176
177
178
179 local function main( args)
180 debug("script started")
181 local err, data, data2,k,v,i
182 -- Read the parameters
183 for o, a in getopt.getopt(args, 'hd') do
184 if o == "h" then help() return end
185 if o == "d" then DEBUG = true end
186 end
187
188 -- Info contained within the tag (block 0 example)
189 -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155
190 -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC
191 -- MM?? ???? ???? ???? ???? ???? NNVV SS?? ----
192 -- M = Manufacturer info
193 -- N = NDEF-Structure-Compliant (if value is E1)
194 -- V = NFC Forum Specification version (if 10 = v1.0)
195
196 -- First, 'connect' (fire up the field) and get the uid
197 local uidHexstr = open()
198
199 -- First, get blockt 3 byte 2
200 local blocks, err = getBlock(0)
201 if err then return oops(err) end
202 -- Block 3 contains number of blocks
203 local b3chars = {string.byte(blocks[4], 1,4)}
204 local numBlocks = b3chars[3] * 2 + 6
205 prlog("Number of blocks:", numBlocks)
206
207 -- NDEF compliant?
208 if b3chars[1] ~= 0xE1 then
209 return oops("This tag is not NDEF-Complian")
210 end
211
212 local ndefVersion = b3chars[2]
213
214 -- Block 1, byte 1 contains manufacturer info
215 local bl1_b1 = string.byte(blocks[1], 1)
216 local manufacturer = taglib.lookupManufacturer(bl1_b1)
217
218 -- Reuse existing info
219 local blockData = {blocks[1],blocks[2],blocks[3],blocks[4]}
220
221
222 --[[ Due to the infineon my-d move bug
223 (if I send 30 0F i receive block0f+block00+block01+block02 insted of block0f+block10+block11+block12)
224 the only way to avoid this is to send the read command as many times as block numbers
225 removing bytes from 5 to 18 from each answer.
226 --]]
227 prlog("Dumping data...please wait")
228 for i=4,numBlocks-1,1 do
229 blocks, err = getBlock(i)
230 if err then return oops(err) end
231 table.insert(blockData,blocks[1])
232 end
233 -- Deactivate field
234 close()
235 -- Print results
236 prlog(string.format("Tag manufacturer: %s", manufacturer))
237 prlog(string.format("Tag UID: %s", uidHexstr))
238 prlog(string.format("Tag NDEF version: 0x%02x", ndefVersion))
239
240 for k,v in ipairs(blockData) do
241 prlog(string.format("Block %02x: %02x %02x %02x %02x",k-1, string.byte(v, 1,4)))
242 end
243 local filename, err = utils.writeDumpFile(uidHexstr, blockData)
244 if err then return oops(err) end
245
246 prlog(string.format("Dumped data into %s", filename))
247
248 end
249 main(args)
Impressum, Datenschutz