]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/tnp3sim.lua
11b268dd04ba94497e850c1aa9321575b49f37d6
[proxmark3-svn] / client / scripts / tnp3sim.lua
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
9 example =[[
10 1. script run tnp3sim
11 2. script run tnp3sim -m
12 3. script run tnp3sim -m -i myfile
13 ]]
14 author = "Iceman"
15 usage = "script run tnp3sim -h -m -i <filename>"
16 desc =[[
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"
21
22 Arguments:
23 -h : this help
24 -m : Maxed out items (experimental)
25 -i : filename for the datadump to read (bin)
26
27 ]]
28
29 local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
30 local DEBUG = false -- the debug flag
31 local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
32
33 local band = bit32.band
34 local bor = bit32.bor
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
41
42 ---
43 -- A debug printout-function
44 function dbg(args)
45 if not DEBUG then
46 return
47 end
48
49 if type(args) == "table" then
50 local i = 1
51 while result[i] do
52 dbg(result[i])
53 i = i+1
54 end
55 else
56 print("###", args)
57 end
58 end
59 ---
60 -- This is only meant to be used when errors occur
61 function oops(err)
62 print("ERROR: ",err)
63 return nil,err
64 end
65 ---
66 -- Usage help
67 function help()
68 print(desc)
69 print("Example usage")
70 print(example)
71 end
72 --
73 -- Exit message
74 function ExitMsg(msg)
75 print( string.rep('--',20) )
76 print( string.rep('--',20) )
77 print(msg)
78 print()
79 end
80
81 local function writedumpfile(infile)
82 t = infile:read("*all")
83 len = string.len(t)
84 local len,hex = bin.unpack(("H%d"):format(len),t)
85 return hex
86 end
87 -- blocks with data
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)
91
92 local crc
93 local area = 36
94 if dataarea == 1 then
95 area = 8
96 end
97
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)
106 end
107 return utils.SwapEndianness(crc,16)
108 end
109
110 local function SetCheckSum(blocks, chksumtype)
111
112 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
113 local newcrc
114 local area1 = 8
115 local area2 = 36
116
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)
135 end
136 end
137
138 function CalcCheckSum(blocks, dataarea, chksumtype)
139 local area = 36
140 if dataarea == 1 then
141 area = 8
142 end
143
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)
152 end
153 return utils.Crc16(data)
154 end
155
156 local function ValidateCheckSums(blocks)
157
158 local isOk, crc, calc
159 -- Checksum Type 0
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))
164
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))
170
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))
176
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))
182
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))
188
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))
194
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))
200
201 end
202 local function LoadEmulator(blocks)
203 local cmd
204 local blockdata
205 for _,b in pairs(blocks) do
206
207 blockdata = b
208
209 if _%4 ~= 3 then
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)
217
218 blockdata = hex
219 io.write( _..',')
220 end
221 end
222
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
226 end
227 io.write('\n')
228 end
229
230 local function Num2Card(m, l)
231
232 local k = {
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
235 }
236 local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
237 local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
238
239 if msw > 0x17ea1 then
240 return "too big"
241 end
242
243 if msw == 0x17ea1 and lsw > 0x8931fee8 then
244 return "out of range"
245 end
246
247 local s = ""
248 local index
249 for i = 1,10 do
250 index, msw, lsw = DivideByK( msw, lsw)
251 if ( index <= 1 ) then
252 s = char(k[index]) .. s
253 else
254 s = char(k[index-1]) .. s
255 end
256 print (index-1, msw, lsw)
257 end
258 return s
259 end
260 --33LRT-LM9Q9
261 --7, 122, 3474858630
262 --20, 4, 1008436634
263 --7, 0, 627182959
264 --17, 0, 21626998
265 --16, 0, 745758
266 --23, 0, 25715
267 --21, 0, 886
268 --16, 0, 30
269 --1, 0, 1
270 --1, 0, 0
271
272 function DivideByK(msw, lsw)
273
274 local lowLSW
275 local highLSW
276 local remainder = 0
277 local RADIX = 29
278
279 --local num = 0 | band( rshift(msw,16), 0xffff)
280 local num = band( rshift(msw, 16), 0xffff)
281
282 --highLSW = 0 | lshift( (num / RADIX) , 16)
283 highLSW = lshift( (num / RADIX) , 16)
284 remainder = num % RADIX
285
286 num = bor( lshift(remainder,16), band(msw, 0xffff))
287
288 --highLSW |= num / RADIX
289 highLSW = highLSW or (num / RADIX)
290 remainder = num % RADIX
291
292 num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
293
294 --lowLSW = 0 | (num / RADIX) << 16
295 lowLSW = 0 or (lshift( (num / RADIX), 16))
296 remainder = num % RADIX
297
298 num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
299
300 lowLSW = bor(lowLSW, (num / RADIX))
301 remainder = num % RADIX
302 return remainder, highLSW, lowLSW
303
304 -- uint num = 0 | (msw >> 16) & 0xffff;
305
306 -- highLSW = 0 | (num / RADIX) << 16;
307 -- remainder = num % RADIX;
308
309 -- num = (remainder << 16) | (msw & 0xffff);
310
311 -- highLSW |= num / RADIX;
312 -- remainder = num % RADIX;
313
314 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
315
316 -- lowLSW = 0 | (num / RADIX) << 16;
317 -- remainder = num % RADIX;
318
319 -- num = (remainder << 16) | (lsw & 0xffff);
320
321 -- lowLSW |= num / RADIX;
322 -- remainder = num % RADIX;
323
324 end
325
326 local function main(args)
327
328 print( string.rep('--',20) )
329 print( string.rep('--',20) )
330
331 local result, err, hex
332 local maxed = false
333 local inputTemplate = "dumpdata.bin"
334 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
335
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
342 end
343
344 -- Turn off Debug
345 local cmdSetDbgOff = "hf mf dbg 0"
346 core.console( cmdSetDbgOff)
347
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
352
353 local blocks = {}
354 local blockindex = 0
355 for i = 1, #hex, 32 do
356 blocks[blockindex] = hex:sub(i,i+31)
357 blockindex = blockindex + 1
358 end
359
360 if DEBUG then
361 print(' Validating checksums')
362 ValidateCheckSums(blocks)
363 end
364
365 --
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)
373
374 -- Show info
375 print( string.rep('--',20) )
376
377 local item = toys.Find( toytype, subtype)
378 if item then
379 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
380 print(' ITEM TYPE : '..itemStr )
381 else
382 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
383 end
384
385 print( (' UID : 0x%s'):format(uid) )
386 print( (' CARDID : 0x%s %s [%s]'):format(
387 cardidMsw,cardidLsw,
388 --Num2Card(cardidMsw, cardidLsw))
389 '')
390 )
391 print( string.rep('--',20) )
392
393
394 -- Experience should be:
395 local experience = blocks[8]:sub(1,6)
396 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
397
398 local money = blocks[8]:sub(7,10)
399 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
400
401 --
402
403 -- Sequence number
404 local seqnum = blocks[8]:sub(18,19)
405 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
406
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
411 path = 'Left'
412 elseif fairy:sub(2,2) == 'F' then
413 path = 'Right'
414 end
415 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
416
417 local hat = blocks[9]:sub(8,11)
418 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
419
420 local level = blocks[13]:sub(27,28)
421 print(('LEVEL : %d'):format( tonumber(level,16)))
422 --hälsa: 667 029b
423 --local health = blocks[]:sub();
424 --print(('Health : %d'):format( tonumber(health,16))
425
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)))
429
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)))
433
434 -- Character Name
435 local name1 = blocks[10]:sub(1,32)
436 local name2 = blocks[12]:sub(1,32)
437 print('Custom name : '..utils.ConvertHexToAscii(name1..name2))
438
439 if maxed then
440 print('Lets try to max out some values')
441 -- max out money, experience
442 --print (blocks[8])
443 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
444 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
445 --print (blocks[8])
446
447 -- max out hero challenges
448 --print (blocks[16])
449 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
450 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
451 --print (blocks[16])
452
453 -- max out heropoints
454 --print (blocks[13])
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)
457 --print (blocks[13])
458
459 -- Update Checksums
460 print('Updating all checksums')
461 SetCheckSum(blocks, 3)
462 SetCheckSum(blocks, 2)
463 SetCheckSum(blocks, 1)
464 SetCheckSum(blocks, 0)
465
466 print('Validating all checksums')
467 ValidateCheckSums(blocks)
468 end
469
470 --Load dumpdata to emulator memory
471 if DEBUG then
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..'\" <--')
477 end
478 end
479 main(args)
Impressum, Datenschutz