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')
11 2. script run tnp3sim -m
12 3. script run tnp3sim -m -i myfile
15 usage = "script run tnp3sim -h -m -i <filename>"
17 This script will try to load a binary datadump of a Mifare TNP3xxx card.
18 It vill try to validate all checksums and view some information stored in the dump
19 For an experimental mode, it tries to manipulate some data.
20 At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim"
24 -m : Maxed out items (experimental)
25 -i : filename for the datadump to read (bin)
29 local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
30 local DEBUG = false -- the debug flag
31 local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
33 local band = bit32.band
35 local lshift = bit32.lshift
36 local rshift = bit32.rshift
37 local byte = string.byte
38 local char = string.char
39 local sub = string.sub
40 local format = string.format
44 local band = bit32.band
46 local lshift = bit32.lshift
47 local rshift = bit32.rshift
48 local byte = string.byte
49 local char = string.char
50 local sub = string.sub
51 local format = string.format
54 -- A debug printout-function
60 if type(args) == "table" then
71 -- This is only meant to be used when errors occur
79 print("Example usage")
85 print( string.rep('--',20) )
86 print( string.rep('--',20) )
91 local function writedumpfile(infile)
92 t = infile:read("*all")
94 local len,hex = bin.unpack(("H%d"):format(len),t)
98 -- there are two dataareas, in block 8 or block 36, ( 1==8 ,
99 -- checksum type = 0, 1, 2, 3
100 local function GetCheckSum(blocks, dataarea, chksumtype)
104 if dataarea == 1 then
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)
117 return utils.SwapEndianness(crc,16)
120 local function SetCheckSum(blocks, chksumtype)
122 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
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)
148 function CalcCheckSum(blocks, dataarea, chksumtype)
150 if dataarea == 1 then
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)
163 return utils.Crc16(data)
166 local function ValidateCheckSums(blocks)
168 local isOk, crc, calc
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))
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))
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))
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))
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))
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))
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))
214 for _,b in pairs(blocks) do
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)
232 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
233 local err = core.SendCommand(cmd:getBytes())
241 local function Num2Card(m, l)
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
247 local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
248 local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
250 if msw > 0x17ea1 then
254 if msw == 0x17ea1 and lsw > 0x8931fee8 then
255 return "out of range"
261 index, msw, lsw = DivideByK( msw, lsw)
262 if ( index <= 1 ) then
263 s = char(k[index]) .. s
265 s = char(k[index-1]) .. s
267 print (index-1, msw, lsw)
283 function DivideByK(msw, lsw)
290 --local num = 0 | band( rshift(msw,16), 0xffff)
291 local num = band( rshift(msw, 16), 0xffff)
293 --highLSW = 0 | lshift( (num / RADIX) , 16)
294 highLSW = lshift( (num / RADIX) , 16)
295 remainder = num % RADIX
297 num = bor( lshift(remainder,16), band(msw, 0xffff))
299 --highLSW |= num / RADIX
300 highLSW = highLSW or (num / RADIX)
301 remainder = num % RADIX
303 num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
305 --lowLSW = 0 | (num / RADIX) << 16
306 lowLSW = 0 or (lshift( (num / RADIX), 16))
307 remainder = num % RADIX
309 num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
311 lowLSW = bor(lowLSW, (num / RADIX))
312 remainder = num % RADIX
313 return remainder, highLSW, lowLSW
315 -- uint num = 0 | (msw >> 16) & 0xffff;
317 -- highLSW = 0 | (num / RADIX) << 16;
318 -- remainder = num % RADIX;
320 -- num = (remainder << 16) | (msw & 0xffff);
322 -- highLSW |= num / RADIX;
323 -- remainder = num % RADIX;
325 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
327 -- lowLSW = 0 | (num / RADIX) << 16;
328 -- remainder = num % RADIX;
330 -- num = (remainder << 16) | (lsw & 0xffff);
332 -- lowLSW |= num / RADIX;
333 -- remainder = num % RADIX;
337 local function main(args)
339 print( string.rep('--',20) )
340 print( string.rep('--',20) )
342 local result, err, hex
344 local inputTemplate = "dumpdata.bin"
345 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
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
356 local cmdSetDbgOff = "hf mf dbg 0"
357 core.console( cmdSetDbgOff)
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
366 for i = 1, #hex, 32 do
367 blocks[blockindex] = hex:sub(i,i+31)
368 blockindex = blockindex + 1
372 print(' Validating checksums')
373 ValidateCheckSums(blocks)
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)
386 print( string.rep('--',20) )
388 local item = toys.Find( toytype, subtype)
390 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
391 print(' ITEM TYPE : '..itemStr )
393 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
396 print( (' UID : 0x%s'):format(uid) )
397 print( (' CARDID : 0x%s %s [%s]'):format(
399 --Num2Card(cardidMsw, cardidLsw))
402 print( string.rep('--',20) )
405 -- Experience should be:
406 local experience = blocks[8]:sub(1,6)
407 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
409 local money = blocks[8]:sub(7,10)
410 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
415 local seqnum = blocks[8]:sub(18,19)
416 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
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
423 elseif fairy:sub(2,2) == 'F' then
426 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
428 local hat = blocks[9]:sub(8,11)
429 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
431 local level = blocks[13]:sub(27,28)
432 print(('LEVEL : %d'):format( tonumber(level,16)))
434 --local health = blocks[]:sub();
435 --print(('Health : %d'):format( tonumber(health,16))
437 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
438 local heropoints = blocks[13]:sub(20,23)
439 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
441 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
442 local challenges = blocks[16]:sub(25,32)
443 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
446 local name1 = blocks[10]:sub(1,32)
447 local name2 = blocks[12]:sub(1,32)
448 print('Custom name : '..utils.ConvertHexToAscii(name1..name2))
451 print('Lets try to max out some values')
452 -- max out money, experience
454 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
455 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
458 -- max out hero challenges
460 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
461 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
464 -- max out heropoints
466 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
467 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
471 print('Updating all checksums')
472 SetCheckSum(blocks, 3)
473 SetCheckSum(blocks, 2)
474 SetCheckSum(blocks, 1)
475 SetCheckSum(blocks, 0)
477 print('Validating all checksums')
478 ValidateCheckSums(blocks)
481 --Load dumpdata to emulator memory
483 print('Sending dumpdata to emulator memory')
484 err = LoadEmulator(blocks)
485 if err then return oops(err) end
486 core.clearCommandBuffer()
487 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')