From 733eb42022728f5b22028d4e8860e4efe6f18a5c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 28 Feb 2016 14:50:49 +0100 Subject: [PATCH] ADD: Added three legic luascript from @iscom (Mosic) Great work! --- client/scripts/Legic_clone.lua | 541 ++++++++++++++ client/scripts/legic.lua | 1039 ++++++++++++++++++++++++++ client/scripts/legic_buffer2card.lua | 54 ++ 3 files changed, 1634 insertions(+) create mode 100644 client/scripts/Legic_clone.lua create mode 100644 client/scripts/legic.lua create mode 100644 client/scripts/legic_buffer2card.lua diff --git a/client/scripts/Legic_clone.lua b/client/scripts/Legic_clone.lua new file mode 100644 index 00000000..2c8a84f4 --- /dev/null +++ b/client/scripts/Legic_clone.lua @@ -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 (file to read data from) + +optional arguments : + -h - Help text + -o - requieres option -c to be given + -c - 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 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 index 00000000..dd119b03 --- /dev/null +++ b/client/scripts/legic.lua @@ -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 + 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 index 00000000..845e8e8e --- /dev/null +++ b/client/scripts/legic_buffer2card.lua @@ -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() -- 2.39.2