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