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
43 -- A debug printout-function
49 if type(args) == "table" then
60 -- This is only meant to be used when errors occur
69 print("Example usage")
75 print( string.rep('--',20) )
76 print( string.rep('--',20) )
81 local function writedumpfile(infile)
82 t = infile:read("*all")
84 local len,hex = bin.unpack(("H%d"):format(len),t)
88 -- there are two dataareas, in block 8 or block 36, ( 1==8 ,
89 -- checksum type = 0, 1, 2, 3
90 local function GetCheckSum(blocks, dataarea, chksumtype)
98 if chksumtype == 0 then
99 crc = blocks[1]:sub(29,32)
100 elseif chksumtype == 1 then
101 crc = blocks[area]:sub(29,32)
102 elseif chksumtype == 2 then
103 crc = blocks[area]:sub(25,28)
104 elseif chksumtype == 3 then
105 crc = blocks[area]:sub(21,24)
107 return utils.SwapEndianness(crc,16)
110 local function SetCheckSum(blocks, chksumtype)
112 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
117 if chksumtype == 0 then
118 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0))
119 blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
120 elseif chksumtype == 1 then
121 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1))
122 blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
123 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1))
124 blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
125 elseif chksumtype == 2 then
126 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2))
127 blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32)
128 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2))
129 blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32)
130 elseif chksumtype == 3 then
131 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3))
132 blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32)
133 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3))
134 blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32)
138 function CalcCheckSum(blocks, dataarea, chksumtype)
140 if dataarea == 1 then
144 if chksumtype == 0 then
145 data = blocks[0]..blocks[1]:sub(1,28)
146 elseif chksumtype == 1 then
147 data = blocks[area]:sub(1,28)..'0500'
148 elseif chksumtype == 2 then
149 data = blocks[area+1]..blocks[area+2]..blocks[area+4]
150 elseif chksumtype == 3 then
151 data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0)
153 return utils.Crc16(data)
156 local function ValidateCheckSums(blocks)
158 local isOk, crc, calc
160 crc = GetCheckSum(blocks,1,0)
161 calc = CalcCheckSum(blocks, 1, 0)
162 if crc == calc then isOk='Ok' else isOk = 'Error' end
163 io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk))
165 -- Checksum Type 1 (DATAAREAHEADER 1)
166 crc = GetCheckSum(blocks,1,1)
167 calc = CalcCheckSum(blocks,1,1)
168 if crc == calc then isOk='Ok' else isOk = 'Error' end
169 io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
171 -- Checksum Type 1 (DATAAREAHEADER 2)
172 crc = GetCheckSum(blocks,2,1)
173 calc = CalcCheckSum(blocks,2,1)
174 if crc == calc then isOk='Ok' else isOk = 'Error' end
175 io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
177 -- Checksum Type 2 (DATAAREA 1)
178 crc = GetCheckSum(blocks,1,2)
179 calc = CalcCheckSum(blocks,1,2)
180 if crc == calc then isOk='Ok' else isOk = 'Error' end
181 io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
183 -- Checksum Type 2 (DATAAREA 2)
184 crc = GetCheckSum(blocks,2,2)
185 calc = CalcCheckSum(blocks,2,2)
186 if crc == calc then isOk='Ok' else isOk = 'Error' end
187 io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
189 -- Checksum Type 3 (DATAAREA 1)
190 crc = GetCheckSum(blocks,1,3)
191 calc = CalcCheckSum(blocks,1,3)
192 if crc == calc then isOk='Ok' else isOk = 'Error' end
193 io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
195 -- Checksum Type 3 (DATAAREA 2)
196 crc = GetCheckSum(blocks,2,3)
197 calc = CalcCheckSum(blocks,2,3)
198 if crc == calc then isOk='Ok' else isOk = 'Error' end
199 io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
202 local function LoadEmulator(blocks)
205 for _,b in pairs(blocks) do
210 if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
211 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , RANDOM)
212 local baseStr = utils.ConvertHexToAscii(base)
213 local key = md5.sumhexa(baseStr)
214 local enc = core.aes128_encrypt(key, blockdata)
215 local hex = utils.ConvertAsciiToBytes(enc)
216 hex = utils.ConvertBytesToHex(hex)
223 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 16, data = blockdata}
224 local err = core.SendCommand(cmd:getBytes())
225 if err then return err end
230 local function Num2Card(m, l)
233 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B,
234 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54,0x56, 0x57, 0x58, 0x59, 0x5A, 0x00
236 local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
237 local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
239 if msw > 0x17ea1 then
243 if msw == 0x17ea1 and lsw > 0x8931fee8 then
244 return "out of range"
250 index, msw, lsw = DivideByK( msw, lsw)
251 if ( index <= 1 ) then
252 s = char(k[index]) .. s
254 s = char(k[index-1]) .. s
256 print (index-1, msw, lsw)
272 function DivideByK(msw, lsw)
279 --local num = 0 | band( rshift(msw,16), 0xffff)
280 local num = band( rshift(msw, 16), 0xffff)
282 --highLSW = 0 | lshift( (num / RADIX) , 16)
283 highLSW = lshift( (num / RADIX) , 16)
284 remainder = num % RADIX
286 num = bor( lshift(remainder,16), band(msw, 0xffff))
288 --highLSW |= num / RADIX
289 highLSW = highLSW or (num / RADIX)
290 remainder = num % RADIX
292 num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
294 --lowLSW = 0 | (num / RADIX) << 16
295 lowLSW = 0 or (lshift( (num / RADIX), 16))
296 remainder = num % RADIX
298 num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
300 lowLSW = bor(lowLSW, (num / RADIX))
301 remainder = num % RADIX
302 return remainder, highLSW, lowLSW
304 -- uint num = 0 | (msw >> 16) & 0xffff;
306 -- highLSW = 0 | (num / RADIX) << 16;
307 -- remainder = num % RADIX;
309 -- num = (remainder << 16) | (msw & 0xffff);
311 -- highLSW |= num / RADIX;
312 -- remainder = num % RADIX;
314 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
316 -- lowLSW = 0 | (num / RADIX) << 16;
317 -- remainder = num % RADIX;
319 -- num = (remainder << 16) | (lsw & 0xffff);
321 -- lowLSW |= num / RADIX;
322 -- remainder = num % RADIX;
326 local function main(args)
328 print( string.rep('--',20) )
329 print( string.rep('--',20) )
331 local result, err, hex
333 local inputTemplate = "dumpdata.bin"
334 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
336 -- Arguments for the script
337 for o, a in getopt.getopt(args, 'hmi:o:') do
338 if o == "h" then return help() end
339 if o == "m" then maxed = true end
340 if o == "o" then outputTemplate = a end
341 if o == "i" then inputTemplate = a end
345 local cmdSetDbgOff = "hf mf dbg 0"
346 core.console( cmdSetDbgOff)
348 -- Load dump.bin file
349 print( (' Load data from %s'):format(inputTemplate))
350 hex, err = utils.ReadDumpFile(inputTemplate)
351 if not hex then return oops(err) end
355 for i = 1, #hex, 32 do
356 blocks[blockindex] = hex:sub(i,i+31)
357 blockindex = blockindex + 1
361 print(' Validating checksums')
362 ValidateCheckSums(blocks)
366 print( string.rep('--',20) )
367 print(' Gathering info')
368 local uid = blocks[0]:sub(1,8)
369 local toytype = blocks[1]:sub(1,4)
370 local cardidLsw = blocks[1]:sub(9,16)
371 local cardidMsw = blocks[1]:sub(17,24)
372 local subtype = blocks[1]:sub(25,28)
375 print( string.rep('--',20) )
377 local item = toys.Find( toytype, subtype)
379 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
380 print(' ITEM TYPE : '..itemStr )
382 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
385 print( (' UID : 0x%s'):format(uid) )
386 print( (' CARDID : 0x%s %s [%s]'):format(
388 --Num2Card(cardidMsw, cardidLsw))
391 print( string.rep('--',20) )
394 -- Experience should be:
395 local experience = blocks[8]:sub(1,6)
396 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
398 local money = blocks[8]:sub(7,10)
399 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
404 local seqnum = blocks[8]:sub(18,19)
405 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
407 local fairy = blocks[9]:sub(1,8)
408 --FD0F = Left, FF0F = Right
409 local path = 'not choosen'
410 if fairy:sub(2,2) == 'D' then
412 elseif fairy:sub(2,2) == 'F' then
415 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
417 local hat = blocks[9]:sub(8,11)
418 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
420 local level = blocks[13]:sub(27,28)
421 print(('LEVEL : %d'):format( tonumber(level,16)))
423 --local health = blocks[]:sub();
424 --print(('Health : %d'):format( tonumber(health,16))
426 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
427 local heropoints = blocks[13]:sub(20,23)
428 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
430 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
431 local challenges = blocks[16]:sub(25,32)
432 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
435 local name1 = blocks[10]:sub(1,32)
436 local name2 = blocks[12]:sub(1,32)
437 print('Custom name : '..utils.ConvertHexToAscii(name1..name2))
440 print('Lets try to max out some values')
441 -- max out money, experience
443 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
444 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
447 -- max out hero challenges
449 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
450 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
453 -- max out heropoints
455 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
456 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
460 print('Updating all checksums')
461 SetCheckSum(blocks, 3)
462 SetCheckSum(blocks, 2)
463 SetCheckSum(blocks, 1)
464 SetCheckSum(blocks, 0)
466 print('Validating all checksums')
467 ValidateCheckSums(blocks)
470 --Load dumpdata to emulator memory
472 print('Sending dumpdata to emulator memory')
473 err = LoadEmulator(blocks)
474 if err then return oops(err) end
475 core.clearCommandBuffer()
476 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')