]> git.zerfleddert.de Git - proxmark3-svn/commitdiff
ADD: Added three legic luascript from @iscom (Mosic) Great work!
authoriceman1001 <iceman@iuse.se>
Sun, 28 Feb 2016 13:50:49 +0000 (14:50 +0100)
committericeman1001 <iceman@iuse.se>
Sun, 28 Feb 2016 13:50:49 +0000 (14:50 +0100)
client/scripts/Legic_clone.lua [new file with mode: 0644]
client/scripts/legic.lua [new file with mode: 0644]
client/scripts/legic_buffer2card.lua [new file with mode: 0644]

diff --git a/client/scripts/Legic_clone.lua b/client/scripts/Legic_clone.lua
new file mode 100644 (file)
index 0000000..2c8a84f
--- /dev/null
@@ -0,0 +1,541 @@
+--[[
+       script to create a clone-dump with new crc
+  Author: mosci
+   my Fork: https://github.com/icsom/proxmark3.git
+       Upstream: https://github.com/Proxmark/proxmark3.git
+
+       1. read tag-dump, xor byte 22..end with byte 0x05 of the inputfile
+       2. write to outfile 
+       3. set byte 0x05 to newcrc
+       4. until byte 0x21 plain like in inputfile
+       5. from 0x22..end xored with newcrc
+       6. calculate new crc on each segment (needs to know the new MCD & MSN0..2)
+       
+       simplest usage:
+       read a valid legic tag with 'hf legic reader'
+       save the dump with 'hf legic save orig.hex'
+  place your 'empty' tag on the reader and run 'script run Legic_clone -i orig.hex -w'
+  you will see some output like:
+               read 1024 bytes from legic_dumps/j_0000.hex
+               
+               place your empty tag onto the PM3 to read and display the MCD & MSN0..2
+               the values will be shown below
+                confirm whnen ready [y/n] ?y
+               #db# setting up legic card
+               #db# MIM 256 card found, reading card ...
+               #db# Card read, use 'hf legic decode' or
+               #db# 'data hexsamples 8' to view results
+               0b ad c0 de  <- !! here you'll see the MCD & MSN of your empty tag, which has to be typed in manually as seen below !!
+               type in  MCD as 2-digit value - e.g.: 00 (default: 79 )
+                > 0b
+               type in MSN0 as 2-digit value - e.g.: 01 (default: 28 )
+                > ad
+               type in MSN1 as 2-digit value - e.g.: 02 (default: d1 )
+                > c0
+               type in MSN2 as 2-digit value - e.g.: 03 (default: 43 )
+                > de
+               MCD:0b, MSN:ad c0 de, MCC:79 <- this crc is calculated from the MCD & MSN and must match the one on yout empty tag
+               
+               wrote 1024 bytes to myLegicClone.hex
+               enter number of bytes to write? (default: 86 )
+               
+               loaded 1024 samples
+               #db# setting up legic card
+               #db# MIM 256 card found, writing 0x00 - 0x01 ...
+               #db# write successful
+               ...
+               #db# setting up legic card
+               #db# MIM 256 card found, writing 0x56 - 0x01 ...
+               #db# write successful
+               proxmark3>
+       
+       the default value (number of bytes to write) is calculated over all valid segments and should be ok - just hit enter, wait until write has finished
+       and your clone should be ready (except there has to be a additional KGH-CRC to be calculated - which credentials are unknown until yet)
+       
+       the '-w' switch will only work with my fork - it needs the binary legic_crc8 which is not part of the proxmark3-master-branch
+       also the ability to write DCF is not possible with the proxmark3-master-branch
+       but creating dumpfile-clone files will be possible (without valid segment-crc - this has to done manually with) 
+
+       
+       (example)       Legic-Prime Layout with 'Kaba Group Header'
+                       +----+----+----+----+----+----+----+----+
+  0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |                
+                       +----+----+----+----+----+----+----+----+
+       0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2|
+                       +----+----+----+----+----+----+----+----+
+       0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1|
+                       +----+----+----+----+----+----+----+----+
+       0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0|
+                       +----+----+----+----+----+----+----+----+
+       0x20|UID1|UID2|kghC|
+                       +----+----+----+
+                                       
+                               MCD=    ManufacturerID                                          (1 Byte)
+               MSN0..2=        ManufactureSerialNumber         (3 Byte)
+                               MCC=    CRC                                                                                             (1 Byte) calculated over MCD,MSN0..2
+                               DCF=    DecrementalField                                        (2 Byte) 'credential' (enduser-Tag) seems to have always DCF-low=0x60 DCF-high=0xea
+               Bck0..5=        Backup                                                                          (6 Byte) Bck0 'dirty-flag', Bck1..5 SegmentHeader-Backup 
+                               BCC=    BackupCRC                                                                       (1 Byte) CRC calculated over Bck1..5
+               Seg0..3=        SegmentHeader                                           (on MIM 4 Byte )
+                        SegC=  SegmentCRC                                                              (1 Byte) calculated over MCD,MSN0..2,Seg0..3
+               Stp0..n=        Stamp0...                                                                       (variable length) length = Segment-Len - UserData - 1
+               UID0..n=        UserDater                                                                       (variable length - with KGH hex 0x00-0x63 / dec 0-99) length = Segment-Len - WRP - WRC - 1 
+                        kghC=  KabaGroupHeader                                         (1 Byte + addr 0x0c must be 0x11)
+       as seen on ths example: addr 0x05..0x08 & 0x0c must have been set to this values - otherwise kghCRC will not be created by a official reader (not accepted)
+--]]
+
+example = "Script create a clone-dump of a dump from a Legic Prime Tag"
+author = "Mosci"
+desc =
+[[
+This is a script which create a clone-dump of a dump from a Legic Prime Tag (MIM256 or MIM1024)
+(created with 'hf legic save my_dump.hex') 
+requiered arguments:
+       -i <input file>         (file to read data from)
+       
+optional arguments :
+       -h                  - Help text
+       -o <output file>        - requieres option -c to be given
+       -c <new-tag crc>        - requieres option -o to be given
+       -d                                      - Display content of found Segments
+       -s                                      - Display summary at the end
+       -w                                      - write directly to Tag - a file myLegicClone.hex wille be generated also
+       
+       e.g.: 
+       hint: using the CRC '00' will result in a plain dump ( -c 00 )
+
+Examples : 
+       script run legic_clone -i my_dump.hex -o my_clone.hex -c f8
+       script run legic_clone -i my_dump.hex -d -s
+]]
+
+local utils = require('utils')
+local getopt = require('getopt')
+local bxor = bit32.bxor
+
+-- we need always 2 digits
+function prepend_zero(s) 
+       if (string.len(s)==1) then return "0" .. s
+       else 
+               if (string.len(s)==0) then return "00"
+               else return s
+               end
+       end
+end
+
+--- 
+-- This is only meant to be used when errors occur
+function oops(err)
+       print("ERROR: ",err)
+       return nil, err
+end
+
+--- 
+-- Usage help
+function help()
+       print(desc)
+       print("Example usage")
+       print(example)
+end
+
+-- Check availability of file
+function file_check(file_name)
+  local file_found=io.open(file_name, "r")      
+  if file_found==nil then
+    file_found=false
+  else
+    file_found=true
+  end
+  return file_found
+end
+
+--- xor-wrapper
+-- xor all from addr 0x22 (start counting from 1 => 23)
+function xorme(hex, xor, index)
+       if ( index >= 23 ) then
+               return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
+       else
+               return hex
+       end
+end
+
+-- read input-file into array
+function getInputBytes(infile)
+       local line
+       local bytes = {}
+
+       local fhi,err = io.open(infile)
+       if err then print("OOps ... faild to read from file ".. infile); return false; end
+
+       while true do
+               line = fhi:read()
+               if line == nil then break end
+
+               for byte in line:gmatch("%w+") do 
+                       table.insert(bytes, byte)
+               end
+       end
+       
+       fhi:close()
+
+       print("\nread ".. #bytes .." bytes from ".. infile)
+       return bytes
+end
+
+-- write to file
+function writeOutputBytes(bytes, outfile)
+       local line
+       local bcnt=0
+       local fho,err = io.open(outfile,"w")
+       if err then print("OOps ... faild to open output-file ".. outfile); return false; end
+
+       for i = 1, #bytes do
+               if (bcnt == 0) then 
+                       line=bytes[i]
+               elseif (bcnt <= 7) then 
+                       line=line.." "..bytes[i]
+               end
+               if (bcnt == 7) then
+                       -- write line to new file
+                       fho:write(line.."\n")
+                       -- reset counter & line
+                       bcnt=-1
+                       line=""
+               end
+               bcnt=bcnt+1
+       end
+       fho:close()
+       print("\nwrote ".. #bytes .." bytes to " .. outfile)
+       return true
+end
+
+-- xore certain bytes
+function xorBytes(inBytes, crc)
+       local bytes = {}
+       for index = 1, #inBytes do
+               bytes[index] = xorme(inBytes[index], crc, index)
+       end
+       if (#inBytes == #bytes) then
+               -- replace crc
+               bytes[5] = string.sub(crc,-2)
+               return bytes
+       else
+               print("error: byte-count missmatch")
+               return false
+       end
+end
+
+-- get raw segment-data
+function getSegmentData(bytes, start, index)
+       local raw, len, valid, last, wrp, wrc, rd, crc
+       local segment = {}
+       segment[0] = bytes[start].." "..bytes[start+1].." "..bytes[start+2].." "..bytes[start+3]
+       -- flag = high nibble of byte 1
+       segment[1] = string.sub(bytes[start+1],0,1)
+
+       -- valid = bit 6 of byte 1
+       segment[2] = tonumber(bit32.extract("0x"..bytes[start+1],6,1),16)
+
+       -- last = bit 7 of byte 1
+       segment[3] = tonumber(bit32.extract("0x"..bytes[start+1],7,1),16)
+
+       -- len = (byte 0)+(bit0-3 of byte 1)
+  segment[4] = tonumber(("%03x"):format(tonumber(bit32.extract("0x"..bytes[start+1],0,3),16)..tonumber(bytes[start],16)),16)
+  
+       -- wrp (write proteted) = byte 2
+       segment[5] = tonumber(bytes[start+2])
+
+       -- wrc (write control) - bit 4-6 of byte 3
+       segment[6] = tonumber(bit32.extract("0x"..bytes[start+3],4,3),16)
+
+       -- rd (read disabled) - bit 7 of byte 3
+       segment[7] = tonumber(bit32.extract("0x"..bytes[start+3],7,1),16)
+
+       -- crc byte 4
+       segment[8] = bytes[start+4]
+
+       -- segment index
+       segment[9] = index
+
+       -- # crc-byte
+       segment[10] = start+4
+  return segment
+end
+
+--- Kaba Group Header
+-- checks if a segment does have a kghCRC
+-- returns boolean false if no kgh has being detected or the kghCRC if a kgh was detected
+function CheckKgh(bytes, segStart, segEnd)
+  if (bytes[8]=='9f' and bytes[9]=='ff' and bytes[13]=='11') then
+    local i
+    local data = {}
+    segStart=tonumber(segStart,10)
+    segEnd=tonumber(segEnd,10)
+    local dataLen = segEnd-segStart-5
+    --- gather creadentials for verify
+    local WRP = bytes[(segStart+2)]
+    local WRC = ("%02x"):format(tonumber(bit32.extract("0x"..bytes[segStart+3],4,3),16))
+    local RD = ("%02x"):format(tonumber(bit32.extract("0x"..bytes[segStart+3],7,1),16))
+    local XX = "00"  
+    cmd = bytes[1]..bytes[2]..bytes[3]..bytes[4]..WRP..WRC..RD..XX
+    for i=(segStart+5), (segStart+5+dataLen-2) do
+      cmd = cmd..bytes[i]
+    end
+    local KGH=("%02x"):format(utils.Crc8Legic(cmd))
+    if (KGH==bytes[segEnd-1]) then
+      return KGH
+    else
+      return false
+    end
+  else
+    return false
+  end
+end
+
+-- get only the addresses of segemnt-crc's and the length of bytes
+function getSegmentCrcBytes(bytes)
+       local start=23
+       local index=0
+       local crcbytes = {}
+       repeat
+               seg = getSegmentData(bytes,start,index)
+               crcbytes[index]= seg[10]
+               start = start + seg[4]
+               index = index + 1
+       until (seg[3] == 1 or tonumber(seg[9]) == 126 ) 
+       crcbytes[index] = start
+       return crcbytes
+end
+
+-- print segment-data (hf legic decode like)
+function displaySegments(bytes)
+       --display segment header(s)
+       start=23
+       index="00"
+       
+       --repeat until last-flag ist set to 1 or segment-index has reached 126
+       repeat
+               wrc=""
+               wrp=""
+               pld=""
+               Seg = getSegmentData(bytes,start,index)
+    KGH = CheckKgh(bytes,start,(start+tonumber(Seg[4],10)))
+               printSegment(Seg)
+
+               -- wrc
+               if(Seg[6]>0) then
+                       print("WRC protected area:")
+                       -- length of wrc = wrc
+                       for i=1, Seg[6] do
+                               -- starts at (segment-start + segment-header + segment-crc)-1 
+                               wrc = wrc..bytes[(start+4+1+i)-1].." " 
+                       end
+                       print(wrc)
+               elseif(Seg[5]>0) then
+                       print("Remaining write protected area:")
+                       -- length of wrp = (wrp-wrc)
+                       for i=1, (Seg[5]-Seg[6]) do
+                               -- starts at (segment-start + segment-header + segment-crc + wrc)-1 
+                               wrp = wrp..bytes[(start+4+1+Seg[6]+i)-1].." " 
+                       end
+                       print(wrp)
+               end
+               
+               -- payload 
+               print("Remaining segment payload:")
+               --length of payload = segment-len - segment-header - segment-crc - wrp -wrc
+               for i=1, (Seg[4]-4-1-Seg[5]-Seg[6]) do
+                       -- starts at (segment-start + segment-header + segment-crc + segment-wrp + segemnt-wrc)-1
+                       pld = pld..bytes[(start+4+1+Seg[5]+Seg[6]+i)-1].." " 
+               end
+               print(pld)
+    if (KGH) then print("'Kaba Group Header' detected"); end
+               start = start+Seg[4]
+               index = prepend_zero(tonumber(Seg[9])+1)
+    
+       until (Seg[3] == 1 or tonumber(Seg[9]) == 126 )
+end
+
+-- print Segment values
+function printSegment(SegmentData)
+       res = "\nSegment "..SegmentData[9]..": "
+       res = res..     "raw header="..SegmentData[0]..", "
+       res = res..     "flag="..SegmentData[1].." (valid="..SegmentData[2].." last="..SegmentData[3].."), "
+       res = res..     "len="..("%04d"):format(SegmentData[4])..", "
+       res = res..     "WRP="..prepend_zero(SegmentData[5])..", "
+       res = res..     "WRC="..prepend_zero(SegmentData[6])..", "
+       res = res.. "RD="..SegmentData[7]..", "
+       res = res.. "crc="..SegmentData[8]
+       print(res)      
+end
+
+-- write clone-data to tag
+function writeToTag(plainBytes)
+       local SegCrcs = {}
+       if(utils.confirm("\nplace your empty tag onto the PM3 to read and display the MCD & MSN0..2\nthe values will be shown below\n confirm when ready") == false) then
+    return
+  end
+
+       -- gather MCD & MSN from new Tag - this must be enterd manually
+       cmd = 'hf legic read 0x00 0x04'
+       core.console(cmd)
+       print("\nthese are the MCD MSN0 MSN1 MSN2 from the Tag that has being read:")
+       cmd = 'data hexsamples 4'
+       core.console(cmd)
+  print("^^ use this values as input for the following answers (one 2-digit-value per question/answer):")
+       -- enter MCD & MSN (in hex)
+       MCD  = utils.input("type in  MCD as 2-digit value - e.g.: 00", plainBytes[1])
+       MSN0 = utils.input("type in MSN0 as 2-digit value - e.g.: 01", plainBytes[2])
+       MSN1 = utils.input("type in MSN1 as 2-digit value - e.g.: 02", plainBytes[3])
+       MSN2 = utils.input("type in MSN2 as 2-digit value - e.g.: 03", plainBytes[4])
+
+       -- calculate crc8 over MCD & MSN
+       cmd = MCD..MSN0..MSN1..MSN2
+       MCC = ("%02x"):format(utils.Crc8Legic(cmd))
+       print("MCD:"..MCD..", MSN:"..MSN0.." "..MSN1.." "..MSN2..", MCC:"..MCC)
+
+       -- calculate new Segment-CRC for each valid segment
+       SegCrcs = getSegmentCrcBytes(plainBytes)
+       for i=0, (#SegCrcs-1) do   
+    -- SegCrcs[i]-4 = address of first byte of segmentHeader (low byte segment-length)
+    segLen=tonumber(("%1x"):format(tonumber(bit32.extract("0x"..plainBytes[(SegCrcs[i]-3)],0,3),16))..("%02x"):format(tonumber(plainBytes[SegCrcs[i]-4],16)),16)
+    segStart=(SegCrcs[i]-4)
+    segEnd=(SegCrcs[i]-4+segLen)
+    KGH=CheckKgh(plainBytes,segStart,segEnd)
+    if (KGH) then 
+      print("'Kaba Group Header' detected - re-calculate...")
+    end
+               cmd = MCD..MSN0..MSN1..MSN2..plainBytes[SegCrcs[i]-4]..plainBytes[SegCrcs[i]-3]..plainBytes[SegCrcs[i]-2]..plainBytes[SegCrcs[i]-1]
+               plainBytes[SegCrcs[i]] = ("%02x"):format(utils.Crc8Legic(cmd))
+       end
+
+       -- apply MCD & MSN to plain data
+       plainBytes[1] = MCD
+       plainBytes[2] = MSN0
+       plainBytes[3] = MSN1
+       plainBytes[4] = MSN2
+       plainBytes[5] = MCC
+       
+       -- prepare plainBytes for writing (xor plain data with new MCC)
+       bytes = xorBytes(plainBytes, MCC)
+       
+       -- write data to file
+       if (writeOutputBytes(bytes, "myLegicClone.hex")) then
+               WriteBytes = utils.input("enter number of bytes to write?", SegCrcs[#SegCrcs])
+
+               -- load file into pm3-buffer
+               cmd = 'hf legic load myLegicClone.hex'
+               core.console(cmd)
+               
+               -- write pm3-buffer to Tag
+               for i=0, WriteBytes do
+                       if ( i<5 or i>6) then
+                               cmd = ('hf legic write 0x%02x 0x01'):format(i)
+                               core.console(cmd)
+                       elseif (i == 6) then
+                               -- write DCF in reverse order (requires 'mosci-patch')
+                               cmd = 'hf legic write 0x05 0x02'
+                               core.console(cmd)
+                       else
+                               print("skipping byte 0x05 - will be written next step")
+                       end                             
+                       utils.Sleep(0.2)
+               end
+       end
+end
+
+-- main function
+function main(args)
+       -- some variables
+       local i=0
+       local oldcrc, newcrc, infile, outfile
+       local bytes = {}
+       local segments = {}
+       
+       -- parse arguments for the script
+       for o, a in getopt.getopt(args, 'hwsdc:i::o:') do
+               -- output file
+               if o == "o" then 
+                       outfile = a
+                       ofs = true
+                       if (file_check(a)) then
+                               local answer = utils.confirm("\nthe output-file "..a.." alredy exists!\nthis will delete the previous content!\ncontinue?")
+                               if (answer==false) then return oops("quiting") end
+                       end
+               end             
+               -- input file
+               if o == "i" then
+                       infile = a 
+                       if (file_check(infile)==false) then
+                               return oops("input file: "..infile.." not found")
+                       else
+                               bytes = getInputBytes(infile)
+                               oldcrc = bytes[5]
+                               ifs = true
+                               if (bytes == false) then return oops('couldnt get input bytes') end
+                       end
+                       i = i+1
+               end
+               -- new crc
+               if o == "c" then 
+                       newcrc = a
+                       ncs = true
+               end
+               -- display segments switch
+               if o == "d" then ds = true; end
+               -- display summary switch
+               if o == "s" then ss = true; end
+               -- write to tag switch
+               if o == "w" then ws = true; end
+               -- help
+               if o == "h" then return help() end
+       end
+       
+       if (not ifs) then return oops("option -i <input file> is required but missing") end
+       
+       -- bytes to plain
+       bytes = xorBytes(bytes, oldcrc)
+       
+       -- show segments (works only on plain bytes)
+       if (ds) then
+               print("+------------------------------------------- Segments -------------------------------------------+") 
+               displaySegments(bytes); 
+       end
+
+       if (ofs and ncs) then
+               -- xor bytes with new crc
+               newBytes = xorBytes(bytes, newcrc)
+               -- write output
+               if (writeOutputBytes(newBytes, outfile)) then
+                       -- show summary if requested
+                       if (ss) then
+                               -- information
+                               res = "\n+-------------------------------------------- Summary -------------------------------------------+"
+                               res = res .."\ncreated clone_dump from\n\t"..infile.." crc: "..oldcrc.."\ndump_file:"
+                               res = res .."\n\t"..outfile.." crc: "..string.sub(newcrc,-2)
+                               res = res .."\nyou may load the new file with: hf legic load "..outfile
+                               res = res .."\n\nif you don't write to tag immediately ('-w' switch) you will need to recalculate each segmentCRC"
+                               res = res .."\nafter writing this dump to a tag!"
+                               res = res .."\n\na segmentCRC gets calculated over MCD,MSN0..3,Segment-Header0..3"
+                               res = res .."\ne.g. (based on Segment00 of the data from "..infile.."):"
+                               res = res .."\nhf legic crc8 "..bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[23]..bytes[24]..bytes[25]..bytes[26]
+                               -- this can not be calculated without knowing the new MCD, MSN0..2
+                               print(res)
+                       end
+               end
+       else
+               if (ss) then
+                       -- show why the output-file was not written
+                       print("\nnew file not written - some arguments are missing ..")
+                       print("output file: ".. (ofs and outfile or "not given"))
+                       print("new crc: ".. (ncs and newcrc or "not given"))
+               end
+       end
+       -- write to tag
+       if (ws and #bytes == 1024) then
+                       writeToTag(bytes)
+       end
+end
+
+-- call main with arguments
+main(args)
\ No newline at end of file
diff --git a/client/scripts/legic.lua b/client/scripts/legic.lua
new file mode 100644 (file)
index 0000000..dd119b0
--- /dev/null
@@ -0,0 +1,1039 @@
+--[[   
+(example)      Legic-Prime Layout with 'Kaba Group Header'
+      +----+----+----+----+----+----+----+----+
+  0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |                
+      +----+----+----+----+----+----+----+----+
+  0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2|
+      +----+----+----+----+----+----+----+----+
+  0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1|
+      +----+----+----+----+----+----+----+----+
+  0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0|
+      +----+----+----+----+----+----+----+----+
+  0x20|UID1|UID2|kghC|
+      +----+----+----+
+--]]
+
+example = "script run legic"
+author  = "Mosci"
+desc =
+[[
+
+This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024)
+it's kinda interactive with following commands in three categories:
+
+    Data I/O           Segment Manipulation        File I/O   
+------------------     --------------------      ---------------
+  rt => read    Tag     ds => dump   Segments     lf => load File
+  wt => write   Tag     as => add    Segment      sf => save File 
+  ct => copy io Tag     es => edit   Segment      xf => xor  File
+  tc => copy oi Tag     ed => edit   Data     
+  di => dump  inTag     rs => remove Segment
+  do => dump outTag     cc => check  Segment-CRC                  
+                        ck => check  KGH                
+                        tk => toggle KGH-Flag
+  q => quit             xc => get    KGH-Str      h => this Help 
+ Data I/O 
+ rt: 'read tag'         - reads a tag placed near to the PM3
+ wt: 'write tag'        - writes the content of the 'virtual inTag' to a tag placed near to th PM3
+                          without the need of changing anything - MCD,MSN,MCC will be read from the tag
+                          before and applied to the output.
+ ct: 'copy tag'         - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed
+ tc: 'copy tag'         - copy the 'second virtual Tag' to 'virtual TAG' - not usefull yet, but inernally needed
+ di: 'dump inTag'       - shows the current content of the 'virtual Tag'
+ do: 'dump outTag'      - shows the current content of the 'virtual outTag'
+
+ Segment Manipulation 
+ (all manipulations happens only in the 'virtual inTAG' - they need to be written with 'wt' to take effect)
+ ds: 'dump Segments'    - will show the content of a selected Segment
+ as: 'add Segment'      - will add a 'empty' Segment to the inTag
+ es: 'edit Segment'     - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid)
+                          all other Segment-Header-Values are either calculated or not needed to edit (yet)
+ ed: 'edit data'        - edit the Data of a Segment (Stamp & Payload)
+ rs: 'remove segment'   - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token)
+ cc: 'check Segment-CRC'- checks & calculates (if check failed) the Segment-CRC of all Segments
+ ck: 'check KGH-CRC'    - checks the and calculates a 'Kaba Group Header' if one was detected
+                          'Kaba Group Header CRC calculation'
+ tk: 'toggle KGH'       - toglle the (script-internal) flag for kgh-calculation for a segment
+ xc: 'etra c'           - show string that was used to calculate the kgh-crc of a segment
+  
+ Input/Output
+ lf: 'load file'        - load a (xored) file from the local Filesystem into the 'virtual inTag'
+ sf: 'save file'        - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC)
+ xf: 'xor file'         - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values)
+]]
+
+--- requirements
+local utils   = require('utils')
+local getopt  = require('getopt')
+
+--- global variables / defines
+local bxor    = bit32.bxor
+local bbit    = bit32.extract
+local input   = utils.input
+local confirm = utils.confirm
+
+--- Error-Handling & Helper
+-- This is only meant to be used when errors occur
+function oops(err)
+       print("ERROR: ",err)
+       return nil, err
+end
+
+---  
+-- Usage help
+function help()
+       print(desc)
+       print("Example usage")
+       print(example)
+end
+
+--- 
+-- table check helper
+function istable(t) 
+  return type(t) == 'table' 
+end
+
+---
+-- put certain bytes into a new table
+function bytesToTable(bytes, bstart, bend)
+  local t={}
+  for i=0, (bend-bstart) do
+    t[i]=bytes[bstart+i]
+  end
+  return t
+end
+
+--- 
+-- xor byte (if addr >= 0x22 - start counting from 1 => 23)
+function xorme(hex, xor, index)
+       if ( index >= 23 ) then
+               return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
+       else
+               return hex
+       end
+end
+
+--- 
+-- (de)obfuscate bytes
+function xorBytes(inBytes, crc)
+       local bytes = {}
+       for index = 1, #inBytes do
+               bytes[index] = xorme(inBytes[index], crc, index)
+       end
+       if (#inBytes == #bytes) then
+               -- replace crc
+               bytes[5] = string.sub(crc,-2)
+               return bytes
+       else
+               print("error: byte-count missmatch")
+               return false
+       end
+end
+
+--- 
+-- check availability of file
+function file_check(file_name)
+  local file_found=io.open(file_name, "r")      
+  if file_found==nil then
+    return false
+  else
+    return true
+  end
+end
+
+---
+-- read file into virtual-tag
+function readFile(filename)
+  local bytes = {}
+  local tag = {}
+       if (file_check(filename)==false) then
+               return oops("input file: "..filename.." not found")
+       else
+               bytes = getInputBytes(filename)
+               if (bytes == false) then return oops('couldnt get input bytes') 
+    else
+      -- make plain bytes
+      bytes = xorBytes(bytes,bytes[5])
+      -- create Tag for plain bytes
+      tag=createTagTable()
+      -- load plain bytes to tag-table
+      tag=bytesToTag(bytes, tag)
+    end
+       end
+  return tag
+end
+
+---
+-- write bytes to file
+-- write to file
+function writeFile(bytes, filename)
+  if (filename~='MylegicClone.hex') then
+    if (file_check(filename)) then
+           local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
+      if (answer==false) then return print("user abort") end
+    end
+  end
+  local line
+       local bcnt=0
+       local fho,err = io.open(filename, "w")
+       if err then oops("OOps ... faild to open output-file ".. filename) end
+  bytes=xorBytes(bytes, bytes[5])
+       for i = 1, #bytes do
+               if (bcnt == 0) then 
+                       line=bytes[i]
+               elseif (bcnt <= 7) then 
+                       line=line.." "..bytes[i]
+               end
+               if (bcnt == 7) then
+                       -- write line to new file
+                       fho:write(line.."\n")
+                       -- reset counter & line
+                       bcnt=-1
+                       line=""
+               end
+               bcnt=bcnt+1
+       end
+       fho:close()
+       print("\nwrote ".. #bytes .." bytes to " .. filename)
+       return true
+end
+
+--- 
+-- read from pm3 into virtual-tag
+function readFromPM3()
+  local tag, bytes, infile 
+  --if (confirm("is the Tag placed onto the Proxmak3 and ready for reading?")) then
+    --print("reading Tag ...")
+    --infile=input("input a name for the temp-file:", "legic.temp")
+               --if (file_check(infile)) then
+               --      local answer = confirm("\nthe output-file "..infile.." alredy exists!\nthis will delete the previous content!\ncontinue?")
+               --      if (answer==false) then return print("user abort") end
+               --end
+    infile="legic.temp"
+    core.console("hf legic reader")
+    core.console("hf legic save "..infile)
+    --print("read temp-file into virtual Tag ...")
+    tag=readFile(infile)
+    return tag
+  --else return print("user abort"); end
+end
+
+--- 
+-- read file into table
+function getInputBytes(infile)
+       local line
+       local bytes = {}
+       local fhi,err = io.open(infile)
+       if err then oops("faild to read from file ".. infile); return false; end
+       while true do
+               line = fhi:read()
+               if line == nil then break end
+               for byte in line:gmatch("%w+") do 
+                       table.insert(bytes, byte)
+               end
+       end
+       fhi:close()
+  print(#bytes .. " bytes from "..infile.." loaded")
+       return bytes
+end
+
+---
+-- read Tag-Table in bytes-table
+function tagToBytes(tag)
+  if (istable(tag)) then
+    local bytes = {}
+    local i, i2
+    -- main token-data
+    table.insert(bytes, tag.MCD)
+    table.insert(bytes, tag.MSN0)
+    table.insert(bytes, tag.MSN1)
+    table.insert(bytes, tag.MSN2)
+    table.insert(bytes, tag.MCC)
+    table.insert(bytes, tag.DCFl)
+    table.insert(bytes, tag.DCFh)
+    table.insert(bytes, tag.raw)
+    table.insert(bytes, tag.SSC)
+    -- raw token data
+    for i=0, #tag.data do
+      table.insert(bytes, tag.data[i])
+    end
+    -- backup data
+    for i=0, #tag.Bck do
+      table.insert(bytes, tag.Bck[i])
+    end
+    -- token-create-time / master-token crc
+    for i=0, #tag.MTC do
+      table.insert(bytes, tag.MTC[i])
+    end
+    -- process segments
+    if (type(tag.SEG[0])=='table') then
+      for i=0, #tag.SEG do
+        for i2=1, #tag.SEG[i].raw+1 do
+          table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2])
+        end
+        table.insert(bytes, #bytes+1, tag.SEG[i].crc)
+        for i2=0, #tag.SEG[i].data-1 do
+          table.insert(bytes, #bytes+1, tag.SEG[i].data[i2])
+        end
+      end
+    end
+    -- fill with zeros
+    for i=#bytes+1, 1024 do
+      table.insert(bytes, i, '00')
+    end
+    print(#bytes.." bytes of Tag dumped")
+    return bytes
+  end
+  return oops("tag is no table in tagToBytes ("..type(tag)..")")
+end
+
+--- virtual TAG functions
+-- create tag-table helper
+function createTagTable()
+  local t={  
+        ['MCD'] = '00',
+        ['MSN0']= '11',
+        ['MSN1']= '22',
+        ['MSN2']= '33',
+        ['MCC'] = 'cc',
+        ['DCFl']= 'ff',
+        ['DCFh']= 'ff',
+        ['Type']= 'GAM',
+        ['OLE'] = 0,
+        ['Stamp_len']= 18,
+        ['WRP'] = '00',
+        ['WRC'] = '00',
+        ['RD']  = '00',
+        ['raw'] = '9f',
+        ['SSC'] = 'ff',
+        ['data']= {},
+        ['bck'] = {},
+        ['MTC'] = {},
+        ['SEG'] = {}
+      }
+  return t
+end
+
+--- 
+-- put bytes into tag-table
+function bytesToTag(bytes, tag)
+  if(istable(tag)) then
+    tag.MCD =bytes[1];
+    tag.MSN0=bytes[2];
+    tag.MSN1=bytes[3];
+    tag.MSN2=bytes[4];
+    tag.MCC =bytes[5];
+    tag.DCFl=bytes[6];
+    tag.DCFh=bytes[7];
+    tag.raw =bytes[8];
+    tag.SSC =bytes[9];
+    tag.Type=getTokenType(tag.DCFl);
+    tag.OLE=bbit("0x"..tag.DCFl,7,1)
+    tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4))
+    tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3))
+    tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1))
+    tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10))
+    tag.data=bytesToTable(bytes, 10, 13)
+    tag.Bck=bytesToTable(bytes, 14, 20)
+    tag.MTC=bytesToTable(bytes, 21, 22)
+    
+    print("Tag-Type: ".. tag.Type)
+    if (tag.Type=="SAM" and #bytes>23) then
+      tag=segmentsToTag(bytes, tag)
+      print((#tag.SEG+1).." Segment(s) found")
+    end
+    print(#bytes.." bytes for Tag processed")
+    return tag
+  end
+  return oops("tag is no table in: bytesToTag ("..type(tag)..")")
+end
+
+---
+-- dump tag-system area
+function dumpCDF(tag)
+  local res=""
+  local i=0
+  local raw=""
+  if (istable(tag)) then
+    res = res.."MCD: "..tag.MCD..", MSN: "..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..", MCC: "..tag.MCC.."\n"
+    res = res.."DCF: "..tag.DCFl.." "..tag.DCFh..", Token_Type="..tag.Type.." (OLE="..tag.OLE.."), Stamp_len="..tag.Stamp_len.."\n"
+    res = res.."WRP="..tag.WRP..", WRC="..tag.WRC..", RD="..tag.RD..", raw="..tag.raw..", SSC="..tag.SSC.."\n"
+    
+    -- credential
+    if (tag.raw..tag.SSC=="9fff") then
+      res = res.."Remaining Header Area\n"
+      for i=0, (#tag.data) do
+        res = res..tag.data[i].." "
+      end
+      res = res.."\nBackup Area\n"
+      for i=0, (#tag.Bck) do
+        res = res..tag.Bck[i].." "
+      end
+      res = res.."\nTime Area\n"
+      for i=0, (#tag.MTC) do
+        res = res..tag.MTC[i].." "
+      end
+    
+    -- Master Token
+    else
+      res = res .."Master-Token Area\n"
+      for i=0, (#tag.data) do
+        res = res..tag.data[i].." "
+      end
+      for i=0, (#tag.Bck) do
+        res = res..tag.Bck[i].." "
+      end
+      for i=0, (#tag.MTC-1) do
+        res = res..tag.MTC[i].." "
+      end
+      res = res .. " MT-CRC: "..tag.MTC[1]
+    end
+    return res
+  else print("no valid Tag in dumpCDF") end
+end
+
+---
+-- dump single segment
+function dumpSegment(tag, index)
+  local i=index
+  local i2
+  local dp=0 --data-position in table
+  local res="" --result
+  local raw="" --raw-header
+  -- segment
+  if ( (istable(tag.SEG[i])) and tag.Type=="SAM") then 
+    if (istable(tag.SEG[i].raw)) then
+      for k,v in pairs(tag.SEG[i].raw) do
+        raw=raw..v.." "
+      end
+    end
+    
+    -- segment header
+    res = res.."Segment "..("%02d"):format(tag.SEG[i].index)..": "
+    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).."), "
+    res = res .."len="..("%04d"):format(tag.SEG[i].len)..", WRP="..("%02x"):format(tag.SEG[i].WRP)..", WRC="..("%02x"):format(tag.SEG[i].WRC)..", "
+    res = res .."RD="..("%02x"):format(tag.SEG[i].RD)..", CRC="..tag.SEG[i].crc.." "
+    res = res .."("..(checkSegmentCrc(tag, i) and "valid" or "error")..")"
+    raw=""
+
+    -- WRC protected
+    if (tag.SEG[i].WRC>0) then
+      res = res .."\nWRC protected area (Stamp):\n"
+      for i2=dp, tag.SEG[i].WRC-1 do
+        res = res..tag.SEG[i].data[dp].." "
+        dp=dp+1
+      end
+    end
+    
+    -- WRP mprotected
+    if (tag.SEG[i].WRP>tag.SEG[i].WRC) then
+      res = res .."\nRemaining write protected area (Stamp):\n"
+      for i2=dp, tag.SEG[i].WRP-tag.SEG[i].WRC-1 do
+        res = res..tag.SEG[i].data[dp].." "
+        dp=dp+1
+      end
+    end
+    
+    -- payload
+    if (#tag.SEG[i].data-dp>0) then
+     res = res .."\nRemaining segment payload:\n"
+     for i2=dp, #tag.SEG[i].data-2 do
+       res = res..tag.SEG[i].data[dp].." "
+       dp=dp+1
+     end
+     if (tag.SEG[i].kgh) then 
+       res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and "valid" or "error")..")"
+     else  res = res..tag.SEG[i].data[dp] end
+    end
+    dp=0
+    return res   
+  else
+    return print("Segment not found") 
+  end
+end
+
+---
+-- check all segmnet-crc
+function checkAllSegCrc(tag)
+  for i=0, #tag.SEG do
+    crc=calcSegmentCrc(tag, i)
+    tag.SEG[i].crc=crc
+  end
+end
+
+---
+-- check all segmnet-crc
+function checkAllKghCrc(tag)
+  for i=0, #tag.SEG do
+    crc=calcKghCrc(tag, i)
+    if (tag.SEG[i].kgh) then 
+      tag.SEG[i].data[#tag.SEG[i].data-1]=crc
+    end
+  end
+end
+
+---
+-- dump virtual Tag-Data
+function dumpTag(tag)
+  local i, i2
+  local res
+  local dp=0
+  local raw=""
+  -- sytstem area
+  res ="\nCDF: System Area"
+  res= res.."\n"..dumpCDF(tag)
+  -- segments (user area)
+  if(istable(tag.SEG[0])) then
+    res = res.."\n\nADF: User Area"
+    for i=0, #tag.SEG do
+      res=res.."\n"..dumpSegment(tag, i).."\n"
+    end
+  end
+  return res
+end
+
+--- 
+-- determine TagType (bits 0..6 of DCFlow)
+function getTokenType(DCFl)
+  --[[
+    0x00–0x2f IAM 
+    0x30–0x6f SAM 
+    0x70–0x7f GAM
+  ]]--
+  local tt = tonumber(bbit("0x"..DCFl,0,7),10)
+  if (tt >= 0 and tt <= 47) then tt = "IAM"
+  elseif (tt == 49) then tt = "SAM63"
+  elseif (tt == 48) then tt = "SAM64"
+  elseif (tt >= 50 and tt <= 111) then tt = "SAM"
+  elseif (tt >= 112 and tt <= 127) then tt = "GAM"
+  else tt = "???" end
+  return tt
+end
+
+---
+-- regenerate segment-header (after edit)
+function regenSegmentHeader(segment)
+  local seg=segment
+  local raw = segment.raw
+  local i
+  -- len  bit0..7 | len=12bit=low nibble of byte1..byte0
+  raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8))
+  -- high nibble of len  bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh?
+  raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),4.4)..bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8))
+  -- WRP
+  raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
+  -- WRC + RD
+  raw[4]=("%02x"):format(bbit("0x"..("%03x"):format(seg.WRC),4,3)..bbit("0x"..("%02x"):format(seg.RD*128),0,8))
+  -- flag
+  seg.flag=string.sub(raw[2],0,1)
+  --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4])
+  if(#seg.data>(seg.len-5)) then
+    print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
+    print("Data-Length has being reduced: removing ".. #seg.data-(seg.len-5) .." bytes from Payload");
+    for i=(seg.len-5), #seg.data-1 do
+      table.remove(seg.data)
+    end
+  elseif (#seg.data<(seg.len-5)) then
+    print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
+    print("Data-Length has being extended: adding "..(seg.len-5)-#seg.data.." bytes to Payload");
+    for i=#seg.data, (seg.len-5)-1 do
+      table.insert(seg.data, '00')
+    end
+  end
+  return seg
+end
+
+---
+-- get segmemnt-data from byte-table
+function getSegmentData(bytes, start, index)
+  local segment={
+    ['index'] = '00',
+    ['flag']  = 'c',
+    ['valid'] = 0,
+    ['last']  = 0,
+    ['len']   = 13,
+    ['raw']   = {'00', '00', '00', '00'},
+    ['WRP']   = 4,
+    ['WRC']   = 0,
+    ['RD']    = 0,
+    ['crc']   = '00',
+    ['data']  = {},
+    ['kgh']   = false
+  }
+  if (bytes[start]) then 
+    local i 
+    -- #index
+         segment.index = index
+         -- flag = high nibble of byte 1
+         segment.flag = string.sub(bytes[start+1],0,1)
+         -- valid = bit 6 of byte 1
+         segment.valid = bbit("0x"..bytes[start+1],6,1)
+         -- last = bit 7 of byte 1
+         segment.last = bbit("0x"..bytes[start+1],7,1)
+         -- len = (byte 0)+(bit0-3 of byte 1)
+    segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16)
+    -- raw segment header
+    segment.raw = {bytes[start], bytes[start+1], bytes[start+2], bytes[start+3]}
+         -- wrp (write proteted) = byte 2
+         segment.WRP = tonumber(bytes[start+2],16)
+         -- wrc (write control) - bit 4-6 of byte 3
+         segment.WRC = tonumber(bbit("0x"..bytes[start+3],4,3),16)
+         -- rd (read disabled) - bit 7 of byte 3
+         segment.RD = tonumber(bbit("0x"..bytes[start+3],7,1),16)
+         -- crc byte 4
+         segment.crc = bytes[start+4]
+    -- segment-data starts at segment.len - segment.header - segment.crc
+    for i=0, (segment.len-5) do
+      segment.data[i]=bytes[start+5+i]
+    end
+    return segment
+  else return false; 
+  end
+end
+
+--- 
+-- put segments from byte-table to tag-table 
+function segmentsToTag(bytes, tag)
+  if(#bytes>23) then
+    local start=23
+    local i=-1
+    if (istable(tag)) then
+      repeat
+        i=i+1
+        tag.SEG[i]=getSegmentData(bytes, start, ("%02d"):format(i))
+        if (tag.Type=="SAM") then
+          if (checkKghCrc(tag, i)) then tag.SEG[i].kgh=true end
+        end
+        start=start+tag.SEG[i].len
+      until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126)
+      return tag
+    else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
+  else print("no Segments: must be a MIM22") end
+end 
+
+--- CRC calculation and validation
+-- build kghCrc credentials
+function kghCrcCredentials(tag, segid) 
+  local x='00'
+  if (type(segid)=="string") then segid=tonumber(segid,10) end
+  if (segid>0) then x='93' end
+  local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2..("%02x"):format(tag.SEG[segid].WRP)
+  cred = cred..("%02x"):format(tag.SEG[segid].WRC)..("%02x"):format(tag.SEG[segid].RD)..x
+  for i=0, #tag.SEG[segid].data-2 do
+    cred = cred..tag.SEG[segid].data[i]
+  end
+  return cred
+end
+
+---
+-- validate kghCRC to segment in tag-table
+function checkKghCrc(tag, segid)
+  if (type(tag.SEG[segid])=='table') then
+    if (tag.data[3]=="11" and tag.raw=="9f" and tag.SSC=="ff") then
+      local data=kghCrcCredentials(tag, segid)
+      if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].data[tag.SEG[segid].len-5-1]) then return true; end 
+      else return false; end
+  else oops("'Kaba Group header' detected but no Segment-Data found") end
+end
+
+---
+-- calcuate kghCRC for a given segment 
+function calcKghCrc(tag, segid)
+  -- check if a 'Kaber Group Header' exists
+    local i
+    local data=kghCrcCredentials(tag, segid)
+    return ("%02x"):format(utils.Crc8Legic(data))
+end
+
+---
+-- build segmentCrc credentials
+function segmentCrcCredentials(tag, segid) 
+  local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+  cred = cred ..tag.SEG[segid].raw[1]..tag.SEG[segid].raw[2]..tag.SEG[segid].raw[3]..tag.SEG[segid].raw[4]
+  return cred
+end
+
+---
+-- validate segmentCRC for a given segment
+function checkSegmentCrc(tag, segid)
+    local data=segmentCrcCredentials(tag, segid)
+    if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].crc) then 
+      return true
+    end
+    return false
+end
+
+---
+-- calculate segmentCRC for a given segment
+function calcSegmentCrc(tag, segid)
+  -- check if a 'Kaber Group Header' exists
+    local data=segmentCrcCredentials(tag, segid)
+    return ("%02x"):format(utils.Crc8Legic(data))
+end
+
+--- create master-token
+
+---
+-- write virtual Tag to real Tag
+-- write clone-data to tag
+function writeToTag(plainBytes, taglen, filename)
+  local bytes
+       if(utils.confirm("\nplace your empty tag onto the PM3 to read & write\n") == false) then
+    return
+  end
+       
+       -- write data to file
+       if (taglen > 0) then
+               WriteBytes = utils.input("enter number of bytes to write?", taglen)
+
+               -- load file into pm3-buffer
+    if (type(filename)~="string") then filename=input("filename to load to pm3-buffer?","legic.temp") end
+               cmd = 'hf legic load '..filename
+               core.console(cmd)
+               
+               -- write pm3-buffer to Tag
+               for i=0, WriteBytes do
+                       if ( i<5 or i>6) then
+                               cmd = ('hf legic write 0x%02x 0x01'):format(i)
+                               core.console(cmd)
+        --print(cmd)
+                       elseif (i == 6) then
+                               -- write DCF in reverse order (requires 'mosci-patch')
+                               cmd = 'hf legic write 0x05 0x02'
+                               core.console(cmd)
+        --print(cmd)
+                       else
+                               print("skipping byte 0x05 - will be written next step")
+                       end                             
+                       utils.Sleep(0.2)
+               end
+       end
+end
+
+---
+-- edit segment helper
+function editSegment(tag, index)
+  local k,v
+  local edit_possible="valid len RD WRP WRC Stamp Payload"
+  if (istable(tag.SEG[index])) then
+    for k,v in pairs(tag.SEG[index]) do
+      if(string.find(edit_possible,k)) then
+        tag.SEG[index][k]=tonumber(input(k..": ", v),10)
+      end
+    end
+  else print("Segment with Index: "..("%02d"):format(index).." not found in Tag")
+    return false
+  end
+  regenSegmentHeader(tag.SEG[index]) 
+  print("\n"..dumpSegment(tag, index).."\n")
+  return tag
+end
+
+---
+-- edit Segment Data
+function editSegmentData(data)
+  if (istable(data)) then
+    for i=0, #data-1 do
+      data[i]=input("Data"..i..": ", data[i])
+    end 
+    return data
+  else
+    print("no Segment-Data found")
+  end
+end
+
+---
+-- list available segmets in virtual tag
+function segmentList(tag)
+  local i
+  local res = "" 
+  if (istable(tag.SEG[0])) then
+    for i=0, #tag.SEG do
+      res = res .. tag.SEG[i].index .. "  "
+    end
+    return res
+    else print("no Segments found in Tag")
+      return false
+  end
+end
+
+---
+-- helper to selecting a segment
+function selectSegment(tag)
+  local sel
+  if (istable(tag)) then 
+    print("availabe Segments:\n"..segmentList(tag))
+    sel=input("select Segment: ", '00')
+    sel=tonumber(sel,10)
+    if (sel) then return sel
+    else return '0' end
+  else
+    print("\nno Segments found")
+    return false
+  end
+end
+
+---
+-- add segment
+function addSegment(tag)
+  local i
+  local segment={
+    ['index'] = '00',
+    ['flag']  = 'c',
+    ['valid'] = 1,
+    ['last']  = 1,
+    ['len']   = 13,
+    ['raw']   = {'0d', '40', '04', '00'},
+    ['WRP']   = 4,
+    ['WRC']   = 0,
+    ['RD']    = 0,
+    ['crc']   = '00',
+    ['data']  = {},
+    ['kgh']   = false
+  }
+  if (istable(tag.SEG[0])) then
+    tag.SEG[#tag.SEG].last=0
+    table.insert(tag.SEG, segment)
+    for i=0, 8 do
+      tag.SEG[#tag.SEG].data[i]=("%02x"):format(i)
+    end
+    tag.SEG[#tag.SEG].index=("%02d"):format(#tag.SEG)
+    return tag
+  else
+    print("no Segment-Table found")
+  end
+end
+
+---
+--
+function delSegment(tag, index)
+  local i
+  if (type(index)=="string") then index=tonumber(index,10) end
+  if (index > 0) then
+    table.remove(tag.SEG, index)
+    for i=0, #tag.SEG do
+      tag.SEG[i].index=("%02d"):format(i)
+    end
+  end
+  if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
+  return tag
+end
+
+---
+-- helptext for modify-mode
+function modifyHelp()
+  local t=[[
+  
+    Data I/O           Segment Manipulation        File I/O   
+------------------     --------------------      ---------------
+  rt => read    Tag     ds => dump   Segments     lf => load File
+  wt => write   Tag     as => add    Segment      sf => save File 
+  ct => copy io Tag     es => edit   Segment      xf => xor  File
+  tc => copy oi Tag     ed => edit   Data     
+  di => dump  inTag     rs => remove Segment
+  do => dump outTag     cc => check  Segment-CRC                  
+                        ck => check  KGH                
+                        tk => toggle KGH-Flag
+  q => quit             xc => get    KGH-Str      h => this Help
+  ]]                    
+  return t
+end
+
+--- 
+-- modify Tag (interactive)
+function modifyMode()
+  local i, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes
+  actions = {
+     ["h"] = function(x) 
+              print(modifyHelp().."\n".."tags im Memory:"..(istable(inTAG) and " inTAG" or "")..(istable(outTAG) and " outTAG" or ""))
+            end,
+    ["rt"] = function(x) inTAG=readFromPM3(); actions['di']('') end,
+    ["wt"] = function(x)  
+              if(istable(inTAG)) then
+                local taglen=22
+                for i=0, #inTAG.SEG do
+                  taglen=taglen+inTAG.SEG[i].len+5
+                end
+                -- read new tag (output tag)
+                outTAG=readFromPM3()
+                outbytes=tagToBytes(outTAG)
+                -- copy 'inputbuffer' to 'outputbuffer'
+                inTAG.MCD  = outbytes[1]
+                inTAG.MSN0 = outbytes[2]
+                inTAG.MSN1 = outbytes[3]
+                inTAG.MSN2 = outbytes[4]
+                inTAG.MCC  = outbytes[5]
+                -- recheck all segments-crc/kghcrc
+                checkAllSegCrc(inTAG)
+                checkAllKghCrc(inTAG)
+                --get bytes from ready outTAG
+                bytes=tagToBytes(inTAG)
+                if (bytes) then   
+                  writeFile(bytes, 'MylegicClone.hex')         
+                  writeToTag(bytes, taglen, 'MylegicClone.hex')
+                  actions['rt']('') 
+                end
+               end
+              end,
+    ["ct"] = function(x)  
+                print("copy virtual input-TAG to output-TAG")
+                outTAG=inTAG
+            end,
+    ["tc"] = function(x)  
+                print("copy virtual output-TAG to input-TAG")
+                inTAG=outTAG
+            end,
+    ["lf"] = function(x)  
+              filename=input("enter filename: ", "legic.temp")
+              inTAG=readFile(filename)
+            end,
+    ["sf"] = function(x)  
+              if(istable(inTAG)) then
+                outfile=input("enter filename:", "legic.temp")
+                bytes=tagToBytes(inTAG)
+                --bytes=xorBytes(bytes, inTAG.MCC)
+                if (bytes) then             
+                  writeFile(bytes, outfile)
+                end
+               end
+              end,
+    ["xf"] = function(x)  
+              if(istable(inTAG)) then
+                outfile=input("enter filename:", "legic.temp")
+                crc=input("enter new crc: ('00' for a plain dump)", inTAG.MCC)
+                print("obfuscate witth: "..crc)
+                bytes=tagToBytes(inTAG)
+                bytes[5]=crc
+                if (bytes) then             
+                  writeFile(bytes, outfile)
+                end
+              end
+             end,
+    ["di"] = function(x) if (istable(inTAG)) then print("\n"..dumpTag(inTAG).."\n") end end,
+    ["do"] = function(x) if (istable(outTAG)) then print("\n"..dumpTag(outTAG).."\n") end end,
+    ["ds"] = function(x) 
+                sel=selectSegment(inTAG)
+                if (sel) then print("\n"..(dumpSegment(inTAG, sel) or "no Segments available").."\n") end 
+              end,
+    ["es"] = function(x) 
+              sel=selectSegment(inTAG)
+              if (sel) then 
+                if(istable(inTAG.SEG)) then
+                  inTAG=editSegment(inTAG, sel)
+                  inTAG.SEG[sel]=regenSegmentHeader(inTAG.SEG[sel])
+              else print("no Segments in Tag") end 
+              end
+            end,
+    ["as"] = function(x) 
+              if (istable(inTAG.SEG[0])) then
+                inTAG=addSegment(inTAG)
+                inTAG.SEG[#inTAG.SEG-1]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG-1])
+                inTAG.SEG[#inTAG.SEG]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG]) 
+                else print("unsegmented Tag!")
+              end
+            end,
+    ["rs"] = function(x) 
+              if (istable(inTAG)) then
+                sel=selectSegment(inTAG)
+                inTAG=delSegment(inTAG, sel)
+                for i=0, #inTAG.SEG do
+                  inTAG.SEG[i]=regenSegmentHeader(inTAG.SEG[i])
+                end
+              end
+            end,
+    ["ed"] = function(x) 
+              sel=selectSegment(inTAG)
+              if (sel) then 
+                inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data) 
+              end
+            end,
+     ["ts"] = function(x) 
+                sel=selectSegment(inTAG)
+                regenSegmentHeader(inTAG.SEG[sel]) 
+              end,
+     ["tk"] = function(x) 
+                sel=selectSegment(inTAG)
+                if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false
+                else inTAG.SEG[sel].kgh=true end
+              end,
+     ["k"] = function(x) 
+               print(("%02x"):format(utils.Crc8Legic(x)))
+              end,
+     ["xc"] = function(x) 
+               --get credential-string for kgh-crc on certain segment
+               --usage: xc <segment-index>
+               print("k "..kghCrcCredentials(inTAG, x))
+              end,
+     ["cc"] = function(x)  if (istable(inTAG)) then checkAllSegCrc(inTAG) end end,
+     ["ck"] = function(x)  if (istable(inTAG)) then checkAllKghCrc(inTAG) end end,
+     ["q"] = function(x)  end,
+  }
+  print("modify-modus! enter 'h' for help or 'q' to quit")
+  repeat 
+    ic=input("Legic command? ('h' for help - 'q' for quit)", "h")
+    -- command actions
+    if (type(actions[string.lower(string.sub(ic,0,1))])=='function') then
+      actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3))
+    elseif (type(actions[string.lower(string.sub(ic,0,2))])=='function') then
+      actions[string.lower(string.sub(ic,0,2))](string.sub(ic,4))
+    else actions['h']('') end
+  until (string.sub(ic,0,1)=="q")
+end
+
+--- main
+function main(args)
+       if (#args == 0 ) then modifyMode() end
+  --- variables
+  local inTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
+       -- just a spacer for better readability
+  print()
+  --- parse arguments
+       for o, a in getopt.getopt(args, 'hrmi:do:c:') do  
+    -- display help
+    if o == "h" then return help(); end
+    -- read tag from PM3
+    if o == "r" then inTAG=readFromPM3() end
+    -- input file
+               if o == "i" then inTAG=readFile(a) end
+    -- dump virtual-Tag 
+    if o == "d" then dfs=true end
+    -- interacive modifying
+    if o == "m" then interactive=true; modifyMode() end
+    -- xor (e.g. for clone or plain file)
+    if o == "c" then cfs=true; crc=a end
+    -- output file
+               if o == "o" then outfile=a; ofs=true end                
+  end
+  
+  -- file conversion (output to file)
+  if (ofs) then
+    -- dump infile / tag-read
+    if (dfs) then 
+      print("-----------------------------------------") 
+      print(dumpTag(inTAG))
+    end
+    bytes=tagToBytes(inTAG)
+    -- xor with given crc
+    if (cfs) then 
+      bytes[5]=crc 
+    end
+    -- write to outfile
+    if (bytes) then 
+      writeFile(bytes, outfile)
+      -- reed new content into virtual tag 
+      
+      inTAG=bytesToTag(bytes, inTAG)
+      -- show new content
+      if (dfs) then  
+        print("-----------------------------------------")
+        print(dumpTag(outTAG)) 
+      end
+    end
+  end
+  
+end
+
+--- start
+main(args)
\ No newline at end of file
diff --git a/client/scripts/legic_buffer2card.lua b/client/scripts/legic_buffer2card.lua
new file mode 100644 (file)
index 0000000..845e8e8
--- /dev/null
@@ -0,0 +1,54 @@
+-- this script writes bytes 8 to 256 on the Legic MIM256
+example = "Script writes to Legic Prime Tag from position 0x07 until 0xFF with the value 0x01"
+author = "Mosci"
+desc =
+[[
+This is a script which writes value 0x01 to bytes from position 0x07 until 0xFF on a Legic Prime Tag (MIM256 or MIM1024)
+(created with 'hf legic save my_dump.hex') 
+       
+optional arguments :
+       -h       - Help text
+
+Examples : 
+       script run legic_buffer2card
+]]
+
+local utils = require('utils')
+local getopt = require('getopt')
+--- 
+-- This is only meant to be used when errors occur
+function oops(err)
+       print("ERROR: ",err)
+       return nil, err
+   end
+--- 
+-- Usage help
+function help()
+       print(desc)
+       print("Example usage")
+       print(example)
+end
+--
+-- simple loop-write from 0x07 to 0xff
+function main()
+
+       -- parse arguments for the script
+       for o, a in getopt.getopt(args, 'h') do
+               if o == "h" then return help() end
+       end
+
+       local cmd = ''
+       local i
+       for i = 7, 255 do
+           cmd = ('hf legic write 0x%02x 0x01'):format(i)
+           print(cmd)
+               core.clearCommandBuffer()
+           core.console(cmd)
+               
+               -- got a 'cmd-buffer overflow' on my mac - so just wait a little
+               -- works without that pause on my linux-box
+               utils.Sleep(0.1)
+       end
+end
+
+main()
Impressum, Datenschutz