]> cvs.zerfleddert.de Git - proxmark3-svn/blame_incremental - client/scripts/tnp3sim.lua
Bugfix: Output typing on print of HID card formats (#895)
[proxmark3-svn] / client / scripts / tnp3sim.lua
... / ...
CommitLineData
1local cmds = require('commands')
2local getopt = require('getopt')
3local bin = require('bin')
4local lib14a = require('read14a')
5local utils = require('utils')
6local md5 = require('md5')
7local toys = require('default_toys')
8
9example =[[
10 1. script run tnp3sim
11 2. script run tnp3sim -m
12 3. script run tnp3sim -m -i myfile
13]]
14author = "Iceman"
15usage = "script run tnp3sim -h -m -i <filename>"
16desc =[[
17This script will try to load a binary datadump of a Mifare TNP3xxx card.
18It vill try to validate all checksums and view some information stored in the dump
19For an experimental mode, it tries to manipulate some data.
20At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim"
21
22Arguments:
23 -h : this help
24 -m : Maxed out items (experimental)
25 -i : filename for the datadump to read (bin)
26
27 ]]
28
29local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
30local DEBUG = false -- the debug flag
31local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
32
33local band = bit32.band
34local bor = bit32.bor
35local lshift = bit32.lshift
36local rshift = bit32.rshift
37local byte = string.byte
38local char = string.char
39local sub = string.sub
40local format = string.format
41
42
43
44local band = bit32.band
45local bor = bit32.bor
46local lshift = bit32.lshift
47local rshift = bit32.rshift
48local byte = string.byte
49local char = string.char
50local sub = string.sub
51local format = string.format
52
53---
54-- A debug printout-function
55function dbg(args)
56 if not DEBUG then
57 return
58 end
59
60 if type(args) == "table" then
61 local i = 1
62 while result[i] do
63 dbg(result[i])
64 i = i+1
65 end
66 else
67 print("###", args)
68 end
69end
70---
71-- This is only meant to be used when errors occur
72function oops(err)
73 print("ERROR: ",err)
74end
75---
76-- Usage help
77function help()
78 print(desc)
79 print("Example usage")
80 print(example)
81end
82--
83-- Exit message
84function ExitMsg(msg)
85 print( string.rep('--',20) )
86 print( string.rep('--',20) )
87 print(msg)
88 print()
89end
90
91local function writedumpfile(infile)
92 t = infile:read("*all")
93 len = string.len(t)
94 local len,hex = bin.unpack(("H%d"):format(len),t)
95 return hex
96end
97-- blocks with data
98-- there are two dataareas, in block 8 or block 36, ( 1==8 ,
99-- checksum type = 0, 1, 2, 3
100local function GetCheckSum(blocks, dataarea, chksumtype)
101
102 local crc
103 local area = 36
104 if dataarea == 1 then
105 area = 8
106 end
107
108 if chksumtype == 0 then
109 crc = blocks[1]:sub(29,32)
110 elseif chksumtype == 1 then
111 crc = blocks[area]:sub(29,32)
112 elseif chksumtype == 2 then
113 crc = blocks[area]:sub(25,28)
114 elseif chksumtype == 3 then
115 crc = blocks[area]:sub(21,24)
116 end
117 return utils.SwapEndianness(crc,16)
118end
119
120local function SetCheckSum(blocks, chksumtype)
121
122 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
123 local newcrc
124 local area1 = 8
125 local area2 = 36
126
127 if chksumtype == 0 then
128 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0))
129 blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
130 elseif chksumtype == 1 then
131 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1))
132 blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
133 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1))
134 blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
135 elseif chksumtype == 2 then
136 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2))
137 blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32)
138 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2))
139 blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32)
140 elseif chksumtype == 3 then
141 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3))
142 blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32)
143 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3))
144 blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32)
145 end
146end
147
148function CalcCheckSum(blocks, dataarea, chksumtype)
149 local area = 36
150 if dataarea == 1 then
151 area = 8
152 end
153
154 if chksumtype == 0 then
155 data = blocks[0]..blocks[1]:sub(1,28)
156 elseif chksumtype == 1 then
157 data = blocks[area]:sub(1,28)..'0500'
158 elseif chksumtype == 2 then
159 data = blocks[area+1]..blocks[area+2]..blocks[area+4]
160 elseif chksumtype == 3 then
161 data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0)
162 end
163 return utils.Crc16(data)
164end
165
166local function ValidateCheckSums(blocks)
167
168 local isOk, crc, calc
169 -- Checksum Type 0
170 crc = GetCheckSum(blocks,1,0)
171 calc = CalcCheckSum(blocks, 1, 0)
172 if crc == calc then isOk='Ok' else isOk = 'Error' end
173 io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk))
174
175 -- Checksum Type 1 (DATAAREAHEADER 1)
176 crc = GetCheckSum(blocks,1,1)
177 calc = CalcCheckSum(blocks,1,1)
178 if crc == calc then isOk='Ok' else isOk = 'Error' end
179 io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
180
181 -- Checksum Type 1 (DATAAREAHEADER 2)
182 crc = GetCheckSum(blocks,2,1)
183 calc = CalcCheckSum(blocks,2,1)
184 if crc == calc then isOk='Ok' else isOk = 'Error' end
185 io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
186
187 -- Checksum Type 2 (DATAAREA 1)
188 crc = GetCheckSum(blocks,1,2)
189 calc = CalcCheckSum(blocks,1,2)
190 if crc == calc then isOk='Ok' else isOk = 'Error' end
191 io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
192
193 -- Checksum Type 2 (DATAAREA 2)
194 crc = GetCheckSum(blocks,2,2)
195 calc = CalcCheckSum(blocks,2,2)
196 if crc == calc then isOk='Ok' else isOk = 'Error' end
197 io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
198
199 -- Checksum Type 3 (DATAAREA 1)
200 crc = GetCheckSum(blocks,1,3)
201 calc = CalcCheckSum(blocks,1,3)
202 if crc == calc then isOk='Ok' else isOk = 'Error' end
203 io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
204
205 -- Checksum Type 3 (DATAAREA 2)
206 crc = GetCheckSum(blocks,2,3)
207 calc = CalcCheckSum(blocks,2,3)
208 if crc == calc then isOk='Ok' else isOk = 'Error' end
209 io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
210end
211
212 local cmd
213 local blockdata
214 for _,b in pairs(blocks) do
215
216 blockdata = b
217
218 if _%4 ~= 3 then
219 if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
220 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , RANDOM)
221 local baseStr = utils.ConvertHexToAscii(base)
222 local key = md5.sumhexa(baseStr)
223 local enc = core.aes128_encrypt(key, blockdata)
224 local hex = utils.ConvertAsciiToBytes(enc)
225 hex = utils.ConvertBytesToHex(hex)
226
227 blockdata = hex
228 io.write( _..',')
229 end
230 end
231
232 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
233 local err = core.SendCommand(cmd:getBytes())
234 if err then
235 return err
236 end
237 end
238 io.write('\n')
239end
240
241local function Num2Card(m, l)
242
243 local k = {
244 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B,
245 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54,0x56, 0x57, 0x58, 0x59, 0x5A, 0x00
246 }
247 local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
248 local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
249
250 if msw > 0x17ea1 then
251 return "too big"
252 end
253
254 if msw == 0x17ea1 and lsw > 0x8931fee8 then
255 return "out of range"
256 end
257
258 local s = ""
259 local index
260 for i = 1,10 do
261 index, msw, lsw = DivideByK( msw, lsw)
262 if ( index <= 1 ) then
263 s = char(k[index]) .. s
264 else
265 s = char(k[index-1]) .. s
266 end
267 print (index-1, msw, lsw)
268 end
269 return s
270end
271--33LRT-LM9Q9
272--7, 122, 3474858630
273--20, 4, 1008436634
274--7, 0, 627182959
275--17, 0, 21626998
276--16, 0, 745758
277--23, 0, 25715
278--21, 0, 886
279--16, 0, 30
280--1, 0, 1
281--1, 0, 0
282
283function DivideByK(msw, lsw)
284
285 local lowLSW
286 local highLSW
287 local remainder = 0
288 local RADIX = 29
289
290 --local num = 0 | band( rshift(msw,16), 0xffff)
291 local num = band( rshift(msw, 16), 0xffff)
292
293 --highLSW = 0 | lshift( (num / RADIX) , 16)
294 highLSW = lshift( (num / RADIX) , 16)
295 remainder = num % RADIX
296
297 num = bor( lshift(remainder,16), band(msw, 0xffff))
298
299 --highLSW |= num / RADIX
300 highLSW = highLSW or (num / RADIX)
301 remainder = num % RADIX
302
303 num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
304
305 --lowLSW = 0 | (num / RADIX) << 16
306 lowLSW = 0 or (lshift( (num / RADIX), 16))
307 remainder = num % RADIX
308
309 num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
310
311 lowLSW = bor(lowLSW, (num / RADIX))
312 remainder = num % RADIX
313 return remainder, highLSW, lowLSW
314
315 -- uint num = 0 | (msw >> 16) & 0xffff;
316
317 -- highLSW = 0 | (num / RADIX) << 16;
318 -- remainder = num % RADIX;
319
320 -- num = (remainder << 16) | (msw & 0xffff);
321
322 -- highLSW |= num / RADIX;
323 -- remainder = num % RADIX;
324
325 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
326
327 -- lowLSW = 0 | (num / RADIX) << 16;
328 -- remainder = num % RADIX;
329
330 -- num = (remainder << 16) | (lsw & 0xffff);
331
332 -- lowLSW |= num / RADIX;
333 -- remainder = num % RADIX;
334
335end
336
337local function main(args)
338
339 print( string.rep('--',20) )
340 print( string.rep('--',20) )
341
342 local result, err, hex
343 local maxed = false
344 local inputTemplate = "dumpdata.bin"
345 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
346
347 -- Arguments for the script
348 for o, a in getopt.getopt(args, 'hmi:o:') do
349 if o == "h" then return help() end
350 if o == "m" then maxed = true end
351 if o == "o" then outputTemplate = a end
352 if o == "i" then inputTemplate = a end
353 end
354
355 -- Turn off Debug
356 local cmdSetDbgOff = "hf mf dbg 0"
357 core.console( cmdSetDbgOff)
358
359 -- Load dump.bin file
360 print( (' Load data from %s'):format(inputTemplate))
361 hex, err = utils.ReadDumpFile(inputTemplate)
362 if not hex then return oops(err) end
363
364 local blocks = {}
365 local blockindex = 0
366 for i = 1, #hex, 32 do
367 blocks[blockindex] = hex:sub(i,i+31)
368 blockindex = blockindex + 1
369 end
370
371 if DEBUG then
372 print(' Validating checksums')
373 ValidateCheckSums(blocks)
374 end
375
376 --
377 print( string.rep('--',20) )
378 print(' Gathering info')
379 local uid = blocks[0]:sub(1,8)
380 local toytype = blocks[1]:sub(1,4)
381 local cardidLsw = blocks[1]:sub(9,16)
382 local cardidMsw = blocks[1]:sub(17,24)
383 local subtype = blocks[1]:sub(25,28)
384
385 -- Show info
386 print( string.rep('--',20) )
387
388 local item = toys.Find( toytype, subtype)
389 if item then
390 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
391 print(' ITEM TYPE : '..itemStr )
392 else
393 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
394 end
395
396 print( (' UID : 0x%s'):format(uid) )
397 print( (' CARDID : 0x%s %s [%s]'):format(
398 cardidMsw,cardidLsw,
399 --Num2Card(cardidMsw, cardidLsw))
400 '')
401 )
402 print( string.rep('--',20) )
403
404
405 -- Experience should be:
406 local experience = blocks[8]:sub(1,6)
407 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
408
409 local money = blocks[8]:sub(7,10)
410 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
411
412 --
413
414 -- Sequence number
415 local seqnum = blocks[8]:sub(18,19)
416 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
417
418 local fairy = blocks[9]:sub(1,8)
419 --FD0F = Left, FF0F = Right
420 local path = 'not choosen'
421 if fairy:sub(2,2) == 'D' then
422 path = 'Left'
423 elseif fairy:sub(2,2) == 'F' then
424 path = 'Right'
425 end
426 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
427
428 local hat = blocks[9]:sub(8,11)
429 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
430
431 local level = blocks[13]:sub(27,28)
432 print(('LEVEL : %d'):format( tonumber(level,16)))
433