]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/tnp3sim.lua
Fixed issue #43 on github
[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 = true -- the debug flag
30 ---
31 -- A debug printout-function
32 function dbg(args)
33 if not DEBUG then
34 return
35 end
36
37 if type(args) == "table" then
38 local i = 1
39 while result[i] do
40 dbg(result[i])
41 i = i+1
42 end
43 else
44 print("###", args)
45 end
46 end
47 ---
48 -- This is only meant to be used when errors occur
49 function oops(err)
50 print("ERROR: ",err)
51 end
52 ---
53 -- Usage help
54 function help()
55 print(desc)
56 print("Example usage")
57 print(example)
58 end
59 --
60 -- Exit message
61 function ExitMsg(msg)
62 print( string.rep('--',20) )
63 print( string.rep('--',20) )
64 print(msg)
65 print()
66 end
67
68
69 local function writedumpfile(infile)
70 t = infile:read("*all")
71 len = string.len(t)
72 local len,hex = bin.unpack(("H%d"):format(len),t)
73 return hex
74 end
75 -- blocks with data
76 -- there are two dataareas, in block 8 or block 36, ( 1==8 ,
77 -- checksum type = 0, 1, 2, 3
78 local function GetCheckSum(blocks, dataarea, chksumtype)
79
80 local crc
81 local area = 36
82 if dataarea == 1 then
83 area = 8
84 end
85
86 if chksumtype == 0 then
87 crc = blocks[1]:sub(29,32)
88 elseif chksumtype == 1 then
89 crc = blocks[area]:sub(29,32)
90 elseif chksumtype == 2 then
91 crc = blocks[area]:sub(25,28)
92 elseif chksumtype == 3 then
93 crc = blocks[area]:sub(21,24)
94 end
95 return utils.SwapEndianness(crc,16)
96 end
97
98 local function SetCheckSum(blocks, chksumtype)
99
100 if blocks == nil then return nil, 'Argument \"blocks\" nil' end
101 local newcrc
102 local area1 = 8
103 local area2 = 36
104
105 if chksumtype == 0 then
106 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0))
107 blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
108 elseif chksumtype == 1 then
109 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1))
110 blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
111 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1))
112 blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
113 elseif chksumtype == 2 then
114 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2))
115 blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32)
116 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2))
117 blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32)
118 elseif chksumtype == 3 then
119 newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3))
120 blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32)
121 newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3))
122 blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32)
123 end
124 end
125
126 function CalcCheckSum(blocks, dataarea, chksumtype)
127 local area = 36
128 if dataarea == 1 then
129 area = 8
130 end
131
132 if chksumtype == 0 then
133 data = blocks[0]..blocks[1]:sub(1,28)
134 elseif chksumtype == 1 then
135 data = blocks[area]:sub(1,28)..'0500'
136 elseif chksumtype == 2 then
137 data = blocks[area+1]..blocks[area+2]..blocks[area+4]
138 elseif chksumtype == 3 then
139 data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0)
140 end
141 return utils.Crc16(data)
142 end
143
144 local function ValidateCheckSums(blocks)
145
146 local isOk, crc, calc
147 -- Checksum Type 0
148 crc = GetCheckSum(blocks,1,0)
149 calc = CalcCheckSum(blocks, 1, 0)
150 if crc == calc then isOk='Ok' else isOk = 'Error' end
151 io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk))
152
153 -- Checksum Type 1 (DATAAREAHEADER 1)
154 crc = GetCheckSum(blocks,1,1)
155 calc = CalcCheckSum(blocks,1,1)
156 if crc == calc then isOk='Ok' else isOk = 'Error' end
157 io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
158
159 -- Checksum Type 1 (DATAAREAHEADER 2)
160 crc = GetCheckSum(blocks,2,1)
161 calc = CalcCheckSum(blocks,2,1)
162 if crc == calc then isOk='Ok' else isOk = 'Error' end
163 io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
164
165 -- Checksum Type 2 (DATAAREA 1)
166 crc = GetCheckSum(blocks,1,2)
167 calc = CalcCheckSum(blocks,1,2)
168 if crc == calc then isOk='Ok' else isOk = 'Error' end
169 io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
170
171 -- Checksum Type 2 (DATAAREA 2)
172 crc = GetCheckSum(blocks,2,2)
173 calc = CalcCheckSum(blocks,2,2)
174 if crc == calc then isOk='Ok' else isOk = 'Error' end
175 io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
176
177 -- Checksum Type 3 (DATAAREA 1)
178 crc = GetCheckSum(blocks,1,3)
179 calc = CalcCheckSum(blocks,1,3)
180 if crc == calc then isOk='Ok' else isOk = 'Error' end
181 io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
182
183 -- Checksum Type 3 (DATAAREA 2)
184 crc = GetCheckSum(blocks,2,3)
185 calc = CalcCheckSum(blocks,2,3)
186 if crc == calc then isOk='Ok' else isOk = 'Error' end
187 io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
188 end
189
190
191 local function LoadEmulator(blocks)
192 local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
193 local cmd
194 local blockdata
195 for _,b in pairs(blocks) do
196
197 blockdata = b
198
199 if _%4 ~= 3 then
200 if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
201 local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , HASHCONSTANT)
202 local baseStr = utils.ConvertHexToAscii(base)
203 local key = md5.sumhexa(baseStr)
204 local enc = core.aes(key, blockdata)
205 local hex = utils.ConvertAsciiToBytes(enc)
206 hex = utils.ConvertBytesToHex(hex)
207
208 blockdata = hex
209 io.write( _..',')
210 end
211 end
212
213 cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
214 local err = core.SendCommand(cmd:getBytes())
215 if err then
216 return err
217 end
218 end
219 io.write('\n')
220 end
221
222 local function main(args)
223
224 print( string.rep('--',20) )
225 print( string.rep('--',20) )
226
227 local result, err, hex
228 local maxed = false
229 local inputTemplate = "dumpdata.bin"
230 local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
231
232 -- Arguments for the script
233 for o, a in getopt.getopt(args, 'hmi:o:') do
234 if o == "h" then return help() end
235 if o == "m" then maxed = true end
236 if o == "o" then outputTemplate = a end
237 if o == "i" then inputTemplate = a end
238 end
239
240 -- Turn off Debug
241 local cmdSetDbgOff = "hf mf dbg 0"
242 core.console( cmdSetDbgOff)
243
244 -- Look for tag present on reader,
245 result, err = lib14a.read1443a(false)
246 if not result then return oops(err) end
247
248 core.clearCommandBuffer()
249
250 if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx
251 return oops('This is not a TNP3xxx tag. aborting.')
252 end
253
254 -- Show tag info
255 print((' Found tag : %s'):format(result.name))
256
257 -- Load dump.bin file
258 print( (' Load data from %s'):format(inputTemplate))
259 hex, err = utils.ReadDumpFile(inputTemplate)
260 if not hex then return oops(err) end
261
262 local blocks = {}
263 local blockindex = 0
264 for i = 1, #hex, 32 do
265 blocks[blockindex] = hex:sub(i,i+31)
266 blockindex = blockindex + 1
267 end
268
269 if DEBUG then
270 print('Validating checksums in the loaded datadump')
271 ValidateCheckSums(blocks)
272 end
273
274 --
275 print( string.rep('--',20) )
276 print(' Gathering info')
277 local uid = blocks[0]:sub(1,8)
278 local itemtype = blocks[1]:sub(1,4)
279 local cardid = blocks[1]:sub(9,24)
280
281 -- Show info
282 print( string.rep('--',20) )
283 print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) )
284 print( (' UID : 0x%s'):format(uid) )
285 print( (' CARDID : 0x%s'):format(cardid ) )
286 print( string.rep('--',20) )
287
288 -- lets do something.
289 --
290 local experience = blocks[8]:sub(1,6)
291 print(('Experience : %d'):format(utils.SwapEndianness(experience,24)))
292 local money = blocks[8]:sub(7,10)
293 print(('Money : %d'):format(utils.SwapEndianness(money,16)))
294 local fairy = blocks[9]:sub(1,8)
295 --FD0F = Left, FF0F = Right
296 local path = 'not choosen'
297 if fairy:sub(2,2) == 'D' then
298 path = 'Left'
299 elseif fairy:sub(2,2) == 'F' then
300 path = 'Right'
301 end
302 print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
303
304 local hat = blocks[9]:sub(8,11)
305 print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
306
307 --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
308 local heropoints = blocks[13]:sub(20,23)
309 print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
310
311 --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
312 local challenges = blocks[16]:sub(25,32)
313 print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
314
315 if maxed then
316 print('Lets try to max out some values')
317 -- max out money, experience
318 --print (blocks[8])
319 blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
320 blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
321 --print (blocks[8])
322
323 -- max out hero challenges
324 --print (blocks[16])
325 blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
326 blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
327 --print (blocks[16])
328
329 -- max out heropoints
330 --print (blocks[13])
331 blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
332 blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
333 --print (blocks[13])
334
335 -- Update Checksums
336 print('Updating all checksums')
337 SetCheckSum(blocks, 3)
338 SetCheckSum(blocks, 2)
339 SetCheckSum(blocks, 1)
340 SetCheckSum(blocks, 0)
341
342 print('Validating all checksums')
343 ValidateCheckSums(blocks)
344 end
345
346 --Load dumpdata to emulator memory
347 if DEBUG then
348 print('Sending dumpdata to emulator memory')
349 err = LoadEmulator(blocks)
350 if err then return oops(err) end
351 core.clearCommandBuffer()
352 print('The simulation is now prepared.\n --> run \"hf mf sim 5 '..uid..'\" <--')
353 end
354 end
355 main(args)
Impressum, Datenschutz