]>
Commit | Line | Data |
---|---|---|
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) |