]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/ndef_dump.lua
lfem4x cleanup/add/fix
[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.."\n")
63 end
64 file:close()
65 return destination
66 end,
67 }
68
69
70
71 ---
72 -- Usage help
73 function help()
74 prlog(desc)
75 prlog("Example usage")
76 prlog(example)
77 end
78
79 function debug(...)
80 if DEBUG then
81 prlog("debug:", ...)
82 end
83 end
84
85
86 local function show(data)
87 if DEBUG then
88 local formatString = ("H%d"):format(string.len(data))
89 local _,hexdata = bin.unpack(formatString, data)
90 debug("Hexdata" , hexdata)
91 end
92 end
93 --- Fire up a connection with a tag, return uid
94 -- @return UID if successfull
95 -- @return nil, errormessage if unsuccessfull
96
97 local function open()
98 debug("Opening connection")
99 core.clearCommandBuffer()
100 local x = string.format("hf 14a raw -r -p -s")
101 debug(x)
102 core.console(x)
103 debug("done")
104 data, err = waitCmd(true)
105 if err then return oops(err) end
106 show(data)
107 local formatString = ("H%d"):format(string.len(data))
108 local _,uid = bin.unpack(formatString, data)
109 return uid
110 end
111 --- Shut down tag communication
112 -- return no return values
113 local function close()
114 debug("Closing connection")
115 core.clearCommandBuffer()
116 local x = string.format("hf 14a raw -r")
117 debug(x)
118 core.console(x)
119 debug("done")
120 --data, err = waitCmd(true)
121 --data, err = waitCmd(false)
122
123 end
124
125
126 ---_ Gets data from a block
127 -- @return {block, block+1, block+2, block+3} if successfull
128 -- @return nil, errormessage if unsuccessfull
129 local function getBlock(block)
130 local data, err
131
132 core.clearCommandBuffer()
133
134 local x = string.format("hf 14a raw -r -c -p 30 %02x", block)
135 debug(x)
136 core.console(x)
137 debug("done")
138 -- By now, there should be an ACK waiting from the device, since
139 -- we used the -r flag (don't read response).
140
141 data, err = waitCmd(false)
142 if err then return oops(err) end
143 show(data)
144
145 if string.len(data) < 18 then
146 return nil, ("Expected at least 18 bytes, got %d - this tag is not NDEF-compliant"):format(string.len(data))
147 end
148 -- Now, parse out the block data
149 -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155
150 -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC
151 b0 = string.sub(data,1,4)
152 b1 = string.sub(data,5,8)
153 b2 = string.sub(data,9,12)
154 b3 = string.sub(data,13,16)
155 return {b0,b1,b2,b3}
156 end
157
158
159 --- This function is a lua-implementation of
160 -- cmdhf14a.c:waitCmd(uint8_t iSelect)
161 function waitCmd(iSelect)
162 local response = core.WaitForResponseTimeout(cmds.CMD_ACK,1000)
163 if response then
164 local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',response)
165
166 local iLen = arg0
167 if iSelect then iLen = arg1 end
168 debug(("Received %i octets (arg0:%d, arg1:%d)"):format(iLen, arg0, arg1))
169 if iLen == 0 then return nil, "No response from tag" end
170 local recv = string.sub(response,count, iLen+count-1)
171 return recv
172 end
173 return nil, "No response from device"
174 end
175
176
177
178 local function main( args)
179 debug("script started")
180 local err, data, data2,k,v,i
181 -- Read the parameters
182 for o, a in getopt.getopt(args, 'hd') do
183 if o == "h" then help() return end
184 if o == "d" then DEBUG = true end
185 end
186
187 -- Info contained within the tag (block 0 example)
188 -- 0534 00B9 049C AD7F 4A00 0000 E110 1000 2155
189 -- b0b0 b0b0 b1b1 b1b1 b2b2 b2b2 b3b3 b3b3 CRCC
190 -- MM?? ???? ???? ???? ???? ???? NNVV SS?? ----
191 -- M = Manufacturer info
192 -- N = NDEF-Structure-Compliant (if value is E1)
193 -- V = NFC Forum Specification version (if 10 = v1.0)
194
195 -- First, 'connect' (fire up the field) and get the uid
196 local uidHexstr = open()
197
198 -- First, get blockt 3 byte 2
199 local blocks, err = getBlock(0)
200 if err then return oops(err) end
201 -- Block 3 contains number of blocks
202 local b3chars = {string.byte(blocks[4], 1,4)}
203 local numBlocks = b3chars[3] * 2 + 6
204 prlog("Number of blocks:", numBlocks)
205
206 -- NDEF compliant?
207 if b3chars[1] ~= 0xE1 then
208 return oops("This tag is not NDEF-Complian")
209 end
210
211 local ndefVersion = b3chars[2]
212
213 -- Block 1, byte 1 contains manufacturer info
214 local bl1_b1 = string.byte(blocks[1], 1)
215 local manufacturer = taglib.lookupManufacturer(bl1_b1)
216
217 -- Reuse existing info
218 local blockData = {blocks[1],blocks[2],blocks[3],blocks[4]}
219
220
221 --[[ Due to the infineon my-d move bug
222 (if I send 30 0F i receive block0f+block00+block01+block02 insted of block0f+block10+block11+block12)
223 the only way to avoid this is to send the read command as many times as block numbers
224 removing bytes from 5 to 18 from each answer.
225 --]]
226 prlog("Dumping data...please wait")
227 for i=4,numBlocks-1,1 do
228 blocks, err = getBlock(i)
229 if err then return oops(err) end
230 table.insert(blockData,blocks[1])
231 end
232 -- Deactivate field
233 close()
234 -- Print results
235 prlog(string.format("Tag manufacturer: %s", manufacturer))
236 prlog(string.format("Tag UID: %s", uidHexstr))
237 prlog(string.format("Tag NDEF version: 0x%02x", ndefVersion))
238
239 for k,v in ipairs(blockData) do
240 prlog(string.format("Block %02x: %02x %02x %02x %02x",k-1, string.byte(v, 1,4)))
241 end
242 local filename, err = utils.writeDumpFile(uidHexstr, blockData)
243 if err then return oops(err) end
244
245 prlog(string.format("Dumped data into %s", filename))
246
247 end
248 main(args)
Impressum, Datenschutz