proxmark3/client/scripts/legic.lua

1039 lines
31 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--[[
(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)
--[[
0x000x2f IAM
0x300x6f SAM
0x700x7f 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)