]> git.zerfleddert.de Git - proxmark3-svn/blame - client/scripts/tnp3sim.lua
FIX: thanks @tony, for pointing out a "end" statement inside tnp3sim.lua
[proxmark3-svn] / client / scripts / tnp3sim.lua
CommitLineData
b915fda3 1local cmds = require('commands')
2local getopt = require('getopt')
3local bin = require('bin')
4local lib14a = require('read14a')
5local utils = require('utils')
6local md5 = require('md5')
cff17e78 7local toys = require('default_toys')
b915fda3 8
9example =[[
10 1. script run tnp3sim
11 2. script run tnp3sim -m
12 3. script run tnp3sim -m -i myfile
13]]
14author = "Iceman"
15usage = "script run tnp3sim -h -m -i <filename>"
16desc =[[
17This script will try to load a binary datadump of a Mifare TNP3xxx card.
18It vill try to validate all checksums and view some information stored in the dump
19For an experimental mode, it tries to manipulate some data.
20At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim"
21
22Arguments:
23 -h : this help
24 -m : Maxed out items (experimental)
25 -i : filename for the datadump to read (bin)
b915fda3 26
1b3c567d 27 ]]
04a6113f 28
1b3c567d 29local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
30local DEBUG = false -- the debug flag
31local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
04a6113f 32
33local band = bit32.band
34local bor = bit32.bor
35local lshift = bit32.lshift
36local rshift = bit32.rshift
37local byte = string.byte
38local char = string.char
39local sub = string.sub
40local format = string.format
41
b915fda3 42---
43-- A debug printout-function
44function 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
58end
59---
60-- This is only meant to be used when errors occur
61function oops(err)
62 print("ERROR: ",err)
3b4fa542 63 return nil,err
b915fda3 64end
65---
66-- Usage help
67function help()
68 print(desc)
69 print("Example usage")
70 print(example)
71end
72--
73-- Exit message
74function ExitMsg(msg)
75 print( string.rep('--',20) )
76 print( string.rep('--',20) )
77 print(msg)
78 print()
79end
80
b915fda3 81local 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
86end
87-- blocks with data
88-- there are two dataareas, in block 8 or block 36, ( 1==8 ,
89-- checksum type = 0, 1, 2, 3
90local 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)
108end
109
110local 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
136end
137
138function 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)
154end
155
156local 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))
b915fda3 200
b915fda3 201 local cmd
202 local blockdata
203 for _,b in pairs(blocks) do
204
205 blockdata = b
206
207 if _%4 ~= 3 then
208 if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
1b3c567d 209 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , RANDOM)
b915fda3 210 local baseStr = utils.ConvertHexToAscii(base)
211 local key = md5.sumhexa(baseStr)
1b3c567d 212 local enc = core.aes128_encrypt(key, blockdata)
b915fda3 213 local hex = utils.ConvertAsciiToBytes(enc)
214 hex = utils.ConvertBytesToHex(hex)
215
216 blockdata = hex
217 io.write( _..',')
218 end
219 end
220
221 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
222 local err = core.SendCommand(cmd:getBytes())
223 if err then
224 return err
225 end
226 end
227 io.write('\n')
228end
229
04a6113f 230local 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
259end
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
272function 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
40762506 304 -- uint num = 0 | (msw >> 16) & 0xffff;
04a6113f 305
40762506 306 -- highLSW = 0 | (num / RADIX) << 16;
307 -- remainder = num % RADIX;
04a6113f 308
40762506 309 -- num = (remainder << 16) | (msw & 0xffff);
04a6113f 310
40762506 311 -- highLSW |= num / RADIX;
312 -- remainder = num % RADIX;
04a6113f 313
40762506 314 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
04a6113f 315
40762506 316 -- lowLSW = 0 | (num / RADIX) << 16;
317 -- remainder = num % RADIX;
04a6113f 318
40762506 319 -- num = (remainder << 16) | (lsw & 0xffff);
04a6113f 320
40762506 321 -- lowLSW |= num / RADIX;
322 -- remainder = num % RADIX;
04a6113f 323
324end
325
b915fda3 326local 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
b915fda3 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
1b3c567d 361 print(' Validating checksums')
b915fda3 362 ValidateCheckSums(blocks)
363 end
364
365 --
366 print( string.rep('--',20) )
367 print(' Gathering info')
368 local uid = blocks[0]:sub(1,8)
c3fe354b 369 local toytype = blocks[1]:sub(1,4)
04a6113f 370 local cardidLsw = blocks[1]:sub(9,16)
371 local cardidMsw = blocks[1]:sub(17,24)
c3fe354b 372 local subtype = blocks[1]:sub(25,28)
b915fda3 373
374 -- Show info
375 print( string.rep('--',20) )
c3fe354b 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])
1b3c567d 380 print(' ITEM TYPE : '..itemStr )
c3fe354b 381 else
382 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
383 end
384
b915fda3 385 print( (' UID : 0x%s'):format(uid) )
04a6113f 386 print( (' CARDID : 0x%s %s [%s]'):format(
387 cardidMsw,cardidLsw,
40762506 388 --Num2Card(cardidMsw, cardidLsw))
389 '')
04a6113f 390 )
b915fda3 391 print( string.rep('--',20) )
392
04a6113f 393
1b3c567d 394 -- Experience should be:
b915fda3 395 local experience = blocks[8]:sub(1,6)
1b3c567d 396 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
397
b915fda3 398 local money = blocks[8]:sub(7,10)
399 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
1b3c567d 400
401 --
402
403 -- Sequence number
404 local seqnum = blocks[8]:sub(18,19)
405 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
406
b915fda3 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)))
1b3c567d 419
420 local level = blocks[13]:sub(27,28)
421 print(('LEVEL : %d'):format( tonumber(level,16)))
fb2d2488 422