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 -- Look for tag present on reader,
245 result, err = lib14a.read1443a(false)
246 if not result then return oops(err) end
248 core.clearCommandBuffer()
250 if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx
251 return oops('This is not a TNP3xxx tag. aborting.')
255 print((' Found tag : %s'):format(result.name))
257 -- Load dump.bin file
258 print( (' Load data from %s'):format(inputTemplate))
259 hex, err = utils.ReadDumpFile(inputTemplate)
260 if not hex then return oops(err) end
264 for i = 1, #hex, 32 do
265 blocks[blockindex] = hex:sub(i,i+31)
266 blockindex = blockindex + 1
270 print('Validating checksums in the loaded datadump')
271 ValidateCheckSums(blocks)
275 print( string.rep('--',20) )
276 print(' Gathering info')
277 local uid = blocks[0]:sub(1,8)
278 local itemtype = blocks[1]:sub(1,4)
279 local cardid = blocks[1]:sub(9,24)
282 print( string.rep('--',20) )
283 print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) )
284 print( (' UID : 0x%s'):format(uid) )
285 print( (' CARDID : 0x%s'):format(cardid ) )
286 print( string.rep('--',20) )
288 -- lets do something.
290 local experience = blocks[8]:sub(1,6)
291 print(('Experience : %d'):format(utils.SwapEndianness(experience,24)))
292 local money = blocks[8]:sub(7,10)
293 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
294 local fairy = blocks[9]:sub(1,8)
295 --FD0F = Left, FF0F = Right
296 local path = 'not choosen'
297 if fairy:sub(2,2) == 'D' then
299 elseif fairy:sub(2,2) == 'F' then
302 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
304 local hat = blocks[9]:sub(8,11)
305 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
307 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
308 local heropoints = blocks[13]:sub(20,23)
309 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
311 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
312 local challenges = blocks[16]:sub(25,32)
313 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
316 print('Lets try to max out some values')
317 -- max out money, experience
319 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
320 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
323 -- max out hero challenges
325 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
326 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
329 -- max out heropoints
331 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
332 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
336 print('Updating all checksums')
337 SetCheckSum(blocks, 3)
338 SetCheckSum(blocks, 2)
339 SetCheckSum(blocks, 1)
340 SetCheckSum(blocks, 0)
342 print('Validating all checksums')
343 ValidateCheckSums(blocks)
346 --Load dumpdata to emulator memory
348 print('Sending dumpdata to emulator memory')
349 err = LoadEmulator(blocks)
350 if err then return oops(err) end
351 core.clearCommandBuffer()
352 print('The simulation is now prepared.\n --> run \"hf mf sim 5 '..uid..'\" <--')