]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/tnp3sim.lua
ADD: next step ISO11784/85
[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
44 local band = bit32.band
45 local bor = bit32.bor
46 local lshift = bit32.lshift
47 local rshift = bit32.rshift
48 local byte = string.byte
49 local char = string.char
50 local sub = string.sub
51 local format = string.format
52
53 ---
54 -- A debug printout-function
55 function dbg(args)
56 if not DEBUG then
57 return
58 end
59
60 if type(args) == "table" then
61 local i = 1
62 while result[i] do
63 dbg(result[i])
64 i = i+1
65 end
66 else
67 print("###", args)
68 end
69 end
70 ---
71 -- This is only meant to be used when errors occur
72 function oops(err)
73 print("ERROR: ",err)
74 end
75 ---
76 -- Usage help
77 function help()
78 print(desc)
79 print("Example usage")
80 print(example)
81 end
82 --
83 -- Exit message
84 function ExitMsg(msg)
85 print( string.rep('--',20) )
86 print( string.rep('--',20) )
87 print(msg)
88 print()
89 end
90
91 local function writedumpfile(infile)
92 t = infile:read("*all")
93 len = string.len(t)
94 local len,hex = bin.unpack(("H%d"):format(len),t)
95 return hex
96 end
97 -- blocks with data
98 -- there are two dataareas, in block 8 or block 36, ( 1==8 ,
99 -- checksum type = 0, 1, 2, 3
100 local function GetCheckSum(blocks, dataarea, chksumtype)
101
102 local crc
103 local area = 36
104 if dataarea == 1 then
105 area = 8
106 end
107
108 if chksumtype == 0 then
109 crc = blocks[1]:sub(29,32)
110 elseif chksumtype == 1 then
111 crc = blocks[area]:sub(29,32)
112 elseif chksumtype == 2 then
113 crc = blocks[area]:sub(25,28)
114 elseif chksumtype == 3 then
115 crc = blocks[area]:sub(21,24)
116 end
117 return utils.SwapEndianness(crc,16)
118 end
119
120 local function SetCheckSum(blocks, chksumtype)
121
122 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
123 local newcrc
124 local area1 = 8
125 local area2 = 36
126
127 if chksumtype == 0 then
128 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0))
129 blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
130 elseif chksumtype == 1 then
131 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1))
132 blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
133 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1))
134 blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
135 elseif chksumtype == 2 then
136 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2))
137 blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32)
138 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2))
139 blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32)
140 elseif chksumtype == 3 then
141 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3))
142 blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32)
143 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3))
144 blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32)
145 end
146 end
147
148 function CalcCheckSum(blocks, dataarea, chksumtype)
149 local area = 36
150 if dataarea == 1 then
151 area = 8
152 end
153
154 if chksumtype == 0 then
155 data = blocks[0]..blocks[1]:sub(1,28)
156 elseif chksumtype == 1 then
157 data = blocks[area]:sub(1,28)..'0500'
158 elseif chksumtype == 2 then
159 data = blocks[area+1]..blocks[area+2]..blocks[area+4]
160 elseif chksumtype == 3 then
161 data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0)
162 end
163 return utils.Crc16(data)
164 end
165
166 local function ValidateCheckSums(blocks)
167
168 local isOk, crc, calc
169 -- Checksum Type 0
170 crc = GetCheckSum(blocks,1,0)
171 calc = CalcCheckSum(blocks, 1, 0)
172 if crc == calc then isOk='Ok' else isOk = 'Error' end
173 io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk))
174
175 -- Checksum Type 1 (DATAAREAHEADER 1)
176 crc = GetCheckSum(blocks,1,1)
177 calc = CalcCheckSum(blocks,1,1)
178 if crc == calc then isOk='Ok' else isOk = 'Error' end
179 io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
180
181 -- Checksum Type 1 (DATAAREAHEADER 2)
182 crc = GetCheckSum(blocks,2,1)
183 calc = CalcCheckSum(blocks,2,1)
184 if crc == calc then isOk='Ok' else isOk = 'Error' end
185 io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
186
187 -- Checksum Type 2 (DATAAREA 1)
188 crc = GetCheckSum(blocks,1,2)
189 calc = CalcCheckSum(blocks,1,2)
190 if crc == calc then isOk='Ok' else isOk = 'Error' end
191 io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
192
193 -- Checksum Type 2 (DATAAREA 2)
194 crc = GetCheckSum(blocks,2,2)
195 calc = CalcCheckSum(blocks,2,2)
196 if crc == calc then isOk='Ok' else isOk = 'Error' end
197 io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
198
199 -- Checksum Type 3 (DATAAREA 1)
200 crc = GetCheckSum(blocks,1,3)
201 calc = CalcCheckSum(blocks,1,3)
202 if crc == calc then isOk='Ok' else isOk = 'Error' end
203 io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
204
205 -- Checksum Type 3 (DATAAREA 2)
206 crc = GetCheckSum(blocks,2,3)
207 calc = CalcCheckSum(blocks,2,3)
208 if crc == calc then isOk='Ok' else isOk = 'Error' end
209 io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
210 end
211
212 local cmd
213 local blockdata
214 for _,b in pairs(blocks) do
215
216 blockdata = b
217
218 if _%4 ~= 3 then
219 if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
220 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , RANDOM)
221 local baseStr = utils.ConvertHexToAscii(base)
222 local key = md5.sumhexa(baseStr)
223 local enc = core.aes128_encrypt(key, blockdata)
224 local hex = utils.ConvertAsciiToBytes(enc)
225 hex = utils.ConvertBytesToHex(hex)
226
227 blockdata = hex
228 io.write( _..',')
229 end
230 end
231
232 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
233 local err = core.SendCommand(cmd:getBytes())
234 if err then
235 return err
236 end
237 end
238 io.write('\n')
239 end
240
241 local function Num2Card(m, l)
242
243 local k = {
244 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B,
245 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54,0x56, 0x57, 0x58, 0x59, 0x5A, 0x00
246 }
247 local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
248 local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
249
250 if msw > 0x17ea1 then
251 return "too big"
252 end
253
254 if msw == 0x17ea1 and lsw > 0x8931fee8 then
255 return "out of range"
256 end
257
258 local s = ""
259 local index
260 for i = 1,10 do
261 index, msw, lsw = DivideByK( msw, lsw)
262 if ( index <= 1 ) then
263 s = char(k[index]) .. s
264 else
265 s = char(k[index-1]) .. s
266 end
267 print (index-1, msw, lsw)
268 end
269 return s
270 end
271 --33LRT-LM9Q9
272 --7, 122, 3474858630
273 --20, 4, 1008436634
274 --7, 0, 627182959
275 --17, 0, 21626998
276 --16, 0, 745758
277 --23, 0, 25715
278 --21, 0, 886
279 --16, 0, 30
280 --1, 0, 1
281 --1, 0, 0
282
283 function DivideByK(msw, lsw)
284
285 local lowLSW
286 local highLSW
287 local remainder = 0
288 local RADIX = 29
289
290 --local num = 0 | band( rshift(msw,16), 0xffff)
291 local num = band( rshift(msw, 16), 0xffff)
292
293 --highLSW = 0 | lshift( (num / RADIX) , 16)
294 highLSW = lshift( (num / RADIX) , 16)
295 remainder = num % RADIX
296
297 num = bor( lshift(remainder,16), band(msw, 0xffff))
298
299 --highLSW |= num / RADIX
300 highLSW = highLSW or (num / RADIX)
301 remainder = num % RADIX
302
303 num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
304
305 --lowLSW = 0 | (num / RADIX) << 16
306 lowLSW = 0 or (lshift( (num / RADIX), 16))
307 remainder = num % RADIX
308
309 num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
310
311 lowLSW = bor(lowLSW, (num / RADIX))
312 remainder = num % RADIX
313 return remainder, highLSW, lowLSW
314
315 -- uint num = 0 | (msw >> 16) & 0xffff;
316
317 -- highLSW = 0 | (num / RADIX) << 16;
318 -- remainder = num % RADIX;
319
320 -- num = (remainder << 16) | (msw & 0xffff);
321
322 -- highLSW |= num / RADIX;
323 -- remainder = num % RADIX;
324
325 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
326
327 -- lowLSW = 0 | (num / RADIX) << 16;
328 -- remainder = num % RADIX;
329
330 -- num = (remainder << 16) | (lsw & 0xffff);
331
332 -- lowLSW |= num / RADIX;
333 -- remainder = num % RADIX;
334
335 end
336
337 local function main(args)
338
339 print( string.rep('--',20) )
340 print( string.rep('--',20) )
341
342 local result, err, hex
343 local maxed = false
344 local inputTemplate = "dumpdata.bin"
345 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
346
347 -- Arguments for the script
348 for o, a in getopt.getopt(args, 'hmi:o:') do
349 if o == "h" then return help() end
350 if o == "m" then maxed = true end
351 if o == "o" then outputTemplate = a end
352 if o == "i" then inputTemplate = a end
353 end
354
355 -- Turn off Debug
356 local cmdSetDbgOff = "hf mf dbg 0"
357 core.console( cmdSetDbgOff)
358
359 -- Load dump.bin file
360 print( (' Load data from %s'):format(inputTemplate))
361 hex, err = utils.ReadDumpFile(inputTemplate)
362 if not hex then return oops(err) end
363
364 local blocks = {}
365 local blockindex = 0
366 for i = 1, #hex, 32 do
367 blocks[blockindex] = hex:sub(i,i+31)
368 blockindex = blockindex + 1
369 end
370
371 if DEBUG then
372 print(' Validating checksums')
373 ValidateCheckSums(blocks)
374 end
375
376 --
377 print( string.rep('--',20) )
378 print(' Gathering info')
379 local uid = blocks[0]:sub(1,8)
380 local toytype = blocks[1]:sub(1,4)
381 local cardidLsw = blocks[1]:sub(9,16)
382 local cardidMsw = blocks[1]:sub(17,24)
383 local subtype = blocks[1]:sub(25,28)
384
385 -- Show info
386 print( string.rep('--',20) )
387
388 local item = toys.Find( toytype, subtype)
389 if item then
390 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
391 print(' ITEM TYPE : '..itemStr )
392 else
393 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
394 end
395
396 print( (' UID : 0x%s'):format(uid) )
397 print( (' CARDID : 0x%s %s [%s]'):format(
398 cardidMsw,cardidLsw,
399 --Num2Card(cardidMsw, cardidLsw))
400 '')
401 )
402 print( string.rep('--',20) )
403
404
405 -- Experience should be:
406 local experience = blocks[8]:sub(1,6)
407 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
408
409 local money = blocks[8]:sub(7,10)
410 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
411
412 --
413
414 -- Sequence number
415 local seqnum = blocks[8]:sub(18,19)
416 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
417
418 local fairy = blocks[9]:sub(1,8)
419 --FD0F = Left, FF0F = Right
420 local path = 'not choosen'
421 if fairy:sub(2,2) == 'D' then
422 path = 'Left'
423 elseif fairy:sub(2,2) == 'F' then
424 path = 'Right'
425 end
426 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
427
428 local hat = blocks[9]:sub(8,11)
429 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
430
431 local level = blocks[13]:sub(27,28)
432 print(('LEVEL : %d'):format( tonumber(level,16)))
433 --hälsa: 667 029b
434 --local health = blocks[]:sub();
435 --print(('Health : %d'):format( tonumber(health,16))
436
437 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
438 local heropoints = blocks[13]:sub(20,23)
439 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
440
441 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
442 local challenges = blocks[16]:sub(25,32)
443 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
444
445 -- Character Name
446 local name1 = blocks[10]:sub(1,32)
447 local name2 = blocks[12]:sub(1,32)
448 print('Custom name : '..utils.ConvertHexToAscii(name1..name2))
449
450 if maxed then
451 print('Lets try to max out some values')
452 -- max out money, experience
453 --print (blocks[8])
454 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
455 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
456 --print (blocks[8])
457
458 -- max out hero challenges
459 --print (blocks[16])
460 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
461 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
462 --print (blocks[16])
463
464 -- max out heropoints
465 --print (blocks[13])
466 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
467 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
468 --print (blocks[13])
469
470 -- Update Checksums
471 print('Updating all checksums')
472 SetCheckSum(blocks, 3)
473 SetCheckSum(blocks, 2)
474 SetCheckSum(blocks, 1)
475 SetCheckSum(blocks, 0)
476
477 print('Validating all checksums')
478 ValidateCheckSums(blocks)
479 end
480
481 --Load dumpdata to emulator memory
482 if DEBUG then
483 print('Sending dumpdata to emulator memory')
484 err = LoadEmulator(blocks)
485 if err then return oops(err) end
486 core.clearCommandBuffer()
487 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')
488 end
489 end
490 main(args)
Impressum, Datenschutz