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 +----+----+----+----+----+----+----+----+
16 example = "script run legic"
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:
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
34 q => quit xc => get KGH-Str h => this Help
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'
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
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)
68 local utils = require('utils')
69 local getopt = require('getopt')
71 --- global variables / defines
72 local bxor = bit32.bxor
73 local bbit = bit32.extract
74 local input = utils.input
75 local confirm = utils.confirm
77 --- Error-Handling & Helper
78 -- This is only meant to be used when errors occur
88 print("Example usage")
95 return type(t) == 'table'
99 -- put certain bytes into a new table
100 function bytesToTable(bytes, bstart, bend)
102 for i=0, (bend-bstart) do
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) ))
119 -- (de)obfuscate bytes
120 function xorBytes(inBytes, crc)
122 for index = 1, #inBytes do
123 bytes[index] = xorme(inBytes[index], crc, index)
125 if (#inBytes == #bytes) then
127 bytes[5] = string.sub(crc,-2)
130 print("error: byte-count missmatch")
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
147 -- read file into virtual-tag
148 function readFile(filename)
151 if (file_check(filename)==false) then
152 return oops("input file: "..filename.." not found")
154 bytes = getInputBytes(filename)
155 if (bytes == false) then return oops('couldnt get input bytes')
158 bytes = xorBytes(bytes,bytes[5])
159 -- create Tag for plain bytes
161 -- load plain bytes to tag-table
162 tag=bytesToTag(bytes, tag)
169 -- write bytes 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
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])
186 elseif (bcnt <= 7) then
187 line=line.." "..bytes[i]
190 -- write line to new file
191 fho:write(line.."\n")
192 -- reset counter & line
199 print("\nwrote ".. #bytes .." bytes to " .. filename)
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
215 core.console("hf legic reader")
216 core.console("hf legic save "..infile)
217 --print("read temp-file into virtual Tag ...")
220 --else return print("user abort"); end
224 -- read file into table
225 function getInputBytes(infile)
228 local fhi,err = io.open(infile)
229 if err then oops("faild to read from file ".. infile); return false; end
232 if line == nil then break end
233 for byte in line:gmatch("%w+") do
234 table.insert(bytes, byte)
238 print(#bytes .. " bytes from "..infile.." loaded")
243 -- read Tag-Table in bytes-table
244 function tagToBytes(tag)
245 if (istable(tag)) then
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)
259 for i=0, #tag.data do
260 table.insert(bytes, tag.data[i])
264 table.insert(bytes, tag.Bck[i])
266 -- token-create-time / master-token crc
268 table.insert(bytes, tag.MTC[i])
271 if (type(tag.SEG[0])=='table') then
273 for i2=1, #tag.SEG[i].raw+1 do
274 table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2])
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])
283 for i=#bytes+1, 1024 do
284 table.insert(bytes, i, '00')
286 print(#bytes.." bytes of Tag dumped")
289 return oops("tag is no table in tagToBytes ("..type(tag)..")")
292 --- virtual TAG functions
293 -- create tag-table helper
294 function createTagTable()
320 -- put bytes into tag-table
321 function bytesToTag(bytes, tag)
322 if(istable(tag)) then
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)
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")
347 print(#bytes.." bytes for Tag processed")
350 return oops("tag is no table in: bytesToTag ("..type(tag)..")")
354 -- dump tag-system area
355 function dumpCDF(tag)
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"
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].." "
370 res = res.."\nBackup Area\n"
371 for i=0, (#tag.Bck) do
372 res = res..tag.Bck[i].." "
374 res = res.."\nTime Area\n"
375 for i=0, (#tag.MTC) do
376 res = res..tag.MTC[i].." "
381 res = res .."Master-Token Area\n"
382 for i=0, (#tag.data) do
383 res = res..tag.data[i].." "
385 for i=0, (#tag.Bck) do
386 res = res..tag.Bck[i].." "
388 for i=0, (#tag.MTC-1) do
389 res = res..tag.MTC[i].." "
391 res = res .. " MT-CRC: "..tag.MTC[1]
394 else print("no valid Tag in dumpCDF") end
398 -- dump single segment
399 function dumpSegment(tag, index)
402 local dp=0 --data-position in table
403 local res="" --result
404 local raw="" --raw-header
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
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")..")"
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].." "
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].." "
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].." "
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
453 return print("Segment not found")
458 -- check all segmnet-crc
459 function checkAllSegCrc(tag)
461 crc=calcSegmentCrc(tag, i)
467 -- check all segmnet-crc
468 function checkAllKghCrc(tag)
470 crc=calcKghCrc(tag, i)
471 if (tag.SEG[i].kgh) then
472 tag.SEG[i].data[#tag.SEG[i].data-1]=crc
478 -- dump virtual Tag-Data
479 function dumpTag(tag)
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"
491 res=res.."\n"..dumpSegment(tag, i).."\n"
498 -- determine TagType (bits 0..6 of DCFlow)
499 function getTokenType(DCFl)
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"
516 -- regenerate segment-header (after edit)
517 function regenSegmentHeader(segment)
519 local raw = segment.raw
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))
526 raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
528 raw[4]=("%02x"):format(bbit("0x"..("%03x"):format(seg.WRC),4,3)..bbit("0x"..("%02x"):format(seg.RD*128),0,8))
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)
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')
549 -- get segmemnt-data from byte-table
550 function getSegmentData(bytes, start, index)
557 ['raw'] = {'00', '00', '00', '00'},
565 if (bytes[start]) then
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)
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]
597 -- put segments from byte-table to tag-table
598 function segmentsToTag(bytes, tag)
602 if (istable(tag)) then
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
609 start=start+tag.SEG[i].len
610 until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126)
612 else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
613 else print("no Segments: must be a MIM22") end
616 --- CRC calculation and validation
617 -- build kghCrc credentials
618 function kghCrcCredentials(tag, segid)
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]
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
642 -- calcuate kghCRC for a given segment
643 function calcKghCrc(tag, segid)
644 -- check if a 'Kaber Group Header' exists
646 local data=kghCrcCredentials(tag, segid)
647 return ("%02x"):format(utils.Crc8Legic(data))
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]
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
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))
676 --- create master-token
679 -- write virtual Tag to real Tag
680 -- write clone-data to tag
681 function writeToTag(plainBytes, taglen, filename)
683 if(utils.confirm("\nplace your empty tag onto the PM3 to read & write\n") == false) then
687 -- write data to file
689 WriteBytes = utils.input("enter number of bytes to write?", taglen)
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
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)
703 -- write DCF in reverse order (requires 'mosci-patch')
704 cmd = 'hf legic write 0x05 0x02'
708 print("skipping byte 0x05 - will be written next step")
716 -- edit segment helper
717 function editSegment(tag, index)
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)
726 else print("Segment with Index: "..("%02d"):format(index).." not found in Tag")
729 regenSegmentHeader(tag.SEG[index])
730 print("\n"..dumpSegment(tag, index).."\n")
736 function editSegmentData(data)
737 if (istable(data)) then
739 data[i]=input("Data"..i..": ", data[i])
743 print("no Segment-Data found")
748 -- list available segmets in virtual tag
749 function segmentList(tag)
752 if (istable(tag.SEG[0])) then
754 res = res .. tag.SEG[i].index .. " "
757 else print("no Segments found in Tag")
763 -- helper to selecting a segment
764 function selectSegment(tag)
766 if (istable(tag)) then
767 print("availabe Segments:\n"..segmentList(tag))
768 sel=input("select Segment: ", '00')
770 if (sel) then return sel
773 print("\nno Segments found")
780 function addSegment(tag)
788 ['raw'] = {'0d', '40', '04', '00'},
796 if (istable(tag.SEG[0])) then
797 tag.SEG[#tag.SEG].last=0
798 table.insert(tag.SEG, segment)
800 tag.SEG[#tag.SEG].data[i]=("%02x"):format(i)
802 tag.SEG[#tag.SEG].index=("%02d"):format(#tag.SEG)
805 print("no Segment-Table found")
811 function delSegment(tag, index)
813 if (type(index)=="string") then index=tonumber(index,10) end
815 table.remove(tag.SEG, index)
817 tag.SEG[i].index=("%02d"):format(i)
820 if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
825 -- helptext for modify-mode
826 function modifyHelp()
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
838 tk => toggle KGH-Flag
839 q => quit xc => get KGH-Str h => this Help
845 -- modify Tag (interactive)
846 function modifyMode()
847 local i, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes
850 print(modifyHelp().."\n".."tags im Memory:"..(istable(inTAG) and " inTAG" or "")..(istable(outTAG) and " outTAG" or ""))
852 ["rt"] = function(x) inTAG=readFromPM3(); actions['di']('') end,
854 if(istable(inTAG)) then
856 for i=0, #inTAG.SEG do
857 taglen=taglen+inTAG.SEG[i].len+5
859 -- read new tag (output tag)
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)
874 writeFile(bytes, 'MylegicClone.hex')
875 writeToTag(bytes, taglen, 'MylegicClone.hex')
881 print("copy virtual input-TAG to output-TAG")
885 print("copy virtual output-TAG to input-TAG")
889 filename=input("enter filename: ", "legic.temp")
890 inTAG=readFile(filename)
893 if(istable(inTAG)) then
894 outfile=input("enter filename:", "legic.temp")
895 bytes=tagToBytes(inTAG)
896 --bytes=xorBytes(bytes, inTAG.MCC)
898 writeFile(bytes, outfile)
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)
910 writeFile(bytes, outfile)
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,
917 sel=selectSegment(inTAG)
918 if (sel) then print("\n"..(dumpSegment(inTAG, sel) or "no Segments available").."\n") end
921 sel=selectSegment(inTAG)
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
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!")
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])
947 sel=selectSegment(inTAG)
949 inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data)
953 sel=selectSegment(inTAG)
954 regenSegmentHeader(inTAG.SEG[sel])
957 sel=selectSegment(inTAG)
958 if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false
959 else inTAG.SEG[sel].kgh=true end
962 print(("%02x"):format(utils.Crc8Legic(x)))
965 --get credential-string for kgh-crc on certain segment
966 --usage: xc <segment-index>
967 print("k "..kghCrcCredentials(inTAG, x))
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,
973 print("modify-modus! enter 'h' for help or 'q' to quit")
975 ic=input("Legic command? ('h' for help - 'q' for quit)", "h")
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")
987 if (#args == 0 ) then modifyMode() end
989 local inTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
990 -- just a spacer for better readability
993 for o, a in getopt.getopt(args, 'hrmi:do:c:') do
995 if o == "h" then return help(); end
997 if o == "r" then inTAG=readFromPM3() end
999 if o == "i" then inTAG=readFile(a) end
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
1007 if o == "o" then outfile=a; ofs=true end
1010 -- file conversion (output to file)
1012 -- dump infile / tag-read
1014 print("-----------------------------------------")
1015 print(dumpTag(inTAG))
1017 bytes=tagToBytes(inTAG)
1018 -- xor with given crc
1024 writeFile(bytes, outfile)
1025 -- reed new content into virtual tag
1027 inTAG=bytesToTag(bytes, inTAG)
1030 print("-----------------------------------------")
1031 print(dumpTag(outTAG))