]> git.zerfleddert.de Git - proxmark3-svn/blame - client/scripts/tnp3sim.lua
REM: Remove a double pasted section of local variables.
[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))
200end
201
b915fda3 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
1b3c567d 210 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , RANDOM)
b915fda3 211 local baseStr = utils.ConvertHexToAscii(base)
212 local key = md5.sumhexa(baseStr)
1b3c567d 213 local enc = core.aes128_encrypt(key, blockdata)
b915fda3 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')
229end
230
04a6113f 231local 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
260end
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
273function 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
40762506 305 -- uint num = 0 | (msw >> 16) & 0xffff;
04a6113f 306
40762506 307 -- highLSW = 0 | (num / RADIX) << 16;
308 -- remainder = num % RADIX;
04a6113f 309
40762506 310 -- num = (remainder << 16) | (msw & 0xffff);
04a6113f 311
40762506 312 -- highLSW |= num / RADIX;
313 -- remainder = num % RADIX;
04a6113f 314
40762506 315 -- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
04a6113f 316
40762506 317 -- lowLSW = 0 | (num / RADIX) << 16;
318 -- remainder = num % RADIX;
04a6113f 319
40762506 320 -- num = (remainder << 16) | (lsw & 0xffff);
04a6113f 321
40762506 322 -- lowLSW |= num / RADIX;
323 -- remainder = num % RADIX;
04a6113f 324
325end
326
b915fda3 327local 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
b915fda3 349 -- Load dump.bin file
350 print( (' Load data from %s'):format(inputTemplate))
351 hex, err = utils.ReadDumpFile(inputTemplate)
352 if not hex then return oops(err) end
353
354 local blocks = {}
355 local blockindex = 0
356 for i = 1, #hex, 32 do
357 blocks[blockindex] = hex:sub(i,i+31)
358 blockindex = blockindex + 1
359 end
360
361 if DEBUG then
1b3c567d 362 print(' Validating checksums')
b915fda3 363 ValidateCheckSums(blocks)
364 end
365
366 --
367 print( string.rep('--',20) )
368 print(' Gathering info')
369 local uid = blocks[0]:sub(1,8)
c3fe354b 370 local toytype = blocks[1]:sub(1,4)
04a6113f 371 local cardidLsw = blocks[1]:sub(9,16)
372 local cardidMsw = blocks[1]:sub(17,24)
c3fe354b 373 local subtype = blocks[1]:sub(25,28)
b915fda3 374
375 -- Show info
376 print( string.rep('--',20) )
c3fe354b 377
378 local item = toys.Find( toytype, subtype)
379 if item then
380 local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
1b3c567d 381 print(' ITEM TYPE : '..itemStr )
c3fe354b 382 else
383 print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
384 end
385
b915fda3 386 print( (' UID : 0x%s'):format(uid) )
04a6113f 387 print( (' CARDID : 0x%s %s [%s]'):format(
388 cardidMsw,cardidLsw,
40762506 389 --Num2Card(cardidMsw, cardidLsw))
390 '')
04a6113f 391 )
b915fda3 392 print( string.rep('--',20) )
393
04a6113f 394
1b3c567d 395 -- Experience should be:
b915fda3 396 local experience = blocks[8]:sub(1,6)
1b3c567d 397 print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
398
b915fda3 399 local money = blocks[8]:sub(7,10)
400 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
1b3c567d 401
402 --
403
404 -- Sequence number
405 local seqnum = blocks[8]:sub(18,19)
406 print(('Sequence number : %d'):format( tonumber(seqnum,16)))
407
b915fda3 408 local fairy = blocks[9]:sub(1,8)
409 --FD0F = Left, FF0F = Right
410 local path = 'not choosen'
411 if fairy:sub(2,2) == 'D' then
412 path = 'Left'
413 elseif fairy:sub(2,2) == 'F' then
414 path = 'Right'
415 end
416 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
417
418 local hat = blocks[9]:sub(8,11)
419 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
1b3c567d 420
421 local level = blocks[13]:sub(27,28)
422 print(('LEVEL : %d'):format( tonumber(level,16)))
fb2d2488 423