]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/tnp3sim.lua
b6821606ca35ce5352c0d11826b42c0b902478ab
[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 = 0, data = blockdata}
224 local err = core.SendCommand(cmd:getBytes())
225 if err then
226 return err
227 end
228 end
229 io.write('\n')
230 end
231
232 local function Num2Card(m, l)
233
234 local k = {
235 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B,
236 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54,0x56, 0x57, 0x58, 0x59, 0x5A, 0x00
237 }
238 local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
239 local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
240
241 if msw > 0x17ea1 then
242 return "too big"
243 end
244
245 if msw == 0x17ea1 and lsw > 0x8931fee8 then
246 return "out of range"
247 end
248
249 local s = ""
250 local index
251 for i = 1,10 do
252 index, msw, lsw = DivideByK( msw, lsw)
253 if ( index <= 1 ) then
254 s = char(k[index]) .. s
255 else
256 s = char(k[index-1]) .. s
257 end
258 print (index-1, msw, lsw)
259 end
260 return s
261 end
262 --33LRT-LM9Q9
263 --7, 122, 3474858630
264 --20, 4, 1008436634
265 --7, 0, 627182959
266 --17, 0, 21626998
267 --16, 0, 745758
268 --23, 0, 25715
269 --21, 0, 886
270 --16, 0, 30
271 --1, 0, 1
272 --1, 0, 0
273
274 function DivideByK(msw, lsw)
275
276 local lowLSW
277 local highLSW
278 local remainder = 0
279 local RADIX = 29
280
281 --local num = 0 | band( rshift(msw,16), 0xffff)
282 local num = band( rshift(msw, 16), 0xffff)
283
284 --highLSW = 0 | lshift( (num / RADIX) , 16)
285 highLSW = lshift( (num / RADIX) , 16)
286 remainder = num % RADIX
287
288 num = bor( lshift(remainder,16), band(msw, 0xffff))
289
290 --highLSW |= num / RADIX
291 highLSW = highLSW or (num / RADIX)
292 remainder = num % RADIX
293
294 num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
295
296 --lowLSW = 0 | (num / RADIX) << 16
297 lowLSW = 0 or (lshift( (num / RADIX), 16))
298 remainder = num % RADIX
299
300 num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
301
302 lowLSW = bor(lowLSW, (num / RADIX))
303 remainder = num % RADIX
304 return remainder, highLSW, lowLSW
305
306 -- uint num = 0 | (msw >> 16) & 0xffff;
307
308 -- highLSW = 0 | (num / RADIX) << 16;
309 -- remainder = num % RADIX;
310
311 -- num = (remainder << 16) | (msw & 0xffff);
312
313 -- highLSW |= num / RADIX;
314 -- remainder = num % RADIX;
315
316 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
317
318 -- lowLSW = 0 | (num / RADIX) << 16;
319 -- remainder = num % RADIX;
320
321 -- num = (remainder << 16) | (lsw & 0xffff);
322
323 -- lowLSW |= num / RADIX;
324 -- remainder = num % RADIX;
325
326 end
327
328 local function main(args)
329
330 print( string.rep('--',20) )
331 print( string.rep('--',20) )
332
333 local result, err, hex
334 local maxed = false
335 local inputTemplate = "dumpdata.bin"
336 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
337
338 -- Arguments for the script
339 for o, a in getopt.getopt(args, 'hmi:o:') do
340 if o == "h" then return help() end
341 if o == "m" then maxed = true end
342 if o == "o" then outputTemplate = a end
343 if o == "i" then inputTemplate = a end
344 end
345
346 -- Turn off Debug
347 local cmdSetDbgOff = "hf mf dbg 0"
348 core.console( cmdSetDbgOff)
349
350 -- Load dump.bin file
351 print( (' Load data from %s'):format(inputTemplate))
352 hex, err = utils.ReadDumpFile(inputTemplate)
353 if not hex then return oops(err) end
354
355 local blocks = {}
356 local blockindex = 0
357 for i = 1, #hex, 32 do
358 blocks[blockindex] = hex:sub(i,i+31)
359 blockindex = blockindex + 1
360 end
361
362 if DEBUG then
363 print(' Validating checksums')
364 ValidateCheckSums(blocks)
365 end
366
367 --
368 print( string.rep('--',20) )
369 print(' Gathering info')
370 local uid = blocks[0]:sub(1,8)
371 local toytype = blocks[1]:sub(1,4)
372 local cardidLsw = blocks[1]:sub(9,16)
373 local cardidMsw = blocks[1]:sub(17,24)
374 local subtype = blocks[1]:sub(25,28)
375
376 -- Show info
377 print( string.rep('--',20) )
378
379 local item = toys.Find( toytype, subtype)
380 if item then
381 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
382 print(' ITEM TYPE : '..itemStr )
383 else
384 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
385 end
386
387 print( (' UID : 0x%s'):format(uid) )
388 print( (' CARDID : 0x%s %s [%s]'):format(
389 cardidMsw,cardidLsw,
390 --Num2Card(cardidMsw, cardidLsw))
391 '')
392 )
393 print( string.rep('--',20) )
394
395
396 -- Experience should be:
397 local experience = blocks[8]:sub(1,6)
398 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
399
400 local money = blocks[8]:sub(7,10)
401 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
402
403 --
404
405 -- Sequence number
406 local seqnum = blocks[8]:sub(18,19)
407 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
408
409 local fairy = blocks[9]:sub(1,8)
410 --FD0F = Left, FF0F = Right
411 local path = 'not choosen'
412 if fairy:sub(2,2) == 'D' then
413 path = 'Left'
414 elseif fairy:sub(2,2) == 'F' then
415 path = 'Right'
416 end
417 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
418
419 local hat = blocks[9]:sub(8,11)
420 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
421
422 local level = blocks[13]:sub(27,28)
423 print(('LEVEL : %d'):format( tonumber(level,16)))
424 --hälsa: 667 029b
425 --local health = blocks[]:sub();
426 --print(('Health : %d'):format( tonumber(health,16))
427
428 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
429 local heropoints = blocks[13]:sub(20,23)
430 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
431
432 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
433 local challenges = blocks[16]:sub(25,32)
434 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
435
436 -- Character Name
437 local name1 = blocks[10]:sub(1,32)
438 local name2 = blocks[12]:sub(1,32)
439 print('Custom name : '..utils.ConvertHexToAscii(name1..name2))
440
441 if maxed then
442 print('Lets try to max out some values')
443 -- max out money, experience
444 --print (blocks[8])
445 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
446 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
447 --print (blocks[8])
448
449 -- max out hero challenges
450 --print (blocks[16])
451 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
452 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
453 --print (blocks[16])
454
455 -- max out heropoints
456 --print (blocks[13])
457 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
458 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
459 --print (blocks[13])
460
461 -- Update Checksums
462 print('Updating all checksums')
463 SetCheckSum(blocks, 3)
464 SetCheckSum(blocks, 2)
465 SetCheckSum(blocks, 1)
466 SetCheckSum(blocks, 0)
467
468 print('Validating all checksums')
469 ValidateCheckSums(blocks)
470 end
471
472 --Load dumpdata to emulator memory
473 if DEBUG then
474 print('Sending dumpdata to emulator memory')
475 err = LoadEmulator(blocks)
476 if err then return oops(err) end
477 core.clearCommandBuffer()
478 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')
479 end
480 end
481 main(args)
Impressum, Datenschutz