Merge pull request #1327 from Pondorasti/patch-1
[RRG-proxmark3.git] / client / luascripts / hf_mf_tnp3_dump.lua
blob8e6a6bfb3598237a67e4dbdd128fd4596ef48053
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 toys = require('default_toys')
9 local ansicolors = require('ansicolors')
11 copyright = ''
12 author = 'Iceman'
13 version = 'v1.0.3'
14 desc = [[
15 This script will try to dump the contents of a Mifare TNP3xxx card.
16 It will need a valid KeyA in order to find the other keys and decode the card.
18 example = [[
19 script run hf_mf_tnp3_dump
20 script run hf_mf_tnp3_dump -n
21 script run hf_mf_tnp3_dump -p
22 script run hf_mf_tnp3_dump -k aabbccddeeff
23 script run hf_mf_tnp3_dump -k aabbccddeeff -n
24 script run hf_mf_tnp3_dump -o myfile
25 script run hf_mf_tnp3_dump -n -o myfile
26 script run hf_mf_tnp3_dump -p -o myfile
27 script run hf_mf_tnp3_dump -k aabbccddeeff -n -o myfile
29 usage = [[
30 script run hf_mf_tnp3_dump [-h] [-k <key>] [-n] [-p] [-o <filename>]
32 arguments = [[
33 -h : this help
34 -k <key> : Sector 0 Key A.
35 -n : Use the nested cmd to find all keys
36 -p : Use the precalc to find all keys
37 -o : filename for the saved dumps
40 local PM3_SUCCESS = 0
41 local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
42 local DEBUG = false -- the debug flag
43 local numBlocks = 64
44 local numSectors = 16
45 ---
46 -- A debug printout-function
47 local function dbg(args)
48 if not DEBUG then return end
49 if type(args) == 'table' then
50 local i = 1
51 while result[i] do
52 dbg(result[i])
53 i = i+1
54 end
55 else
56 print('###', args)
57 end
58 end
59 ---
60 -- This is only meant to be used when errors occur
61 local function oops(err)
62 print('ERROR:', err)
63 core.clearCommandBuffer()
64 return nil, err
65 end
66 ---
67 -- Usage help
68 local function help()
69 print(copyright)
70 print(author)
71 print(version)
72 print(desc)
73 print(ansicolors.cyan..'Usage'..ansicolors.reset)
74 print(usage)
75 print(ansicolors.cyan..'Arguments'..ansicolors.reset)
76 print(arguments)
77 print(ansicolors.cyan..'Example usage'..ansicolors.reset)
78 print(example)
79 end
81 -- Exit message
82 local function ExitMsg(msg)
83 print( string.rep('--',20) )
84 print( string.rep('--',20) )
85 print(msg)
86 print()
87 end
89 local function readdumpkeys(infile)
90 t = infile:read("*all")
91 len = string.len(t)
92 local len,hex = bin.unpack(("H%d"):format(len),t)
93 return hex
94 end
96 local function getblockdata(response)
97 if not response then
98 return nil, 'No response from device'
99 end
100 if response.Status == PM3_SUCCESS then
101 return response.Data
102 else
103 return nil, "Couldn't read block.. ["..response.Status.."]"
107 local function main(args)
109 print( string.rep('--',20) )
110 print( string.rep('--',20) )
112 local keyA, cmd, err
113 local useNested = false
114 local usePreCalc = false
115 local cmdReadBlockString = 'hf mf rdbl --blk %d -k %s'
116 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S");
118 -- Arguments for the script
119 for o, a in getopt.getopt(args, 'hk:npo:') do
120 if o == "h" then return help() end
121 if o == "k" then keyA = a end
122 if o == "n" then useNested = true end
123 if o == "p" then usePreCalc = true end
124 if o == "o" then outputTemplate = a end
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))
133 -- Turn off Debug
134 local cmdSetDbgOff = "hw dbg -0"
135 core.console( cmdSetDbgOff)
136 utils.Sleep(0.5)
138 tag, err = lib14a.read(false, true)
139 if not tag then return oops(err) end
141 core.clearCommandBuffer()
143 -- Show tag info
144 print((' Found tag %s'):format(tag.name))
146 dbg(('Using keyA : %s'):format(keyA))
148 --Trying to find the other keys
149 if useNested then
150 core.console( ('hf mf nested -t 1 -b 0 --keya -k %s --dumpkeys'):format(keyA) )
153 core.clearCommandBuffer()
155 local akeys = ''
156 if usePreCalc then
157 local pre = require('precalc')
158 akeys = pre.GetAll(tag.uid)
159 dbg(akeys)
160 else
161 local filename = ('hf-mf-%s-key.bin'):format(tag.uid);
162 print('loading '..filename)
163 local hex, err = utils.ReadDumpFile(filename)
164 if not hex then
165 print('loading dumpkeys.bin')
166 hex, err = utils.ReadDumpFile('dumpkeys.bin')
167 if not hex then
168 return oops(err)
171 akeys = hex:sub(0,12*16)
174 local block0, block1
175 -- Read block 0
176 dbg('Reading block 0')
177 local blockno = '00'
178 local keytype = '00'
179 local data = ('%s%s%s'):format(blockno, keytype, keyA)
180 cmd = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = data}
181 block0, err = getblockdata(cmd:sendNG(false))
182 if not block0 then return oops(err) end
184 core.clearCommandBuffer()
186 -- Read block 1
187 dbg('Reading block 1')
188 local blockno = '01'
189 data = ('%s%s%s'):format(blockno, keytype, keyA)
190 cmd = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = data}
191 block1, err = getblockdata(cmd:sendNG(false))
192 if not block1 then return oops(err) end
194 core.clearCommandBuffer()
196 local tmpHash = block0..block1..'%02x'..RANDOM
198 local key
199 local pos = 0
200 local blockNo
201 local blocks = {}
203 -- main loop
204 io.write('Reading blocks > ')
205 for blockNo = 0, numBlocks-1, 1 do
207 io.flush()
209 if core.kbd_enter_pressed() then
210 print("aborted by user")
211 break
214 core.clearCommandBuffer()
216 pos = (math.floor( blockNo / 4 ) * 12)+1
217 key = akeys:sub(pos, pos + 11 )
218 data = ('%02x%s%s'):format(blockNo, keytype, key)
219 cmd = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = data}
220 local blockdata, err = getblockdata(cmd:sendNG(false))
221 if not blockdata then return oops(err) end
223 if blockNo%4 ~= 3 then
225 if blockNo < 8 then
226 -- Block 0-7 not encrypted
227 blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata)
228 else
229 -- blocks with zero not encrypted.
230 if string.find(blockdata, '^0+$') then
231 blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata)
232 else
233 local baseStr = utils.ConvertHexToAscii(tmpHash:format(blockNo))
234 local key = md5.sumhexa(baseStr)
235 local aestest = core.aes128_decrypt(key, blockdata)
236 local hex = utils.ConvertAsciiToHex(aestest)
238 blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex)
239 io.write(blockNo..',')
242 else
243 -- Sectorblocks, not encrypted
244 blocks[blockNo+1] = ('%02d :: %s%s'):format(blockNo,key,blockdata:sub(13,32))
247 io.write('\n')
249 core.clearCommandBuffer()
251 -- Print results
252 local bindata = {}
253 local emldata = ''
255 for _,s in pairs(blocks) do
256 local slice = s:sub(8,#s)
257 local str = utils.ConvertHexToAscii(slice)
258 emldata = emldata..slice..'\n'
259 for c in (str):gmatch('.') do
260 bindata[#bindata+1] = c
264 print( string.rep('--',20) )
266 local uid = block0:sub(1,8)
267 local toytype = block1:sub(1,4)
268 local cardidLsw = block1:sub(9,16)
269 local cardidMsw = block1:sub(16,24)
270 local cardid = block1:sub(9,24)
271 local subtype = block1:sub(25,28)
273 -- Write dump to files
274 if not DEBUG then
275 local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'-'..uid..'.bin')
276 print(("Wrote a BIN dump to: %s"):format(foo))
277 local bar = dumplib.SaveAsText(emldata, outputTemplate..'-'..uid..'.eml')
278 print(("Wrote a EML dump to: %s"):format(bar))
281 print( string.rep('--',20) )
282 -- Show info
284 local item = toys.Find(toytype, subtype)
285 if item then
286 print((' ITEM TYPE : %s - %s (%s)'):format(item[6],item[5], item[4]) )
287 else
288 print((' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype))
291 print( (' UID : 0x%s'):format(uid) )
292 print( (' CARDID : 0x%s'):format(cardid ) )
293 print( string.rep('--',20) )
295 core.clearCommandBuffer()
297 main(args)