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 toyNames = 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)
28 local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
29 local DEBUG = true -- the debug flag
31 -- A debug printout-function
37 if type(args) == "table" then
48 -- This is only meant to be used when errors occur
56 print("Example usage")
62 print( string.rep('--',20) )
63 print( string.rep('--',20) )
69 local function writedumpfile(infile)
70 t = infile:read("*all")
72 local len,hex = bin.unpack(("H%d"):format(len),t)
76 -- there are two dataareas, in block 8 or block 36, ( 1==8 ,
77 -- checksum type = 0, 1, 2, 3
78 local function GetCheckSum(blocks, dataarea, chksumtype)
86 if chksumtype == 0 then
87 crc = blocks[1]:sub(29,32)
88 elseif chksumtype == 1 then
89 crc = blocks[area]:sub(29,32)
90 elseif chksumtype == 2 then
91 crc = blocks[area]:sub(25,28)
92 elseif chksumtype == 3 then
93 crc = blocks[area]:sub(21,24)
95 return utils.SwapEndianness(crc,16)
98 local function SetCheckSum(blocks, chksumtype)
100 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
105 if chksumtype == 0 then
106 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0))
107 blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
108 elseif chksumtype == 1 then
109 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1))
110 blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
111 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1))
112 blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
113 elseif chksumtype == 2 then
114 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2))
115 blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32)
116 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2))
117 blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32)
118 elseif chksumtype == 3 then
119 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3))
120 blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32)
121 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3))
122 blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32)
126 function CalcCheckSum(blocks, dataarea, chksumtype)
128 if dataarea == 1 then
132 if chksumtype == 0 then
133 data = blocks[0]..blocks[1]:sub(1,28)
134 elseif chksumtype == 1 then
135 data = blocks[area]:sub(1,28)..'0500'
136 elseif chksumtype == 2 then
137 data = blocks[area+1]..blocks[area+2]..blocks[area+4]
138 elseif chksumtype == 3 then
139 data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0)
141 return utils.Crc16(data)
144 local function ValidateCheckSums(blocks)
146 local isOk, crc, calc
148 crc = GetCheckSum(blocks,1,0)
149 calc = CalcCheckSum(blocks, 1, 0)
150 if crc == calc then isOk='Ok' else isOk = 'Error' end
151 io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk))
153 -- Checksum Type 1 (DATAAREAHEADER 1)
154 crc = GetCheckSum(blocks,1,1)
155 calc = CalcCheckSum(blocks,1,1)
156 if crc == calc then isOk='Ok' else isOk = 'Error' end
157 io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
159 -- Checksum Type 1 (DATAAREAHEADER 2)
160 crc = GetCheckSum(blocks,2,1)
161 calc = CalcCheckSum(blocks,2,1)
162 if crc == calc then isOk='Ok' else isOk = 'Error' end
163 io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
165 -- Checksum Type 2 (DATAAREA 1)
166 crc = GetCheckSum(blocks,1,2)
167 calc = CalcCheckSum(blocks,1,2)
168 if crc == calc then isOk='Ok' else isOk = 'Error' end
169 io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
171 -- Checksum Type 2 (DATAAREA 2)
172 crc = GetCheckSum(blocks,2,2)
173 calc = CalcCheckSum(blocks,2,2)
174 if crc == calc then isOk='Ok' else isOk = 'Error' end
175 io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
177 -- Checksum Type 3 (DATAAREA 1)
178 crc = GetCheckSum(blocks,1,3)
179 calc = CalcCheckSum(blocks,1,3)
180 if crc == calc then isOk='Ok' else isOk = 'Error' end
181 io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
183 -- Checksum Type 3 (DATAAREA 2)
184 crc = GetCheckSum(blocks,2,3)
185 calc = CalcCheckSum(blocks,2,3)
186 if crc == calc then isOk='Ok' else isOk = 'Error' end
187 io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
191 local function LoadEmulator(blocks)
192 local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
195 for _,b in pairs(blocks) do
200 if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
201 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , HASHCONSTANT)
202 local baseStr = utils.ConvertHexToAscii(base)
203 local key = md5.sumhexa(baseStr)
204 local enc = core.aes(key, blockdata)
205 local hex = utils.ConvertAsciiToBytes(enc)
206 hex = utils.ConvertBytesToHex(hex)
213 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
214 local err = core.SendCommand(cmd:getBytes())
222 local function main(args)
224 print( string.rep('--',20) )
225 print( string.rep('--',20) )
227 local result, err, hex
229 local inputTemplate = "dumpdata.bin"
230 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
232 -- Arguments for the script
233 for o, a in getopt.getopt(args, 'hmi:o:') do
234 if o == "h" then return help() end
235 if o == "m" then maxed = true end
236 if o == "o" then outputTemplate = a end
237 if o == "i" then inputTemplate = a end
241 local cmdSetDbgOff = "hf mf dbg 0"
242 core.console( cmdSetDbgOff)
244 -- if not loadFromDump then
245 -- -- Look for tag present on reader,
246 -- result, err = lib14a.read1443a(false)
247 -- if not result then return oops(err) end
249 -- core.clearCommandBuffer()
251 -- if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx
252 -- return oops('This is not a TNP3xxx tag. aborting.')
256 -- print((' Found tag : %s'):format(result.name))
259 -- Load dump.bin file
260 print( (' Load data from %s'):format(inputTemplate))
261 hex, err = utils.ReadDumpFile(inputTemplate)
262 if not hex then return oops(err) end
266 for i = 1, #hex, 32 do
267 blocks[blockindex] = hex:sub(i,i+31)
268 blockindex = blockindex + 1
272 print('Validating checksums in the loaded datadump')
273 ValidateCheckSums(blocks)
277 print( string.rep('--',20) )
278 print(' Gathering info')
279 local uid = blocks[0]:sub(1,8)
280 local itemtype = blocks[1]:sub(1,4)
281 local cardid = blocks[1]:sub(9,24)
284 print( string.rep('--',20) )
285 print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) )
286 print( (' UID : 0x%s'):format(uid) )
287 print( (' CARDID : 0x%s'):format(cardid ) )
288 print( string.rep('--',20) )
290 -- lets do something.
292 local experience = blocks[8]:sub(1,6)
293 print(('Experience : %d'):format(utils.SwapEndianness(experience,24)))
294 local money = blocks[8]:sub(7,10)
295 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
296 local fairy = blocks[9]:sub(1,8)
297 --FD0F = Left, FF0F = Right
298 local path = 'not choosen'
299 if fairy:sub(2,2) == 'D' then
301 elseif fairy:sub(2,2) == 'F' then
304 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
306 local hat = blocks[9]:sub(8,11)
307 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
309 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
310 local heropoints = blocks[13]:sub(20,23)
311 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
313 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
314 local challenges = blocks[16]:sub(25,32)
315 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
318 print('Lets try to max out some values')
319 -- max out money, experience
321 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
322 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
325 -- max out hero challenges
327 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
328 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
331 -- max out heropoints
333 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
334 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
338 print('Updating all checksums')
339 SetCheckSum(blocks, 3)
340 SetCheckSum(blocks, 2)
341 SetCheckSum(blocks, 1)
342 SetCheckSum(blocks, 0)
344 print('Validating all checksums')
345 ValidateCheckSums(blocks)
348 --Load dumpdata to emulator memory
350 print('Sending dumpdata to emulator memory')
351 err = LoadEmulator(blocks)
352 if err then return oops(err) end
353 core.clearCommandBuffer()
354 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..' x\" <--')