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