]>
Commit | Line | Data |
---|---|---|
733eb420 | 1 | --[[ |
2 | (example) Legic-Prime Layout with 'Kaba Group Header' | |
3 | +----+----+----+----+----+----+----+----+ | |
4 | 0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f | | |
5 | +----+----+----+----+----+----+----+----+ | |
6 | 0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2| | |
7 | +----+----+----+----+----+----+----+----+ | |
8 | 0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1| | |
9 | +----+----+----+----+----+----+----+----+ | |
10 | 0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0| | |
11 | +----+----+----+----+----+----+----+----+ | |
12 | 0x20|UID1|UID2|kghC| | |
13 | +----+----+----+ | |
14 | --]] | |
15 | ||
16 | example = "script run legic" | |
17 | author = "Mosci" | |
7f0cb92e | 18 | version = "1.0" |
733eb420 | 19 | desc = |
20 | [[ | |
21 | ||
22 | This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024) | |
23 | it's kinda interactive with following commands in three categories: | |
24 | ||
25 | Data I/O Segment Manipulation File I/O | |
26 | ------------------ -------------------- --------------- | |
27 | rt => read Tag ds => dump Segments lf => load File | |
28 | wt => write Tag as => add Segment sf => save File | |
7f0cb92e | 29 | es => edit Segment xf => xor File |
30 | ct => copy io Tag ed => edit Data | |
31 | tc => copy oi Tag rs => remove Segment | |
32 | di => dump inTag cc => check Segment-CRC | |
33 | do => dump outTag ck => check KGH | |
733eb420 | 34 | tk => toggle KGH-Flag |
7f0cb92e | 35 | mt => make Token |
36 | q => quit et => edit Token h => this Help | |
733eb420 | 37 | |
38 | Data I/O | |
39 | rt: 'read tag' - reads a tag placed near to the PM3 | |
40 | wt: 'write tag' - writes the content of the 'virtual inTag' to a tag placed near to th PM3 | |
41 | without the need of changing anything - MCD,MSN,MCC will be read from the tag | |
42 | before and applied to the output. | |
43 | ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed | |
44 | tc: 'copy tag' - copy the 'second virtual Tag' to 'virtual TAG' - not usefull yet, but inernally needed | |
45 | di: 'dump inTag' - shows the current content of the 'virtual Tag' | |
46 | do: 'dump outTag' - shows the current content of the 'virtual outTag' | |
47 | ||
48 | Segment Manipulation | |
49 | (all manipulations happens only in the 'virtual inTAG' - they need to be written with 'wt' to take effect) | |
50 | ds: 'dump Segments' - will show the content of a selected Segment | |
51 | as: 'add Segment' - will add a 'empty' Segment to the inTag | |
52 | es: 'edit Segment' - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid) | |
53 | all other Segment-Header-Values are either calculated or not needed to edit (yet) | |
7f0cb92e | 54 | ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data) |
55 | et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data) | |
56 | mt: 'make Token' - create a Token 'from scratch' (guided) | |
733eb420 | 57 | rs: 'remove segment' - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token) |
58 | cc: 'check Segment-CRC'- checks & calculates (if check failed) the Segment-CRC of all Segments | |
59 | ck: 'check KGH-CRC' - checks the and calculates a 'Kaba Group Header' if one was detected | |
60 | 'Kaba Group Header CRC calculation' | |
61 | tk: 'toggle KGH' - toglle the (script-internal) flag for kgh-calculation for a segment | |
62 | xc: 'etra c' - show string that was used to calculate the kgh-crc of a segment | |
63 | ||
64 | Input/Output | |
65 | lf: 'load file' - load a (xored) file from the local Filesystem into the 'virtual inTag' | |
66 | sf: 'save file' - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC) | |
67 | xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values) | |
68 | ||
69 | ]] | |
70 | ||
7f0cb92e | 71 | --- |
72 | -- requirements | |
733eb420 | 73 | local utils = require('utils') |
74 | local getopt = require('getopt') | |
75 | ||
7f0cb92e | 76 | --- |
77 | -- global variables / defines | |
733eb420 | 78 | local bxor = bit32.bxor |
79 | local bbit = bit32.extract | |
80 | local input = utils.input | |
81 | local confirm = utils.confirm | |
82 | ||
7f0cb92e | 83 | --- |
733eb420 | 84 | -- This is only meant to be used when errors occur |
85 | function oops(err) | |
86 | print("ERROR: ",err) | |
87 | return nil, err | |
88 | end | |
89 | ||
90 | --- | |
91 | -- Usage help | |
92 | function help() | |
93 | print(desc) | |
7f0cb92e | 94 | print(version) |
733eb420 | 95 | print("Example usage") |
96 | print(example) | |
97 | end | |
98 | ||
99 | --- | |
100 | -- table check helper | |
101 | function istable(t) | |
102 | return type(t) == 'table' | |
103 | end | |
104 | ||
105 | --- | |
7f0cb92e | 106 | -- xor single byte |
733eb420 | 107 | function xorme(hex, xor, index) |
108 | if ( index >= 23 ) then | |
109 | return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) )) | |
110 | else | |
111 | return hex | |
112 | end | |
113 | end | |
114 | ||
115 | --- | |
116 | -- (de)obfuscate bytes | |
117 | function xorBytes(inBytes, crc) | |
118 | local bytes = {} | |
119 | for index = 1, #inBytes do | |
120 | bytes[index] = xorme(inBytes[index], crc, index) | |
121 | end | |
122 | if (#inBytes == #bytes) then | |
123 | -- replace crc | |
124 | bytes[5] = string.sub(crc,-2) | |
125 | return bytes | |
126 | else | |
127 | print("error: byte-count missmatch") | |
128 | return false | |
129 | end | |
130 | end | |
131 | ||
132 | --- | |
133 | -- check availability of file | |
134 | function file_check(file_name) | |
135 | local file_found=io.open(file_name, "r") | |
136 | if file_found==nil then | |
137 | return false | |
138 | else | |
139 | return true | |
140 | end | |
141 | end | |
142 | ||
143 | --- | |
144 | -- read file into virtual-tag | |
145 | function readFile(filename) | |
146 | local bytes = {} | |
147 | local tag = {} | |
148 | if (file_check(filename)==false) then | |
149 | return oops("input file: "..filename.." not found") | |
150 | else | |
151 | bytes = getInputBytes(filename) | |
152 | if (bytes == false) then return oops('couldnt get input bytes') | |
153 | else | |
154 | -- make plain bytes | |
155 | bytes = xorBytes(bytes,bytes[5]) | |
156 | -- create Tag for plain bytes | |
157 | tag=createTagTable() | |
158 | -- load plain bytes to tag-table | |
159 | tag=bytesToTag(bytes, tag) | |
160 | end | |
161 | end | |
162 | return tag | |
163 | end | |
164 | ||
165 | --- | |
166 | -- write bytes to file | |
733eb420 | 167 | function writeFile(bytes, filename) |
168 | if (filename~='MylegicClone.hex') then | |
169 | if (file_check(filename)) then | |
170 | local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?") | |
171 | if (answer==false) then return print("user abort") end | |
172 | end | |
173 | end | |
174 | local line | |
175 | local bcnt=0 | |
176 | local fho,err = io.open(filename, "w") | |
177 | if err then oops("OOps ... faild to open output-file ".. filename) end | |
178 | bytes=xorBytes(bytes, bytes[5]) | |
179 | for i = 1, #bytes do | |
180 | if (bcnt == 0) then | |
181 | line=bytes[i] | |
182 | elseif (bcnt <= 7) then | |
183 | line=line.." "..bytes[i] | |
184 | end | |
185 | if (bcnt == 7) then | |
186 | -- write line to new file | |
187 | fho:write(line.."\n") | |
188 | -- reset counter & line | |
189 | bcnt=-1 | |
190 | line="" | |
191 | end | |
192 | bcnt=bcnt+1 | |
193 | end | |
194 | fho:close() | |
195 | print("\nwrote ".. #bytes .." bytes to " .. filename) | |
196 | return true | |
197 | end | |
198 | ||
7f0cb92e | 199 | --- |
200 | -- put certain bytes into a new table | |
201 | function bytesToTable(bytes, bstart, bend) | |
202 | local t={} | |
203 | for i=0, (bend-bstart) do | |
204 | t[i]=bytes[bstart+i] | |
205 | end | |
206 | return t | |
207 | end | |
208 | ||
733eb420 | 209 | --- |
210 | -- read from pm3 into virtual-tag | |
211 | function readFromPM3() | |
212 | local tag, bytes, infile | |
213 | --if (confirm("is the Tag placed onto the Proxmak3 and ready for reading?")) then | |
214 | --print("reading Tag ...") | |
215 | --infile=input("input a name for the temp-file:", "legic.temp") | |
216 | --if (file_check(infile)) then | |
217 | -- local answer = confirm("\nthe output-file "..infile.." alredy exists!\nthis will delete the previous content!\ncontinue?") | |
218 | -- if (answer==false) then return print("user abort") end | |
219 | --end | |
220 | infile="legic.temp" | |
221 | core.console("hf legic reader") | |
222 | core.console("hf legic save "..infile) | |
223 | --print("read temp-file into virtual Tag ...") | |
224 | tag=readFile(infile) | |
225 | return tag | |
226 | --else return print("user abort"); end | |
227 | end | |
228 | ||
7f0cb92e | 229 | --- |
230 | -- write virtual Tag to real Tag | |
231 | function writeToTag(plainBytes, taglen, filename) | |
232 | local bytes | |
233 | if(utils.confirm("\nplace your empty tag onto the PM3 to read & write\n") == false) then | |
234 | return | |
235 | end | |
236 | ||
237 | -- write data to file | |
238 | if (taglen > 0) then | |
239 | WriteBytes = utils.input("enter number of bytes to write?", taglen) | |
240 | ||
241 | -- load file into pm3-buffer | |
242 | if (type(filename)~="string") then filename=input("filename to load to pm3-buffer?","legic.temp") end | |
243 | cmd = 'hf legic load '..filename | |
244 | core.console(cmd) | |
245 | ||
246 | -- write pm3-buffer to Tag | |
247 | for i=0, WriteBytes do | |
248 | if ( i<5 or i>6) then | |
249 | cmd = ('hf legic write 0x%02x 0x01'):format(i) | |
250 | core.console(cmd) | |
251 | --print(cmd) | |
252 | elseif (i == 6) then | |
253 | -- write DCF in reverse order (requires 'mosci-patch') | |
254 | cmd = 'hf legic write 0x05 0x02' | |
255 | core.console(cmd) | |
256 | --print(cmd) | |
257 | else | |
258 | print("skipping byte 0x05 - will be written next step") | |
259 | end | |
260 | utils.Sleep(0.2) | |
261 | end | |
262 | end | |
263 | end | |
264 | ||
733eb420 | 265 | --- |
266 | -- read file into table | |
267 | function getInputBytes(infile) | |
268 | local line | |
269 | local bytes = {} | |
270 | local fhi,err = io.open(infile) | |
271 | if err then oops("faild to read from file ".. infile); return false; end | |
272 | while true do | |
273 | line = fhi:read() | |
274 | if line == nil then break end | |
275 | for byte in line:gmatch("%w+") do | |
276 | table.insert(bytes, byte) | |
277 | end | |
278 | end | |
279 | fhi:close() | |
280 | print(#bytes .. " bytes from "..infile.." loaded") | |
281 | return bytes | |
282 | end | |
283 | ||
284 | --- | |
733eb420 | 285 | -- create tag-table helper |
286 | function createTagTable() | |
287 | local t={ | |
288 | ['MCD'] = '00', | |
289 | ['MSN0']= '11', | |
290 | ['MSN1']= '22', | |
291 | ['MSN2']= '33', | |
292 | ['MCC'] = 'cc', | |
293 | ['DCFl']= 'ff', | |
294 | ['DCFh']= 'ff', | |
295 | ['Type']= 'GAM', | |
296 | ['OLE'] = 0, | |
297 | ['Stamp_len']= 18, | |
298 | ['WRP'] = '00', | |
299 | ['WRC'] = '00', | |
300 | ['RD'] = '00', | |
301 | ['raw'] = '9f', | |
302 | ['SSC'] = 'ff', | |
303 | ['data']= {}, | |
304 | ['bck'] = {}, | |
305 | ['MTC'] = {}, | |
306 | ['SEG'] = {} | |
307 | } | |
308 | return t | |
309 | end | |
310 | ||
311 | --- | |
312 | -- put bytes into tag-table | |
313 | function bytesToTag(bytes, tag) | |
314 | if(istable(tag)) then | |
315 | tag.MCD =bytes[1]; | |
316 | tag.MSN0=bytes[2]; | |
317 | tag.MSN1=bytes[3]; | |
318 | tag.MSN2=bytes[4]; | |
319 | tag.MCC =bytes[5]; | |
320 | tag.DCFl=bytes[6]; | |
321 | tag.DCFh=bytes[7]; | |
322 | tag.raw =bytes[8]; | |
323 | tag.SSC =bytes[9]; | |
324 | tag.Type=getTokenType(tag.DCFl); | |
325 | tag.OLE=bbit("0x"..tag.DCFl,7,1) | |
326 | tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4)) | |
327 | tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3)) | |
328 | tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1)) | |
329 | tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10)) | |
330 | tag.data=bytesToTable(bytes, 10, 13) | |
331 | tag.Bck=bytesToTable(bytes, 14, 20) | |
332 | tag.MTC=bytesToTable(bytes, 21, 22) | |
333 | ||
334 | print("Tag-Type: ".. tag.Type) | |
335 | if (tag.Type=="SAM" and #bytes>23) then | |
336 | tag=segmentsToTag(bytes, tag) | |
337 | print((#tag.SEG+1).." Segment(s) found") | |
7f0cb92e | 338 | -- unsegmented Master-Token |
339 | -- only tag-data | |
340 | else | |
341 | for i=0, #tag.Bck do | |
342 | table.insert(tag.data, tag.Bck[i]) | |
343 | end | |
344 | tag.data[#tag.data]=tag.MTC[0] | |
345 | tag.Bck=nil | |
346 | --tag.MTC[0]=tag.MTC[1] | |
347 | --tag.MTC[1]=nil | |
733eb420 | 348 | end |
349 | print(#bytes.." bytes for Tag processed") | |
350 | return tag | |
351 | end | |
352 | return oops("tag is no table in: bytesToTag ("..type(tag)..")") | |
353 | end | |
354 | ||
355 | --- | |
7f0cb92e | 356 | -- read Tag-Table in bytes-table |
357 | function tagToBytes(tag) | |
733eb420 | 358 | if (istable(tag)) then |
7f0cb92e | 359 | local bytes = {} |
360 | local i, i2 | |
361 | -- main token-data | |
362 | table.insert(bytes, tag.MCD) | |
363 | table.insert(bytes, tag.MSN0) | |
364 | table.insert(bytes, tag.MSN1) | |
365 | table.insert(bytes, tag.MSN2) | |
366 | table.insert(bytes, tag.MCC) | |
367 | table.insert(bytes, tag.DCFl) | |
368 | table.insert(bytes, tag.DCFh) | |
369 | table.insert(bytes, tag.raw) | |
370 | table.insert(bytes, tag.SSC) | |
371 | -- raw token data | |
372 | for i=0, #tag.data do | |
373 | table.insert(bytes, tag.data[i]) | |
733eb420 | 374 | end |
7f0cb92e | 375 | -- backup data |
376 | if(istable(tag.Bck)) then | |
377 | for i=0, #tag.Bck do | |
378 | table.insert(bytes, tag.Bck[i]) | |
733eb420 | 379 | end |
733eb420 | 380 | end |
7f0cb92e | 381 | -- token-create-time / master-token crc |
382 | for i=0, #tag.MTC do | |
383 | table.insert(bytes, tag.MTC[i]) | |
733eb420 | 384 | end |
7f0cb92e | 385 | -- process segments |
386 | if (type(tag.SEG[0])=='table') then | |
387 | for i=0, #tag.SEG do | |
388 | for i2=1, #tag.SEG[i].raw+1 do | |
389 | table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2]) | |
733eb420 | 390 | end |
7f0cb92e | 391 | table.insert(bytes, #bytes+1, tag.SEG[i].crc) |
392 | for i2=0, #tag.SEG[i].data-1 do | |
393 | table.insert(bytes, #bytes+1, tag.SEG[i].data[i2]) | |
733eb420 | 394 | end |
733eb420 | 395 | end |
7f0cb92e | 396 | end |
397 | -- fill with zeros | |
398 | for i=#bytes+1, 1024 do | |
399 | table.insert(bytes, i, '00') | |
400 | end | |
401 | print(#bytes.." bytes of Tag dumped") | |
402 | return bytes | |
403 | end | |
404 | return oops("tag is no table in tagToBytes ("..type(tag)..")") | |
733eb420 | 405 | end |
406 | ||
407 | --- | |
7f0cb92e | 408 | -- make token |
409 | function makeToken() | |
410 | local mt={ | |
411 | ['Type'] = {"SAM", "SAM63", "SAM64", "IAM", "GAM"}, | |
412 | ['DCF'] = {"60ea", "31fa", "30fa", "80fa", "f0fa"}, | |
413 | ['WRP'] = {"15", "2", "2", "2", "2"}, | |
414 | ['WRC'] = {"01", "02", "02", "00", "00"}, | |
415 | ['RD'] = {"01", "00", "00", "00", "00"}, | |
416 | ['Stamp'] = {"00", "00", "00", "00", "00"}, | |
417 | ['Segment'] = {"0d", "c0", "04", "00", "be", "01", "02", "03", "04", "01", "02", "03", "04"} | |
418 | } | |
419 | ttype="" | |
420 | for k, v in pairs(mt.Type) do | |
421 | ttype=ttype..k..") "..v.." " | |
733eb420 | 422 | end |
7f0cb92e | 423 | mtq=tonumber(input("select number for Token-Type\n"..ttype, '1'), 10) |
424 | if (type(mtq)~="number") then return print("selection invalid!") | |
425 | elseif (mtq>#mt.Type) then return print("selection invalid!") | |
426 | else print("Token-Type '"..mt.Type[mtq].."' selected") end | |
427 | local raw=calcHeaderRaw(mt.WRP[mtq], mt.WRC[mtq], mt.RD[mtq]) | |
428 | local mtCRC="00" | |
429 | ||
430 | bytes={"01", "02", "03", "04", "cb", string.sub(mt.DCF[mtq], 0, 2), string.sub(mt.DCF[mtq], 3), raw, | |
431 | "00", "00", "00", "00", "00", "00", "00", "00", | |
432 | "00", "00", "00", "00", "00", "00"} | |
433 | if (mtq==1) then | |
434 | for i=0, #mt.Segment do | |
435 | table.insert(bytes, mt.Segment[i]) | |
733eb420 | 436 | end |
7f0cb92e | 437 | bytes[9]="ff" |
733eb420 | 438 | end |
7f0cb92e | 439 | -- fill bytes |
440 | for i=#bytes, 1023 do table.insert(bytes, "00") end | |
441 | -- if Master-Token -> calc Master-Token-CRC | |
442 | if (mtq>1) then bytes[22]=calcMtCrc(bytes) end | |
443 | local tempTag=createTagTable() | |
444 | -- remove segment if MasterToken | |
445 | if (mtq>1) then tempTag.SEG[0]=nil end | |
446 | return bytesToTag(bytes, tempTag) | |
733eb420 | 447 | end |
448 | ||
7f0cb92e | 449 | --- |
450 | -- edit token-data | |
451 | function editTag(tag) | |
452 | -- for simulation it makes sense to edit everything | |
453 | local edit_sim="MCD MSN0 MSN2 MSN2 MCC DCFl DCFh WRP WRC RD" | |
454 | -- on real tags it makes only sense to edit DCF, WRP, WRC, RD | |
455 | local edit_real="DCFl DCFh WRP WRC RD" | |
456 | if (confirm("do you want to edit non-writeable values (e.g. for simulation)?")) then | |
457 | edit_tag=edit_sim | |
458 | else edit_tag=edit_real end | |
459 | ||
460 | if(istable(tag)) then | |
461 | for k,v in pairs(tag) do | |
462 | if(type(v)~="table" and type(v)~="boolean" and string.find(edit_tag, k)) then | |
463 | tag[k]=input("value for: "..k, v) | |
733eb420 | 464 | end |
465 | end | |
466 | ||
7f0cb92e | 467 | if (tag.Type=="SAM") then ttype="Header"; else ttype="Stamp"; end |
468 | if (confirm("do you want to edit "..ttype.." Data?")) then | |
469 | -- master-token specific | |
470 | if(istable(tag.Bck)==false) then | |
471 | -- stamp-data length=(0xfc-DCFh) | |
472 | -- on MT: SSC holds the Starting Stamp Character (Stamp0) | |
473 | tag.SSC=input(ttype.."0: ", tag.SSC) | |
474 | -- rest of stamp-bytes are in tag.data 0..n | |
475 | for i=0, (tonumber(0xfc ,10)-("%d"):format('0x'..tag.DCFh))-2 do | |
476 | tag.data[i]=input(ttype.. i+1 ..": ", tag.data[i]) | |
733eb420 | 477 | end |
733eb420 | 478 | else |
7f0cb92e | 479 | --- on credentials byte7 should always be 9f and byte8 ff |
480 | -- on Master-Token not (even on SAM63/64 not) | |
481 | -- tag.SSC=input(ttype.."0: ", tag.SSC) | |
482 | for i=0, #tag.data do | |
483 | tag.data[i]=input(ttype.. i ..": ", tag.data[i]) | |
484 | end | |
485 | end | |
733eb420 | 486 | end |
7f0cb92e | 487 | |
488 | bytes=tagToBytes(tag) | |
489 | ||
490 | --- check data-consistency (calculate tag.raw) | |
491 | bytes[8]=calcHeaderRaw(tag.WRP, tag.WRC, tag.RD) | |
492 | ||
493 | --- Master-Token specific | |
494 | -- should be triggered if a SAM was converted to a non-SAM (user-Token to Master-Token) | |
495 | -- or a Master-Token has being edited (also SAM64 & SAM63 - which are in fact Master-Token) | |
496 | if(tag.Type~="SAM" or bytes[6]..bytes[7]~="60ea") then | |
497 | -- calc new Master-Token crc | |
498 | bytes[22]=calcMtCrc(bytes) | |
499 | else | |
500 | -- ensure tag.SSC set to 'ff' on credential-token (SAM) | |
501 | bytes[9]='ff' | |
502 | -- if a Master-Token was converted to a Credential-Token | |
503 | -- lets unset the Time-Area to 00 00 (will contain Stamp-Data on MT) | |
504 | bytes[21]='00' | |
505 | bytes[22]='00' | |
733eb420 | 506 | end |
507 | ||
7f0cb92e | 508 | tag=bytesToTag(bytes, tag) |
733eb420 | 509 | end |
510 | end | |
511 | ||
512 | --- | |
7f0cb92e | 513 | -- calculates header-byte (addr 0x07) |
514 | function calcHeaderRaw(wrp, wrc, rd) | |
515 | local res | |
516 | wrp=("%02x"):format(tonumber(wrp, 10)) | |
517 | rd=tonumber(rd, 16) | |
518 | res=("%02x"):format(tonumber(wrp, 16)+tonumber(wrc.."0", 16)+((rd>0) and tonumber("8"..(rd-1), 16) or 0)) | |
519 | return res | |
733eb420 | 520 | end |
521 | ||
522 | --- | |
523 | -- dump virtual Tag-Data | |
524 | function dumpTag(tag) | |
525 | local i, i2 | |
526 | local res | |
527 | local dp=0 | |
528 | local raw="" | |
529 | -- sytstem area | |
530 | res ="\nCDF: System Area" | |
531 | res= res.."\n"..dumpCDF(tag) | |
7f0cb92e | 532 | -- segments (user-token area) |
533 | if(tag.Type=="SAM") then | |
733eb420 | 534 | res = res.."\n\nADF: User Area" |
535 | for i=0, #tag.SEG do | |
536 | res=res.."\n"..dumpSegment(tag, i).."\n" | |
537 | end | |
538 | end | |
539 | return res | |
540 | end | |
541 | ||
733eb420 | 542 | --- |
543 | -- get segmemnt-data from byte-table | |
544 | function getSegmentData(bytes, start, index) | |
545 | local segment={ | |
546 | ['index'] = '00', | |
547 | ['flag'] = 'c', | |
548 | ['valid'] = 0, | |
549 | ['last'] = 0, | |
550 | ['len'] = 13, | |
551 | ['raw'] = {'00', '00', '00', '00'}, | |
552 | ['WRP'] = 4, | |
553 | ['WRC'] = 0, | |
554 | ['RD'] = 0, | |
555 | ['crc'] = '00', | |
556 | ['data'] = {}, | |
557 | ['kgh'] = false | |
558 | } | |
559 | if (bytes[start]) then | |
560 | local i | |
561 | -- #index | |
562 | segment.index = index | |
563 | -- flag = high nibble of byte 1 | |
564 | segment.flag = string.sub(bytes[start+1],0,1) | |
565 | -- valid = bit 6 of byte 1 | |
566 | segment.valid = bbit("0x"..bytes[start+1],6,1) | |
567 | -- last = bit 7 of byte 1 | |
568 | segment.last = bbit("0x"..bytes[start+1],7,1) | |
569 | -- len = (byte 0)+(bit0-3 of byte 1) | |
570 | segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16) | |
571 | -- raw segment header | |
572 | segment.raw = {bytes[start], bytes[start+1], bytes[start+2], bytes[start+3]} | |
573 | -- wrp (write proteted) = byte 2 | |
574 | segment.WRP = tonumber(bytes[start+2],16) | |
575 | -- wrc (write control) - bit 4-6 of byte 3 | |
576 | segment.WRC = tonumber(bbit("0x"..bytes[start+3],4,3),16) | |
577 | -- rd (read disabled) - bit 7 of byte 3 | |
578 | segment.RD = tonumber(bbit("0x"..bytes[start+3],7,1),16) | |
579 | -- crc byte 4 | |
580 | segment.crc = bytes[start+4] | |
581 | -- segment-data starts at segment.len - segment.header - segment.crc | |
582 | for i=0, (segment.len-5) do | |
583 | segment.data[i]=bytes[start+5+i] | |
584 | end | |
585 | return segment | |
586 | else return false; | |
587 | end | |
588 | end | |
589 | ||
590 | --- | |
591 | -- put segments from byte-table to tag-table | |
592 | function segmentsToTag(bytes, tag) | |
593 | if(#bytes>23) then | |
594 | local start=23 | |
595 | local i=-1 | |
596 | if (istable(tag)) then | |
597 | repeat | |
598 | i=i+1 | |
599 | tag.SEG[i]=getSegmentData(bytes, start, ("%02d"):format(i)) | |
600 | if (tag.Type=="SAM") then | |
601 | if (checkKghCrc(tag, i)) then tag.SEG[i].kgh=true end | |
602 | end | |
603 | start=start+tag.SEG[i].len | |
604 | until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126) | |
605 | return tag | |
606 | else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end | |
607 | else print("no Segments: must be a MIM22") end | |
608 | end | |
609 | ||
733eb420 | 610 | --- |
7f0cb92e | 611 | -- regenerate segment-header (after edit) |
612 | function regenSegmentHeader(segment) | |
613 | local seg=segment | |
614 | local raw = segment.raw | |
733eb420 | 615 | local i |
7f0cb92e | 616 | -- len bit0..7 | len=12bit=low nibble of byte1..byte0 |
617 | raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8)) | |
618 | -- high nibble of len bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh? | |
619 | raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),4.4)..bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8)) | |
620 | -- WRP | |
621 | raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8)) | |
622 | -- WRC + RD | |
623 | raw[4]=("%02x"):format(bbit("0x"..("%03x"):format(seg.WRC),4,3)..bbit("0x"..("%02x"):format(seg.RD*128),0,8)) | |
624 | -- flag | |
625 | seg.flag=string.sub(raw[2],0,1) | |
626 | --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4]) | |
627 | if(#seg.data>(seg.len-5)) then | |
628 | print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5) | |
629 | print("Data-Length has being reduced: removing ".. #seg.data-(seg.len-5) .." bytes from Payload"); | |
630 | for i=(seg.len-5), #seg.data-1 do | |
631 | table.remove(seg.data) | |
733eb420 | 632 | end |
7f0cb92e | 633 | elseif (#seg.data<(seg.len-5)) then |
634 | print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5) | |
635 | print("Data-Length has being extended: adding "..(seg.len-5)-#seg.data.." bytes to Payload"); | |
636 | for i=#seg.data, (seg.len-5)-1 do | |
637 | table.insert(seg.data, '00') | |
733eb420 | 638 | end |
7f0cb92e | 639 | end |
640 | return seg | |
733eb420 | 641 | end |
642 | ||
643 | --- | |
7f0cb92e | 644 | -- determine TagType (bits 0..6 of DCFlow) |
645 | function getTokenType(DCFl) | |
646 | --[[ | |
647 | 0x00–0x2f IAM | |
648 | 0x30–0x6f SAM | |
649 | 0x70–0x7f GAM | |
650 | ]]-- | |
651 | local tt = tonumber(bbit("0x"..DCFl,0,7),10) | |
652 | if (tt >= 0 and tt <= 47) then tt = "IAM" | |
653 | elseif (tt == 49) then tt = "SAM63" | |
654 | elseif (tt == 48) then tt = "SAM64" | |
655 | elseif (tt >= 50 and tt <= 111) then tt = "SAM" | |
656 | elseif (tt >= 112 and tt <= 127) then tt = "GAM" | |
657 | else tt = "???" end | |
658 | return tt | |
659 | end | |
733eb420 | 660 | |
661 | --- | |
7f0cb92e | 662 | -- dump tag-system area |
663 | function dumpCDF(tag) | |
664 | local res="" | |
665 | local i=0 | |
666 | local raw="" | |
733eb420 | 667 | local bytes |
7f0cb92e | 668 | if (istable(tag)) then |
669 | res = res.."MCD: "..tag.MCD..", MSN: "..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..", MCC: "..tag.MCC.."\n" | |
670 | res = res.."DCF: "..tag.DCFl.." "..tag.DCFh..", Token_Type="..tag.Type.." (OLE="..tag.OLE.."), Stamp_len="..tag.Stamp_len.."\n" | |
671 | res = res.."WRP="..tag.WRP..", WRC="..tag.WRC..", RD="..tag.RD..", raw="..tag.raw..((tag.raw=='9f') and (", SSC="..tag.SSC.."\n") or "\n") | |
672 | ||
673 | -- credential (end-user tag) | |
674 | if (tag.Type=="SAM") then | |
675 | res = res.."Remaining Header Area\n" | |
676 | for i=0, (#tag.data) do | |
677 | res = res..tag.data[i].." " | |
733eb420 | 678 | end |
7f0cb92e | 679 | res = res.."\nBackup Area\n" |
680 | for i=0, (#tag.Bck) do | |
681 | res = res..tag.Bck[i].." " | |
682 | end | |
683 | res = res.."\nTime Area\n" | |
684 | for i=0, (#tag.MTC) do | |
685 | res = res..tag.MTC[i].." " | |
686 | end | |
687 | ||
688 | -- Master Token specific | |
733eb420 | 689 | else |
7f0cb92e | 690 | res = res .."Master-Token Area\nStamp: " |
691 | res= res..tag.SSC.." " | |
692 | for i=0, tag.Stamp_len-2 do | |
693 | res = res..tag.data[i].." " | |
733eb420 | 694 | end |
7f0cb92e | 695 | res=res.."\nunused payload\n" |
696 | for i=0, (#tag.data-tag.Stamp_len-1) do | |
697 | res = res..tag.data[i].." " | |
733eb420 | 698 | end |
7f0cb92e | 699 | bytes=tagToBytes(tag) |
700 | local mtcrc=calcMtCrc(bytes) | |
701 | res=res.."\nMaster-Token CRC: " | |
702 | res = res ..tag.MTC[1].." ("..((tag.MTC[1]==mtcrc) and "valid" or "error")..")" | |
703 | end | |
704 | return res | |
705 | else print("no valid Tag in dumpCDF") end | |
706 | end | |
707 | ||
708 | --- | |
709 | -- dump single segment | |
710 | function dumpSegment(tag, index) | |
711 | local i=index | |
712 | local i2 | |
713 | local dp=0 --data-position in table | |
714 | local res="" --result | |
715 | local raw="" --raw-header | |
716 | -- segment | |
717 | if ( (istable(tag.SEG[i])) and tag.Type=="SAM") then | |
718 | if (istable(tag.SEG[i].raw)) then | |
719 | for k,v in pairs(tag.SEG[i].raw) do | |
720 | raw=raw..v.." " | |
721 | end | |
722 | end | |
723 | ||
724 | -- segment header | |
725 | res = res.."Segment "..("%02d"):format(tag.SEG[i].index)..": " | |
726 | res = res .."raw header:"..string.sub(raw,0,-2)..", flag="..tag.SEG[i].flag..", (valid="..("%x"):format(tag.SEG[i].valid)..", last="..("%x"):format(tag.SEG[i].last).."), " | |
727 | res = res .."len="..("%04d"):format(tag.SEG[i].len)..", WRP="..("%02x"):format(tag.SEG[i].WRP)..", WRC="..("%02x"):format(tag.SEG[i].WRC)..", " | |
728 | res = res .."RD="..("%02x"):format(tag.SEG[i].RD)..", CRC="..tag.SEG[i].crc.." " | |
729 | res = res .."("..(checkSegmentCrc(tag, i) and "valid" or "error")..")" | |
730 | raw="" | |
731 | ||
732 | -- WRC protected | |
733 | if (tag.SEG[i].WRC>0) then | |
734 | res = res .."\nWRC protected area (Stamp):\n" | |
735 | for i2=dp, tag.SEG[i].WRC-1 do | |
736 | res = res..tag.SEG[i].data[dp].." " | |
737 | dp=dp+1 | |
738 | end | |
739 | end | |
740 | ||
741 | -- WRP mprotected | |
742 | if (tag.SEG[i].WRP>tag.SEG[i].WRC) then | |
743 | res = res .."\nRemaining write protected area (Stamp):\n" | |
744 | for i2=dp, tag.SEG[i].WRP-tag.SEG[i].WRC-1 do | |
745 | res = res..tag.SEG[i].data[dp].." " | |
746 | dp=dp+1 | |
747 | end | |
748 | end | |
749 | ||
750 | -- payload | |
751 | if (#tag.SEG[i].data-dp>0) then | |
752 | res = res .."\nRemaining segment payload:\n" | |
753 | for i2=dp, #tag.SEG[i].data-2 do | |
754 | res = res..tag.SEG[i].data[dp].." " | |
755 | dp=dp+1 | |
756 | end | |
757 | if (tag.SEG[i].kgh) then | |
758 | res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and "valid" or "error")..")" | |
759 | else res = res..tag.SEG[i].data[dp] end | |
760 | end | |
761 | dp=0 | |
762 | return res | |
763 | else | |
764 | return print("Segment not found") | |
733eb420 | 765 | end |
766 | end | |
767 | ||
768 | --- | |
769 | -- edit segment helper | |
770 | function editSegment(tag, index) | |
771 | local k,v | |
772 | local edit_possible="valid len RD WRP WRC Stamp Payload" | |
773 | if (istable(tag.SEG[index])) then | |
774 | for k,v in pairs(tag.SEG[index]) do | |
775 | if(string.find(edit_possible,k)) then | |
776 | tag.SEG[index][k]=tonumber(input(k..": ", v),10) | |
777 | end | |
778 | end | |
779 | else print("Segment with Index: "..("%02d"):format(index).." not found in Tag") | |
780 | return false | |
781 | end | |
782 | regenSegmentHeader(tag.SEG[index]) | |
783 | print("\n"..dumpSegment(tag, index).."\n") | |
784 | return tag | |
785 | end | |
786 | ||
787 | --- | |
788 | -- edit Segment Data | |
789 | function editSegmentData(data) | |
790 | if (istable(data)) then | |
791 | for i=0, #data-1 do | |
792 | data[i]=input("Data"..i..": ", data[i]) | |
793 | end | |
794 | return data | |
795 | else | |
796 | print("no Segment-Data found") | |
797 | end | |
798 | end | |
799 | ||
800 | --- | |
801 | -- list available segmets in virtual tag | |
802 | function segmentList(tag) | |
803 | local i | |
804 | local res = "" | |
805 | if (istable(tag.SEG[0])) then | |
806 | for i=0, #tag.SEG do | |
807 | res = res .. tag.SEG[i].index .. " " | |
808 | end | |
809 | return res | |
810 | else print("no Segments found in Tag") | |
811 | return false | |
812 | end | |
813 | end | |
814 | ||
815 | --- | |
816 | -- helper to selecting a segment | |
817 | function selectSegment(tag) | |
818 | local sel | |
7f0cb92e | 819 | if (istable(tag.SEG[0])) then |
733eb420 | 820 | print("availabe Segments:\n"..segmentList(tag)) |
821 | sel=input("select Segment: ", '00') | |
822 | sel=tonumber(sel,10) | |
823 | if (sel) then return sel | |
824 | else return '0' end | |
825 | else | |
826 | print("\nno Segments found") | |
827 | return false | |
828 | end | |
829 | end | |
830 | ||
831 | --- | |
832 | -- add segment | |
833 | function addSegment(tag) | |
834 | local i | |
835 | local segment={ | |
836 | ['index'] = '00', | |
837 | ['flag'] = 'c', | |
838 | ['valid'] = 1, | |
839 | ['last'] = 1, | |
840 | ['len'] = 13, | |
841 | ['raw'] = {'0d', '40', '04', '00'}, | |
842 | ['WRP'] = 4, | |
843 | ['WRC'] = 0, | |
844 | ['RD'] = 0, | |
845 | ['crc'] = '00', | |
846 | ['data'] = {}, | |
847 | ['kgh'] = false | |
848 | } | |
849 | if (istable(tag.SEG[0])) then | |
850 | tag.SEG[#tag.SEG].last=0 | |
851 | table.insert(tag.SEG, segment) | |
852 | for i=0, 8 do | |
853 | tag.SEG[#tag.SEG].data[i]=("%02x"):format(i) | |
854 | end | |
855 | tag.SEG[#tag.SEG].index=("%02d"):format(#tag.SEG) | |
856 | return tag | |
857 | else | |
858 | print("no Segment-Table found") | |
859 | end | |
860 | end | |
861 | ||
862 | --- | |
7f0cb92e | 863 | -- delete segment (except segment 00) |
733eb420 | 864 | function delSegment(tag, index) |
7f0cb92e | 865 | if (istable(tag.SEG[0])) then |
733eb420 | 866 | local i |
867 | if (type(index)=="string") then index=tonumber(index,10) end | |
868 | if (index > 0) then | |
869 | table.remove(tag.SEG, index) | |
870 | for i=0, #tag.SEG do | |
871 | tag.SEG[i].index=("%02d"):format(i) | |
872 | end | |
873 | end | |
874 | if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end | |
875 | return tag | |
876 | end | |
7f0cb92e | 877 | end |
878 | ||
879 | --- | |
880 | -- calculate Master-Token crc | |
881 | function calcMtCrc(bytes) | |
882 | --print(#bytes) | |
883 | local cmd=bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[7]..bytes[6]..bytes[8] | |
884 | local len=(tonumber(0xfc ,10)-("%d"):format('0x'..bytes[7])) | |
885 | for i=1, len do | |
886 | cmd=cmd..bytes[8+i] | |
887 | end | |
888 | local res=("%02x"):format(utils.Crc8Legic(cmd)) | |
889 | return res | |
890 | end | |
891 | ||
892 | --- | |
893 | -- check all segmnet-crc | |
894 | function checkAllSegCrc(tag) | |
895 | if (istable(tag.SEG[0])) then | |
896 | for i=0, #tag.SEG do | |
897 | crc=calcSegmentCrc(tag, i) | |
898 | tag.SEG[i].crc=crc | |
899 | end | |
900 | else return print("Matser-Token / unsegmented Tag") end | |
901 | end | |
902 | ||
903 | --- | |
904 | -- check all segmnet-crc | |
905 | function checkAllKghCrc(tag) | |
906 | if (istable(tag.SEG[0])) then | |
907 | for i=0, #tag.SEG do | |
908 | crc=calcKghCrc(tag, i) | |
909 | if (tag.SEG[i].kgh) then | |
910 | tag.SEG[i].data[#tag.SEG[i].data-1]=crc | |
911 | end | |
912 | end | |
913 | end | |
914 | end | |
915 | ||
916 | --- | |
917 | -- build kghCrc credentials | |
918 | function kghCrcCredentials(tag, segid) | |
919 | if (istable(tag) and istable(tag.SEG[0])) then | |
920 | local x='00' | |
921 | if (type(segid)=="string") then segid=tonumber(segid,10) end | |
922 | if (segid>0) then x='93' end | |
923 | local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2..("%02x"):format(tag.SEG[segid].WRP) | |
924 | cred = cred..("%02x"):format(tag.SEG[segid].WRC)..("%02x"):format(tag.SEG[segid].RD)..x | |
925 | for i=0, #tag.SEG[segid].data-2 do | |
926 | cred = cred..tag.SEG[segid].data[i] | |
927 | end | |
928 | return cred | |
929 | end | |
930 | end | |
931 | ||
932 | --- | |
933 | -- validate kghCRC to segment in tag-table | |
934 | function checkKghCrc(tag, segid) | |
935 | if (type(tag.SEG[segid])=='table') then | |
936 | if (tag.data[3]=="11" and tag.raw=="9f" and tag.SSC=="ff") then | |
937 | local data=kghCrcCredentials(tag, segid) | |
938 | if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].data[tag.SEG[segid].len-5-1]) then return true; end | |
939 | else return false; end | |
940 | else oops("'Kaba Group header' detected but no Segment-Data found") end | |
941 | end | |
942 | ||
943 | --- | |
944 | -- calcuate kghCRC for a given segment | |
945 | function calcKghCrc(tag, segid) | |
946 | if (istable(tag.SEG[0])) then | |
947 | -- check if a 'Kaber Group Header' exists | |
948 | local i | |
949 | local data=kghCrcCredentials(tag, segid) | |
950 | return ("%02x"):format(utils.Crc8Legic(data)) | |
951 | end | |
952 | end | |
953 | ||
954 | --- | |
955 | -- build segmentCrc credentials | |
956 | function segmentCrcCredentials(tag, segid) | |
957 | if (istable(tag.SEG[0])) then | |
958 | local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2 | |
959 | cred = cred ..tag.SEG[segid].raw[1]..tag.SEG[segid].raw[2]..tag.SEG[segid].raw[3]..tag.SEG[segid].raw[4] | |
960 | return cred | |
961 | else return print("Master-Token / unsegmented Tag!") end | |
962 | end | |
963 | ||
964 | --- | |
965 | -- validate segmentCRC for a given segment | |
966 | function checkSegmentCrc(tag, segid) | |
967 | local data=segmentCrcCredentials(tag, segid) | |
968 | if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].crc) then | |
969 | return true | |
970 | end | |
971 | return false | |
972 | end | |
973 | ||
974 | --- | |
975 | -- calculate segmentCRC for a given segment | |
976 | function calcSegmentCrc(tag, segid) | |
977 | if (istable(tag.SEG[0])) then | |
978 | -- check if a 'Kaber Group Header' exists | |
979 | local data=segmentCrcCredentials(tag, segid) | |
980 | return ("%02x"):format(utils.Crc8Legic(data)) | |
981 | end | |
982 | end | |
733eb420 | 983 | |
984 | --- | |
985 | -- helptext for modify-mode | |
986 | function modifyHelp() | |
987 | local t=[[ | |
988 | ||
989 | Data I/O Segment Manipulation File I/O | |
7f0cb92e | 990 | ------------------ -------------------- ------------------ |
733eb420 | 991 | rt => read Tag ds => dump Segments lf => load File |
992 | wt => write Tag as => add Segment sf => save File | |
7f0cb92e | 993 | es => edit Segment xf => xor to File |
994 | ct => copy io Tag ed => edit Data | |
995 | tc => copy oi Tag rs => remove Segment | |
996 | cc => check Segment-CRC | |
997 | di => dump inTag ck => check KGH | |
998 | do => dump outTag tk => toggle KGH-Flag | |
999 | mt => make Token | |
1000 | q => quit et => edit Token h => this Help | |
733eb420 | 1001 | ]] |
1002 | return t | |
1003 | end | |
1004 | ||
1005 | --- | |
1006 | -- modify Tag (interactive) | |
1007 | function modifyMode() | |
1008 | local i, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes | |
1009 | actions = { | |
1010 | ["h"] = function(x) | |
1011 | print(modifyHelp().."\n".."tags im Memory:"..(istable(inTAG) and " inTAG" or "")..(istable(outTAG) and " outTAG" or "")) | |
1012 | end, | |
7f0cb92e | 1013 | ["rt"] = function(x) inTAG=readFromPM3(); actions.di() end, |
733eb420 | 1014 | ["wt"] = function(x) |
7f0cb92e | 1015 | if(istable(inTAG.SEG)) then |
733eb420 | 1016 | local taglen=22 |
7f0cb92e | 1017 | if (istable(inTAG.Bck)) then |
733eb420 | 1018 | for i=0, #inTAG.SEG do |
1019 | taglen=taglen+inTAG.SEG[i].len+5 | |
1020 | end | |
7f0cb92e | 1021 | end |
733eb420 | 1022 | -- read new tag (output tag) |
1023 | outTAG=readFromPM3() | |
1024 | outbytes=tagToBytes(outTAG) | |
1025 | -- copy 'inputbuffer' to 'outputbuffer' | |
1026 | inTAG.MCD = outbytes[1] | |
1027 | inTAG.MSN0 = outbytes[2] | |
1028 | inTAG.MSN1 = outbytes[3] | |
1029 | inTAG.MSN2 = outbytes[4] | |
1030 | inTAG.MCC = outbytes[5] | |
7f0cb92e | 1031 | -- recheck all segments-crc/kghcrc (only on a credential) |
1032 | if(istable(inTAG.Bck)) then | |
733eb420 | 1033 | checkAllSegCrc(inTAG) |
1034 | checkAllKghCrc(inTAG) | |
7f0cb92e | 1035 | end |
733eb420 | 1036 | --get bytes from ready outTAG |
1037 | bytes=tagToBytes(inTAG) | |
7f0cb92e | 1038 | -- mater-token-crc |
1039 | if (inTAG.Type~="SAM") then bytes[22]=calcMtCrc(bytes) end | |
733eb420 | 1040 | if (bytes) then |
1041 | writeFile(bytes, 'MylegicClone.hex') | |
1042 | writeToTag(bytes, taglen, 'MylegicClone.hex') | |
7f0cb92e | 1043 | actions.rt('') |
733eb420 | 1044 | end |
1045 | end | |
1046 | end, | |
1047 | ["ct"] = function(x) | |
1048 | print("copy virtual input-TAG to output-TAG") | |
1049 | outTAG=inTAG | |
1050 | end, | |
1051 | ["tc"] = function(x) | |
1052 | print("copy virtual output-TAG to input-TAG") | |
1053 | inTAG=outTAG | |
1054 | end, | |
1055 | ["lf"] = function(x) | |
7f0cb92e | 1056 | if (file_check(x)) then filename=x |
1057 | else filename=input("enter filename: ", "legic.temp") end | |
733eb420 | 1058 | inTAG=readFile(filename) |
1059 | end, | |
1060 | ["sf"] = function(x) | |
1061 | if(istable(inTAG)) then | |
1062 | outfile=input("enter filename:", "legic.temp") | |
1063 | bytes=tagToBytes(inTAG) | |
1064 | --bytes=xorBytes(bytes, inTAG.MCC) | |
1065 | if (bytes) then | |
1066 | writeFile(bytes, outfile) | |
1067 | end | |
1068 | end | |
1069 | end, | |
1070 | ["xf"] = function(x) | |
1071 | if(istable(inTAG)) then | |
1072 | outfile=input("enter filename:", "legic.temp") | |
1073 | crc=input("enter new crc: ('00' for a plain dump)", inTAG.MCC) | |
1074 | print("obfuscate witth: "..crc) | |
1075 | bytes=tagToBytes(inTAG) | |
1076 | bytes[5]=crc | |
1077 | if (bytes) then | |
1078 | writeFile(bytes, outfile) | |
1079 | end | |
1080 | end | |
1081 | end, | |
1082 | ["di"] = function(x) if (istable(inTAG)) then print("\n"..dumpTag(inTAG).."\n") end end, | |
1083 | ["do"] = function(x) if (istable(outTAG)) then print("\n"..dumpTag(outTAG).."\n") end end, | |
1084 | ["ds"] = function(x) | |
7f0cb92e | 1085 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) |
1086 | else sel=selectSegment(inTAG) end | |
733eb420 | 1087 | if (sel) then print("\n"..(dumpSegment(inTAG, sel) or "no Segments available").."\n") end |
1088 | end, | |
1089 | ["es"] = function(x) | |
7f0cb92e | 1090 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) |
1091 | else sel=selectSegment(inTAG) end | |
733eb420 | 1092 | if (sel) then |
7f0cb92e | 1093 | if(istable(inTAG.SEG[0])) then |
733eb420 | 1094 | inTAG=editSegment(inTAG, sel) |
1095 | inTAG.SEG[sel]=regenSegmentHeader(inTAG.SEG[sel]) | |
1096 | else print("no Segments in Tag") end | |
1097 | end | |
1098 | end, | |
1099 | ["as"] = function(x) | |
1100 | if (istable(inTAG.SEG[0])) then | |
1101 | inTAG=addSegment(inTAG) | |
1102 | inTAG.SEG[#inTAG.SEG-1]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG-1]) | |
1103 | inTAG.SEG[#inTAG.SEG]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG]) | |
7f0cb92e | 1104 | else print("Master-Token / unsegmented Tag!") |
733eb420 | 1105 | end |
1106 | end, | |
1107 | ["rs"] = function(x) | |
7f0cb92e | 1108 | if (istable(inTAG.SEG[0])) then |
1109 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) | |
1110 | else sel=selectSegment(inTAG) end | |
733eb420 | 1111 | inTAG=delSegment(inTAG, sel) |
1112 | for i=0, #inTAG.SEG do | |
1113 | inTAG.SEG[i]=regenSegmentHeader(inTAG.SEG[i]) | |
1114 | end | |
1115 | end | |
1116 | end, | |
1117 | ["ed"] = function(x) | |
7f0cb92e | 1118 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) |
1119 | else sel=selectSegment(inTAG) end | |
733eb420 | 1120 | if (sel) then |
1121 | inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data) | |
1122 | end | |
1123 | end, | |
7f0cb92e | 1124 | ["et"] = function(x) |
1125 | if (istable(inTAG)) then | |
1126 | editTag(inTAG) | |
1127 | end | |
1128 | end, | |
1129 | ["mt"] = function(x) inTAG=makeToken(); actions.di() end, | |
733eb420 | 1130 | ["ts"] = function(x) |
7f0cb92e | 1131 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) |
1132 | else sel=selectSegment(inTAG) end | |
733eb420 | 1133 | regenSegmentHeader(inTAG.SEG[sel]) |
1134 | end, | |
1135 | ["tk"] = function(x) | |
7f0cb92e | 1136 | if (istable(inTAG) and istable(inTAG.SEG[0])) then |
1137 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) | |
1138 | else sel=selectSegment(inTAG) end | |
733eb420 | 1139 | if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false |
1140 | else inTAG.SEG[sel].kgh=true end | |
7f0cb92e | 1141 | end |
733eb420 | 1142 | end, |
1143 | ["k"] = function(x) | |
7f0cb92e | 1144 | if (type(x)=="string" and string.len(x)>0) then |
733eb420 | 1145 | print(("%02x"):format(utils.Crc8Legic(x))) |
7f0cb92e | 1146 | end |
733eb420 | 1147 | end, |
1148 | ["xc"] = function(x) | |
7f0cb92e | 1149 | if (istable(inTAG) and istable(inTAG.SEG[0])) then |
1150 | if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) | |
1151 | else sel=selectSegment(inTAG) end | |
1152 | print("k "..kghCrcCredentials(inTAG, sel)) | |
1153 | end | |
733eb420 | 1154 | end, |
1155 | ["cc"] = function(x) if (istable(inTAG)) then checkAllSegCrc(inTAG) end end, | |
1156 | ["ck"] = function(x) if (istable(inTAG)) then checkAllKghCrc(inTAG) end end, | |
733eb420 | 1157 | } |
1158 | print("modify-modus! enter 'h' for help or 'q' to quit") | |
1159 | repeat | |
1160 | ic=input("Legic command? ('h' for help - 'q' for quit)", "h") | |
1161 | -- command actions | |
1162 | if (type(actions[string.lower(string.sub(ic,0,1))])=='function') then | |
1163 | actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3)) | |
1164 | elseif (type(actions[string.lower(string.sub(ic,0,2))])=='function') then | |
1165 | actions[string.lower(string.sub(ic,0,2))](string.sub(ic,4)) | |
7f0cb92e | 1166 | else actions.h('') end |
733eb420 | 1167 | until (string.sub(ic,0,1)=="q") |
1168 | end | |
1169 | ||
7f0cb92e | 1170 | --- main function |
733eb420 | 1171 | function main(args) |
1172 | if (#args == 0 ) then modifyMode() end | |
1173 | --- variables | |
1174 | local inTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs | |
1175 | -- just a spacer for better readability | |
1176 | print() | |
1177 | --- parse arguments | |
1178 | for o, a in getopt.getopt(args, 'hrmi:do:c:') do | |
1179 | -- display help | |
1180 | if o == "h" then return help(); end | |
1181 | -- read tag from PM3 | |
1182 | if o == "r" then inTAG=readFromPM3() end | |
1183 | -- input file | |
1184 | if o == "i" then inTAG=readFile(a) end | |
1185 | -- dump virtual-Tag | |
1186 | if o == "d" then dfs=true end | |
1187 | -- interacive modifying | |
1188 | if o == "m" then interactive=true; modifyMode() end | |
1189 | -- xor (e.g. for clone or plain file) | |
1190 | if o == "c" then cfs=true; crc=a end | |
1191 | -- output file | |
1192 | if o == "o" then outfile=a; ofs=true end | |
1193 | end | |
1194 | ||
1195 | -- file conversion (output to file) | |
1196 | if (ofs) then | |
1197 | -- dump infile / tag-read | |
1198 | if (dfs) then | |
1199 | print("-----------------------------------------") | |
1200 | print(dumpTag(inTAG)) | |
1201 | end | |
1202 | bytes=tagToBytes(inTAG) | |
733eb420 | 1203 | if (cfs) then |
7f0cb92e | 1204 | -- xor willl be done in function writeFile |
1205 | -- with the value of byte[5] | |
733eb420 | 1206 | bytes[5]=crc |
1207 | end | |
1208 | -- write to outfile | |
1209 | if (bytes) then | |
1210 | writeFile(bytes, outfile) | |
7f0cb92e | 1211 | --- read real tag into virtual tag |
1212 | -- inTAG=readFromPM3() end | |
1213 | --- or simply use the bytes that where wriiten | |
733eb420 | 1214 | inTAG=bytesToTag(bytes, inTAG) |
1215 | -- show new content | |
1216 | if (dfs) then | |
1217 | print("-----------------------------------------") | |
7f0cb92e | 1218 | print(dumpTag(inTAG)) |
733eb420 | 1219 | end |
1220 | end | |
1221 | end | |
1222 | ||
1223 | end | |
1224 | ||
1225 | --- start | |
1226 | main(args) |