]> git.zerfleddert.de Git - proxmark3-svn/blame - client/scripts/tnp3sim.lua
chg: @piwi's code cleanup and some more.
[proxmark3-svn] / client / scripts / tnp3sim.lua
CommitLineData
b915fda3 1local cmds = require('commands')
2local getopt = require('getopt')
3local bin = require('bin')
4local lib14a = require('read14a')
5local utils = require('utils')
6local md5 = require('md5')
cff17e78 7local toys = require('default_toys')
a826cb0d 8local pre = require('precalc')
9
b915fda3 10example =[[
11 1. script run tnp3sim
12 2. script run tnp3sim -m
13 3. script run tnp3sim -m -i myfile
14]]
15author = "Iceman"
16usage = "script run tnp3sim -h -m -i <filename>"
17desc =[[
18This script will try to load a binary datadump of a Mifare TNP3xxx card.
19It vill try to validate all checksums and view some information stored in the dump
20For an experimental mode, it tries to manipulate some data.
21At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim"
22
23Arguments:
24 -h : this help
25 -m : Maxed out items (experimental)
26 -i : filename for the datadump to read (bin)
b915fda3 27
1b3c567d 28 ]]
04a6113f 29
1b3c567d 30local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
a826cb0d 31local DEBUG = true -- the debug flag
1b3c567d 32local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
04a6113f 33
34local band = bit32.band
35local bor = bit32.bor
36local lshift = bit32.lshift
37local rshift = bit32.rshift
38local byte = string.byte
39local char = string.char
40local sub = string.sub
41local format = string.format
42
b915fda3 43---
44-- A debug printout-function
45function dbg(args)
a826cb0d 46 if not DEBUG then return end
b915fda3 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
57end
58---
59-- This is only meant to be used when errors occur
60function oops(err)
61 print("ERROR: ",err)
3b4fa542 62 return nil,err
b915fda3 63end
64---
65-- Usage help
66function help()
67 print(desc)
68 print("Example usage")
69 print(example)
70end
71--
72-- Exit message
73function ExitMsg(msg)
74 print( string.rep('--',20) )
75 print( string.rep('--',20) )
76 print(msg)
77 print()
78end
79
b915fda3 80local 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
85end
86-- blocks with data
87-- there are two dataareas, in block 8 or block 36, ( 1==8 ,
88-- checksum type = 0, 1, 2, 3
89local 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)
107end
108
a826cb0d 109local function SetAllCheckSum(blocks)
110 print('Updating all checksums')
111 SetCheckSum(blocks, 3)
112 SetCheckSum(blocks, 2)
113 SetCheckSum(blocks, 1)
114 SetCheckSum(blocks, 0)
115end
116
b915fda3 117local 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
143end
144
145function 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)
161end
162
163local function ValidateCheckSums(blocks)
a826cb0d 164 print(' Validating checksums')
165
b915fda3 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))
b915fda3 208
d8a3b6c1 209end
a826cb0d 210
211local 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)
215end
216
217local function LoadEmulator(uid, blocks)
218 print('Sending dumpdata to emulator memory')
219 local keys = pre.GetAll(uid)
220 local cmd, blockdata
b915fda3 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
1b3c567d 227 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , RANDOM)
b915fda3 228 local baseStr = utils.ConvertHexToAscii(base)
229 local key = md5.sumhexa(baseStr)
1b3c567d 230 local enc = core.aes128_encrypt(key, blockdata)
a826cb0d 231 blockdata = utils.ConvertAsciiToHex(enc)
b915fda3 232 io.write( _..',')
233 end
a826cb0d 234 else
235 -- add keys if not existing..
236 if ( blockdata:sub(1,12) == '000000000000' ) then
237 blockdata = AddKey(keys, _, blockdata)
238 end
b915fda3 239 end
a826cb0d 240 core.clearCommandBuffer()
f14c9bf9 241 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 16, data = blockdata}
b915fda3 242 local err = core.SendCommand(cmd:getBytes())
f14c9bf9 243 if err then return err end
b915fda3 244 end
245 io.write('\n')
246end
247
04a6113f 248local 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
277end
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
290function 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
40762506 322 -- uint num = 0 | (msw >> 16) & 0xffff;
04a6113f 323
40762506 324 -- highLSW = 0 | (num / RADIX) << 16;
325 -- remainder = num % RADIX;
04a6113f 326
40762506 327 -- num = (remainder << 16) | (msw & 0xffff);
04a6113f 328
40762506 329 -- highLSW |= num / RADIX;
330 -- remainder = num % RADIX;
04a6113f 331
40762506 332 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
04a6113f 333
40762506 334 -- lowLSW = 0 | (num / RADIX) << 16;
335 -- remainder = num % RADIX;
04a6113f 336
40762506 337 -- num = (remainder << 16) | (lsw & 0xffff);
04a6113f 338
40762506 339 -- lowLSW |= num / RADIX;
340 -- remainder = num % RADIX;
04a6113f 341
342end
343
b915fda3 344local 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
b915fda3 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
a826cb0d 378 if DEBUG then ValidateCheckSums(blocks) end
b915fda3 379
380 --
381 print( string.rep('--',20) )
382 print(' Gathering info')
383 local uid = blocks[0]:sub(1,8)
c3fe354b 384 local toytype = blocks[1]:sub(1,4)
04a6113f 385 local cardidLsw = blocks[1]:sub(9,16)
386 local cardidMsw = blocks[1]:sub(17,24)
c3fe354b 387 local subtype = blocks[1]:sub(25,28)
b915fda3 388
389 -- Show info
390 print( string.rep('--',20) )
c3fe354b 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])
1b3c567d 395 print(' ITEM TYPE : '..itemStr )
c3fe354b 396 else
397 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
398 end
399
b915fda3 400 print( (' UID : 0x%s'):format(uid) )
04a6113f 401 print( (' CARDID : 0x%s %s [%s]'):format(
402 cardidMsw,cardidLsw,
40762506 403 --Num2Card(cardidMsw, cardidLsw))
404 '')
04a6113f 405 )
b915fda3 406 print( string.rep('--',20) )
407
04a6113f 408
1b3c567d 409 -- Experience should be:
b915fda3 410 local experience = blocks[8]:sub(1,6)
1b3c567d 411 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
412
b915fda3 413 local money = blocks[8]:sub(7,10)
414 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
1b3c567d 415
416 --
417
418 -- Sequence number
419 local seqnum = blocks[8]:sub(18,19)
420 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
421
b915fda3 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)))
1b3c567d 434
435 local level = blocks[13]:sub(27,28)
436 print(('LEVEL : %d'):format( tonumber(level,16)))
a826cb0d 437
1b3c567d 438 --local health = blocks[]:sub();
439 --print(('Health : %d'):format( tonumber(health,16))
b915fda3 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
1b3c567d 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
b915fda3 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
a826cb0d 475 SetAllCheckSum(blocks)
476
477 -- Validate Checksums
b915fda3 478 ValidateCheckSums(blocks)
479 end
a826cb0d 480
b915fda3 481 --Load dumpdata to emulator memory
482 if DEBUG then
a826cb0d 483 err = LoadEmulator(uid, blocks)
b915fda3 484 if err then return oops(err) end
485 core.clearCommandBuffer()
8e726f6c 486 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')
b915fda3 487 end
488end
489main(args)
Impressum, Datenschutz