]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/tnp3sim.lua
MF Ultralight - Iceman's updates + mine
[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 local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
29 local DEBUG = true -- 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 toytype = blocks[1]:sub(1,4)
386 local cardidLsw = blocks[1]:sub(9,16)
387 local cardidMsw = blocks[1]:sub(17,24)
388 local subtype = blocks[1]:sub(25,28)
389
390 -- Show info
391 print( string.rep('--',20) )
392
393 local item = toys.Find( toytype, subtype)
394 if item then
395 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
396 print(' ITEM TYPE :'..itemStr )
397 else
398 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
399 end
400
401 print( (' UID : 0x%s'):format(uid) )
402 print( (' CARDID : 0x%s %s [%s]'):format(
403 cardidMsw,cardidLsw,
404 --Num2Card(cardidMsw, cardidLsw))
405 '')
406 )
407 print( string.rep('--',20) )
408
409
410 -- lets do something.
411 --
412 local experience = blocks[8]:sub(1,6)
413 print(('Experience : %d'):format(utils.SwapEndianness(experience,24)))
414 local money = blocks[8]:sub(7,10)
415 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
416 local fairy = blocks[9]:sub(1,8)
417 --FD0F = Left, FF0F = Right
418 local path = 'not choosen'
419 if fairy:sub(2,2) == 'D' then
420 path = 'Left'
421 elseif fairy:sub(2,2) == 'F' then
422 path = 'Right'
423 end
424 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
425
426 local hat = blocks[9]:sub(8,11)
427 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
428
429 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
430 local heropoints = blocks[13]:sub(20,23)
431 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
432
433 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
434 local challenges = blocks[16]:sub(25,32)
435 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
436
437 if maxed then
438 print('Lets try to max out some values')
439 -- max out money, experience
440 --print (blocks[8])
441 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
442 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
443 --print (blocks[8])
444
445 -- max out hero challenges
446 --print (blocks[16])
447 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
448 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
449 --print (blocks[16])
450
451 -- max out heropoints
452 --print (blocks[13])
453 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
454 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
455 --print (blocks[13])
456
457 -- Update Checksums
458 print('Updating all checksums')
459 SetCheckSum(blocks, 3)
460 SetCheckSum(blocks, 2)
461 SetCheckSum(blocks, 1)
462 SetCheckSum(blocks, 0)
463
464 print('Validating all checksums')
465 ValidateCheckSums(blocks)
466 end
467
468 --Load dumpdata to emulator memory
469 if DEBUG then
470 print('Sending dumpdata to emulator memory')
471 err = LoadEmulator(blocks)
472 if err then return oops(err) end
473 core.clearCommandBuffer()
474 print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')
475 end
476 end
477 main(args)
Impressum, Datenschutz