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