b915fda3 |
1 | local cmds = require('commands') |
2 | local getopt = require('getopt') |
3 | local bin = require('bin') |
4 | local lib14a = require('read14a') |
5 | local utils = require('utils') |
6 | local md5 = require('md5') |
7 | local dumplib = require('html_dumplib') |
8 | local toyNames = require('default_toys') |
9 | |
10 | example =[[ |
11 | 1. script run tnp3dump |
12 | 2. script run tnp3dump -n |
13 | 3. script run tnp3dump -k aabbccddeeff |
14 | 4. script run tnp3dump -k aabbccddeeff -n |
15 | 5. script run tnp3dump -o myfile |
16 | 6. script run tnp3dump -n -o myfile |
17 | 7. script run tnp3dump -k aabbccddeeff -n -o myfile |
18 | ]] |
19 | author = "Iceman" |
20 | usage = "script run tnp3dump -k <key> -n -o <filename>" |
21 | desc =[[ |
22 | This script will try to dump the contents of a Mifare TNP3xxx card. |
23 | It will need a valid KeyA in order to find the other keys and decode the card. |
24 | Arguments: |
25 | -h : this help |
26 | -k <key> : Sector 0 Key A. |
27 | -n : Use the nested cmd to find all keys |
28 | -o : filename for the saved dumps |
29 | ]] |
30 | |
31 | local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' |
32 | |
33 | local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds |
34 | local DEBUG = false -- the debug flag |
35 | local numBlocks = 64 |
36 | local numSectors = 16 |
37 | --- |
38 | -- A debug printout-function |
39 | function dbg(args) |
40 | if not DEBUG then |
41 | return |
42 | end |
43 | |
44 | if type(args) == "table" then |
45 | local i = 1 |
46 | while result[i] do |
47 | dbg(result[i]) |
48 | i = i+1 |
49 | end |
50 | else |
51 | print("###", args) |
52 | end |
53 | end |
54 | --- |
55 | -- This is only meant to be used when errors occur |
56 | function oops(err) |
57 | print("ERROR: ",err) |
58 | end |
59 | --- |
60 | -- Usage help |
61 | function help() |
62 | print(desc) |
63 | print("Example usage") |
64 | print(example) |
65 | end |
66 | -- |
67 | -- Exit message |
68 | function ExitMsg(msg) |
69 | print( string.rep('--',20) ) |
70 | print( string.rep('--',20) ) |
71 | print(msg) |
72 | print() |
73 | end |
74 | |
75 | local function readdumpkeys(infile) |
76 | t = infile:read("*all") |
77 | len = string.len(t) |
78 | local len,hex = bin.unpack(("H%d"):format(len),t) |
79 | return hex |
80 | end |
81 | |
82 | local function waitCmd() |
83 | local response = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT) |
84 | if response then |
85 | local count,cmd,arg0 = bin.unpack('LL',response) |
86 | if(arg0==1) then |
87 | local count,arg1,arg2,data = bin.unpack('LLH511',response,count) |
88 | return data:sub(1,32) |
89 | else |
90 | return nil, "Couldn't read block.." |
91 | end |
92 | end |
93 | return nil, "No response from device" |
94 | end |
95 | |
96 | local function computeCrc16(s) |
97 | local hash = core.crc16(utils.ConvertHexToAscii(s)) |
98 | return hash |
99 | end |
100 | |
101 | local function reverseCrcBytes(crc) |
102 | crc2 = crc:sub(3,4)..crc:sub(1,2) |
103 | return tonumber(crc2,16) |
104 | end |
105 | |
106 | local function main(args) |
107 | |
108 | print( string.rep('--',20) ) |
109 | print( string.rep('--',20) ) |
110 | |
111 | local keyA |
112 | local cmd |
113 | local err |
114 | local useNested = false |
115 | local cmdReadBlockString = 'hf mf rdbl %d A %s' |
116 | local input = "dumpkeys.bin" |
117 | local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S"); |
118 | |
119 | -- Arguments for the script |
120 | for o, a in getopt.getopt(args, 'hk:no:') do |
121 | if o == "h" then return help() end |
122 | if o == "k" then keyA = a end |
123 | if o == "n" then useNested = true end |
124 | if o == "o" then outputTemplate = a end |
125 | end |
126 | |
127 | -- validate input args. |
128 | keyA = keyA or '4b0b20107ccb' |
129 | if #(keyA) ~= 12 then |
130 | return oops( string.format('Wrong length of write key (was %d) expected 12', #keyA)) |
131 | end |
132 | |
133 | -- Turn off Debug |
134 | local cmdSetDbgOff = "hf mf dbg 0" |
135 | core.console( cmdSetDbgOff) |
136 | |
137 | result, err = lib14a.read1443a(false) |
138 | if not result then |
139 | return oops(err) |
140 | end |
141 | |
142 | core.clearCommandBuffer() |
143 | |
144 | if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx |
145 | return oops('This is not a TNP3xxx tag. aborting.') |
146 | end |
147 | |
148 | -- Show tag info |
149 | print((' Found tag : %s'):format(result.name)) |
150 | print(('Using keyA : %s'):format(keyA)) |
151 | |
152 | --Trying to find the other keys |
153 | if useNested then |
154 | core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) |
155 | end |
156 | |
157 | core.clearCommandBuffer() |
158 | |
159 | -- Loading keyfile |
160 | print('Loading dumpkeys.bin') |
161 | local hex, err = utils.ReadDumpFile(input) |
162 | if not hex then |
163 | return oops(err) |
164 | end |
165 | |
166 | local akeys = hex:sub(0,12*16) |
167 | |
168 | -- Read block 0 |
169 | cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA} |
170 | err = core.SendCommand(cmd:getBytes()) |
171 | if err then return oops(err) end |
172 | local block0, err = waitCmd() |
173 | if err then return oops(err) end |
174 | |
175 | -- Read block 1 |
176 | cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 1,arg2 = 0,arg3 = 0, data = keyA} |
177 | err = core.SendCommand(cmd:getBytes()) |
178 | if err then return oops(err) end |
179 | local block1, err = waitCmd() |
180 | if err then return oops(err) end |
181 | |
182 | local key |
183 | local pos = 0 |
184 | local blockNo |
185 | local blocks = {} |
186 | |
187 | print('Reading card data') |
188 | core.clearCommandBuffer() |
189 | |
190 | -- main loop |
191 | io.write('Decrypting blocks > ') |
192 | for blockNo = 0, numBlocks-1, 1 do |
193 | |
194 | if core.ukbhit() then |
195 | print("aborted by user") |
196 | break |
197 | end |
198 | |
199 | pos = (math.floor( blockNo / 4 ) * 12)+1 |
200 | key = akeys:sub(pos, pos + 11 ) |
201 | cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key} |
202 | local err = core.SendCommand(cmd:getBytes()) |
203 | if err then return oops(err) end |
204 | local blockdata, err = waitCmd() |
205 | if err then return oops(err) end |
206 | |
207 | if blockNo%4 ~= 3 then |
208 | if blockNo < 8 then |
209 | -- Block 0-7 not encrypted |
210 | blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) |
211 | else |
212 | local base = ('%s%s%02x%s'):format(block0, block1, blockNo, HASHCONSTANT) |
213 | local baseStr = utils.ConvertHexToAscii(base) |
214 | local md5hash = md5.sumhexa(baseStr) |
215 | local aestest = core.aes(md5hash, blockdata) |
216 | |
217 | local hex = utils.ConvertAsciiToBytes(aestest) |
218 | hex = utils.ConvertBytesToHex(hex) |
219 | |
220 | -- blocks with zero not encrypted. |
221 | if string.find(blockdata, '^0+$') then |
222 | blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) |
223 | else |
224 | blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex) |
225 | io.write( blockNo..',') |
226 | end |
227 | end |
228 | else |
229 | -- Sectorblocks, not encrypted |
230 | blocks[blockNo+1] = ('%02d :: %s%s'):format(blockNo,key,blockdata:sub(13,32)) |
231 | end |
232 | end |
233 | io.write('\n') |
234 | |
235 | core.clearCommandBuffer() |
236 | |
237 | -- Print results |
238 | local bindata = {} |
239 | local emldata = '' |
240 | |
241 | for _,s in pairs(blocks) do |
242 | local slice = s:sub(8,#s) |
243 | local str = utils.ConvertBytesToAscii( |
244 | utils.ConvertHexToBytes(slice) |
245 | ) |
246 | emldata = emldata..slice..'\n' |
247 | for c in (str):gmatch('.') do |
248 | bindata[#bindata+1] = c |
249 | end |
250 | end |
251 | |
5149e37e |
252 | |
253 | local uid = block0:sub(1,8) |
254 | local itemtype = block1:sub(1,4) |
255 | local cardid = block1:sub(9,24) |
256 | |
b915fda3 |
257 | -- Write dump to files |
258 | if not DEBUG then |
5149e37e |
259 | local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'_uid_'..uid..'.bin') |
b915fda3 |
260 | print(("Wrote a BIN dump to the file %s"):format(foo)) |
5149e37e |
261 | local bar = dumplib.SaveAsText(emldata, outputTemplate..'_uid_'..uid..'.eml') |
b915fda3 |
262 | print(("Wrote a EML dump to the file %s"):format(bar)) |
263 | end |
264 | |
b915fda3 |
265 | -- Show info |
266 | print( string.rep('--',20) ) |
267 | print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) ) |
268 | print( (' UID : 0x%s'):format(uid) ) |
269 | print( (' CARDID : 0x%s'):format(cardid ) ) |
270 | print( string.rep('--',20) ) |
271 | |
272 | end |
273 | main(args) |