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 local ansicolors
= require('ansicolors')
15 This script will try to load a binary datadump of a Mifare TNP3xxx card.
16 It will try to validate all checksums and view some information stored in the dump
17 For an experimental mode, it tries to manipulate some data.
18 At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim"
21 1. script run hf_mf_tnp3_sim
22 2. script run hf_mf_tnp3_sim -m
23 3. script run hf_mf_tnp3_sim -m -i myfile
26 script run hf_mf_tnp3_sim [-h] [-m] [-i <filename>]
30 -m : Maxed out items (experimental)
31 -i : filename for the datadump to read (bin)
34 local DEBUG
= true -- the debug flag
35 local RANDOM
= '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
37 local band
= bit32
.band
39 local lshift
= bit32
.lshift
40 local rshift
= bit32
.rshift
41 local byte
= string.byte
42 local char
= string.char
43 local sub
= string.sub
44 local format = string.format
47 -- A debug printout-function
48 local function dbg(args
)
49 if not DEBUG
then return end
50 if type(args
) == 'table' then
61 -- This is only meant to be used when errors occur
62 local function oops(err
)
64 core
.clearCommandBuffer()
74 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
76 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
78 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
83 local function ExitMsg(msg
)
84 print( string.rep('--',20) )
85 print( string.rep('--',20) )
91 -- there are two dataareas, in block 8 or block 36, ( 1==8 ,
92 -- checksum type = 0, 1, 2, 3
93 local function GetCheckSum(blocks
, dataarea
, chksumtype
)
101 if chksumtype
== 0 then
102 crc
= blocks
[1]:sub(29,32)
103 elseif chksumtype
== 1 then
104 crc
= blocks
[area
]:sub(29,32)
105 elseif chksumtype
== 2 then
106 crc
= blocks
[area
]:sub(25,28)
107 elseif chksumtype
== 3 then
108 crc
= blocks
[area
]:sub(21,24)
110 return utils
.SwapEndianness(crc
,16)
113 local function SetAllCheckSum(blocks
)
114 print('Updating all checksums')
115 SetCheckSum(blocks
, 3)
116 SetCheckSum(blocks
, 2)
117 SetCheckSum(blocks
, 1)
118 SetCheckSum(blocks
, 0)
121 local function SetCheckSum(blocks
, chksumtype
)
123 if blocks
== nil then return nil, 'Argument \"blocks\" nil' end
128 if chksumtype
== 0 then
129 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,1,0))
130 blocks
[1] = blocks
[1]:sub(1,28)..newcrc
:sub(3,4)..newcrc
:sub(1,2)
131 elseif chksumtype
== 1 then
132 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,1,1))
133 blocks
[area1
] = blocks
[area1
]:sub(1,28)..newcrc
:sub(3,4)..newcrc
:sub(1,2)
134 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,2,1))
135 blocks
[area2
] = blocks
[area2
]:sub(1,28)..newcrc
:sub(3,4)..newcrc
:sub(1,2)
136 elseif chksumtype
== 2 then
137 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,1,2))
138 blocks
[area1
] = blocks
[area1
]:sub(1,24)..newcrc
:sub(3,4)..newcrc
:sub(1,2)..blocks
[area1
]:sub(29,32)
139 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,2,2))
140 blocks
[area2
] = blocks
[area2
]:sub(1,24)..newcrc
:sub(3,4)..newcrc
:sub(1,2)..blocks
[area2
]:sub(29,32)
141 elseif chksumtype
== 3 then
142 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,1,3))
143 blocks
[area1
] = blocks
[area1
]:sub(1,20)..newcrc
:sub(3,4)..newcrc
:sub(1,2)..blocks
[area1
]:sub(25,32)
144 newcrc
= ('%04X'):format(CalcCheckSum(blocks
,2,3))
145 blocks
[area2
] = blocks
[area2
]:sub(1,20)..newcrc
:sub(3,4)..newcrc
:sub(1,2)..blocks
[area2
]:sub(25,32)
149 function CalcCheckSum(blocks
, dataarea
, chksumtype
)
151 if dataarea
== 1 then
155 if chksumtype
== 0 then
156 data
= blocks
[0]..blocks
[1]:sub(1,28)
157 elseif chksumtype
== 1 then
158 data
= blocks
[area
]:sub(1,28)..'0500'
159 elseif chksumtype
== 2 then
160 data
= blocks
[area
+1]..blocks
[area
+2]..blocks
[area
+4]
161 elseif chksumtype
== 3 then
162 data
= blocks
[area
+5]..blocks
[area
+6]..blocks
[area
+8]..string.rep('00',0xe0)
164 return utils
.Crc16(data
)
167 local function ValidateCheckSums(blocks
)
168 print(' Validating checksums')
170 local isOk
, crc
, calc
172 crc
= GetCheckSum(blocks
,1,0)
173 calc
= CalcCheckSum(blocks
, 1, 0)
174 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
175 io
.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
177 -- Checksum Type 1 (DATAAREAHEADER 1)
178 crc
= GetCheckSum(blocks
,1,1)
179 calc
= CalcCheckSum(blocks
,1,1)
180 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
181 io
.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
183 -- Checksum Type 1 (DATAAREAHEADER 2)
184 crc
= GetCheckSum(blocks
,2,1)
185 calc
= CalcCheckSum(blocks
,2,1)
186 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
187 io
.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
189 -- Checksum Type 2 (DATAAREA 1)
190 crc
= GetCheckSum(blocks
,1,2)
191 calc
= CalcCheckSum(blocks
,1,2)
192 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
193 io
.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
195 -- Checksum Type 2 (DATAAREA 2)
196 crc
= GetCheckSum(blocks
,2,2)
197 calc
= CalcCheckSum(blocks
,2,2)
198 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
199 io
.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
201 -- Checksum Type 3 (DATAAREA 1)
202 crc
= GetCheckSum(blocks
,1,3)
203 calc
= CalcCheckSum(blocks
,1,3)
204 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
205 io
.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
207 -- Checksum Type 3 (DATAAREA 2)
208 crc
= GetCheckSum(blocks
,2,3)
209 calc
= CalcCheckSum(blocks
,2,3)
210 if crc
== calc
then isOk
='Ok' else isOk
= 'Error' end
211 io
.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc
,calc
,isOk
))
215 local function AddKey(keys
, blockNo
, data
)
216 local pos
= (math
.floor( blockNo
/ 4 ) * 12)+1
217 local key
= keys
:sub(pos
, pos
+ 11 )
218 return key
..data
:sub(13)
221 local function LoadEmulator(uid
, blocks
)
222 print('Sending dumpdata to emulator memory')
223 local keys
= pre
.GetAll(uid
)
225 for _
,b
in pairs(blocks
) do
230 if (_
>= 8 and _
<=21) or (_
>= 36 and _
<=49) then
231 local base
= ('%s%s%02x%s'):format(blocks
[0], blocks
[1], _
, RANDOM
)
232 local baseStr
= utils
.ConvertHexToAscii(base
)
233 local key
= md5
.sumhexa(baseStr
)
234 local enc
= core
.aes128_encrypt(key
, blockdata
)
235 blockdata
= utils
.ConvertAsciiToHex(enc
)
238 -- add keys if not existing..
239 if ( blockdata
:sub(1,12) == '000000000000' ) then
240 blockdata
= AddKey(keys
, _
, blockdata
)
246 core
.clearCommandBuffer()
247 cmd
= Command
:newMIX
{cmd
= cmds
.CMD_HF_MIFARE_EML_MEMSET
, arg1
= _
,arg2
= 1,arg3
= 16, data
= blockdata
}
248 local err
, msg
= cmd
:sendMIX(true)
249 if err
== nil then return err
, msg
end
254 local function Num2Card(m
, l
)
257 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B,
258 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54,0x56, 0x57, 0x58, 0x59, 0x5A, 0x00
260 local msw
= tonumber(utils
.SwapEndiannessStr(m
,32),16)
261 local lsw
= tonumber(utils
.SwapEndiannessStr(l
,32),16)
263 if msw
> 0x17ea1 then
267 if msw
== 0x17ea1 and lsw
> 0x8931fee8 then
268 return "out of range"
274 index
, msw
, lsw
= DivideByK( msw
, lsw
)
275 if ( index
<= 1 ) then
276 s
= char(k
[index
]) .. s
278 s
= char(k
[index
-1]) .. s
280 print (index
-1, msw
, lsw
)
296 function DivideByK(msw
, lsw
)
303 --local num = 0 | band( rshift(msw,16), 0xffff)
304 local num
= band( rshift(msw
, 16), 0xffff)
306 --highLSW = 0 | lshift( (num / RADIX) , 16)
307 highLSW
= lshift( (num
/ RADIX
) , 16)
308 remainder
= num
% RADIX
310 num
= bor( lshift(remainder
,16), band(msw
, 0xffff))
312 --highLSW |= num / RADIX
313 highLSW
= highLSW
or (num
/ RADIX
)
314 remainder
= num
% RADIX
316 num
= bor( lshift(remainder
,16), ( band(rshift(lsw
,16), 0xffff)))
318 --lowLSW = 0 | (num / RADIX) << 16
319 lowLSW
= 0 or (lshift( (num
/ RADIX
), 16))
320 remainder
= num
% RADIX
322 num
= bor( lshift(remainder
,16) , band(lsw
, 0xffff) )
324 lowLSW
= bor(lowLSW
, (num
/ RADIX
))
325 remainder
= num
% RADIX
326 return remainder
, highLSW
, lowLSW
328 -- uint num = 0 | (msw >> 16) & 0xffff;
330 -- highLSW = 0 | (num / RADIX) << 16;
331 -- remainder = num % RADIX;
333 -- num = (remainder << 16) | (msw & 0xffff);
335 -- highLSW |= num / RADIX;
336 -- remainder = num % RADIX;
338 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
340 -- lowLSW = 0 | (num / RADIX) << 16;
341 -- remainder = num % RADIX;
343 -- num = (remainder << 16) | (lsw & 0xffff);
345 -- lowLSW |= num / RADIX;
346 -- remainder = num % RADIX;
350 local function main(args
)
352 print( string.rep('--',20) )
353 print( string.rep('--',20) )
355 local result
, err
, hex
357 local inputTemplate
= 'dumpdata.bin'
358 local outputTemplate
= os
.date('toydump_%Y-%m-%d_%H%M');
360 -- Arguments for the script
361 for o
, a
in getopt
.getopt(args
, 'hmi:o:') do
362 if o
== 'h' then return help() end
363 if o
== 'm' then maxed
= true end
364 if o
== 'o' then outputTemplate
= a
end
365 if o
== 'i' then inputTemplate
= a
end
369 local cmdSetDbgOff
= 'hw dbg -0'
370 core
.console( cmdSetDbgOff
)
373 -- Load dump.bin file
374 print( ('Load data from %s'):format(inputTemplate
))
375 hex
, err
= utils
.ReadDumpFile(inputTemplate
)
376 if not hex
then return oops(err
) end
380 for i
= 1, #hex
, 32 do
381 blocks
[blockindex
] = hex
:sub(i
, i
+31)
382 blockindex
= blockindex
+ 1
385 if DEBUG
then ValidateCheckSums(blocks
) end
388 print( string.rep('--',20) )
389 print(' Gathering info')
390 local uid
= blocks
[0]:sub(1,8)
391 local toytype
= blocks
[1]:sub(1,4)
392 local cardidLsw
= blocks
[1]:sub(9,16)
393 local cardidMsw
= blocks
[1]:sub(17,24)
394 local subtype
= blocks
[1]:sub(25,28)
397 print( string.rep('--',20) )
399 local item
= toys
.Find( toytype
, subtype
)
401 local itemStr
= ('%s - %s (%s)'):format(item
[6],item
[5], item
[4])
402 print(' ITEM TYPE : '..itemStr
)
404 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype
, subtype
) )
407 print( (' UID : %s'):format(uid
) )
408 print( (' CARDID : %s %s [%s]'):format(
410 --Num2Card(cardidMsw, cardidLsw))
413 print( string.rep('--',20) )
416 -- Experience should be:
417 local experience
= blocks
[8]:sub(1,6)
418 print(('Experience : %d'):format(utils
.SwapEndianness(experience
,16)))
420 local money
= blocks
[8]:sub(7,10)
421 print(('Money : %d'):format(utils
.SwapEndianness(money
,16)))
426 local seqnum
= blocks
[8]:sub(18,19)
427 print(('Sequence number : %d'):format( tonumber(seqnum
,16)))
429 local fairy
= blocks
[9]:sub(1,8)
430 --FD0F = Left, FF0F = Right
431 local path
= 'not chosen'
432 if fairy
:sub(2,2) == 'D' then
434 elseif fairy
:sub(2,2) == 'F' then
437 print(('Fairy : %d [Path: %s] '):format(utils
.SwapEndianness(fairy
,24),path
))
439 local hat
= blocks
[9]:sub(8,11)
440 print(('Hat : %d'):format(utils
.SwapEndianness(hat
,16)))
442 local level
= blocks
[13]:sub(27,28)
443 print(('LEVEL : %d'):format( tonumber(level
,16)))
445 --local health = blocks[]:sub();
446 --print(('Health : %d'):format( tonumber(health,16))
448 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
449 local heropoints
= blocks
[13]:sub(20,23)
450 print(('Hero points : %d'):format(utils
.SwapEndianness(heropoints
,16)))
452 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
453 local challenges
= blocks
[16]:sub(25,32)
454 print(('Finished hero challenges : %d'):format(utils
.SwapEndianness(challenges
,32)))
457 local name1
= blocks
[10]:sub(1,32)
458 local name2
= blocks
[12]:sub(1,32)
459 print('Custom name : '..utils
.ConvertHexToAscii(name1
..name2
))
462 print('Lets try to max out some values')
463 -- max out money, experience
465 blocks
[8] = 'FFFFFF'..'FFFF'..blocks
[8]:sub(11,32)
466 blocks
[36] = 'FFFFFF'..'FFFF'..blocks
[36]:sub(11,32)
469 -- max out hero challenges
471 blocks
[16] = blocks
[16]:sub(1,24)..'FFFFFFFF'
472 blocks
[44] = blocks
[44]:sub(1,24)..'FFFFFFFF'
475 -- max out heropoints
477 blocks
[13] = blocks
[13]:sub(1,19)..'0064'..blocks
[13]:sub(24,32)
478 blocks
[41] = blocks
[41]:sub(1,19)..'0064'..blocks
[41]:sub(24,32)
482 SetAllCheckSum(blocks
)
484 -- Validate Checksums
485 ValidateCheckSums(blocks
)
488 --Load dumpdata to emulator memory
490 err
= LoadEmulator(uid
, blocks
)
491 if err
then return oops(err
) end
492 core
.clearCommandBuffer()
493 print('The simulation is now prepared.\n --> run \"hf mf sim -u '..uid
..'\" <--')