--[[
+if it don't works with you tag-layout - be so kind and let me know ;-)
+
+Tested on Tags with those Layouts:
+
(example) Legic-Prime Layout with 'Kaba Group Header'
+----+----+----+----+----+----+----+----+
0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |
+----+----+----+----+----+----+----+----+
0x20|UID1|UID2|kghC|
+----+----+----+
+MCD = Manufacturer ID
+MSN = Manufacturer SerialNumber
+60 ea = DCF Low + DCF high
+9f = raw byte which holds the bits for OLE,WRP,WRC,RD
+ff = unknown but important
+00 = unimportant
+11 = unknown but important
+Bck = header-backup-area
+00 00 = Year (00 = 2000) & Week (not important)
+Seg = Segment Header
+SegC = Crc8 over the Segment Header
+Stp = Stamp (could be more as 4 - up to 7)
+UID = dec User-ID for online-Mapping
+kghC = crc8 over MCD + MSN0..MSN2 + UID
+
+
+(example) Legic-Cash on MIM256/1024 tag' (37 bytes)
+ +----+----+----+----+----+----+----+----+
+ 0x00|Seg0|Seg1|Seg2|Seg3|SegC|STP0|STP1|STP2|
+ +----+----+----+----+----+----+----+----+
+ 0x08|STP3|STP4|STP5|STP6| 01 |CURh|CURl|LIMh|
+ +----+----+----+----+----+----+----+----+
+ 0x10|LIMm|LIMl|CHKh|CHKl|BALh|BALm|BALl|LRBh|
+ +----+----+----+----+----+----+----+----+
+ 0x18|LRBm|LRBl|CHKh|CHKl|SHDh|SHDm|SHDl|LRSh|
+ +----+----+----+----+----+----+----+----+
+ 0x20|LRSm|LRSl| CV |CHKh|CHKl|
+ +----+----+----+----+----+
+STP = Stamp (seems to be always 7 bytes)
+01 = unknown but important
+CUR = currency in HEX (ISO 4217)
+LIM = Cash-Limit
+CHK = crc16 over byte-addr 0x05..0x12
+BAL = Balance
+LRB = ID of the reader that changed the balance
+CHK = crc16 over BAL + LRB
+SHD = shadow Balance
+LRS = ID of the reader that changed the shadow balance (?? should be always the same as LRB)
+CV = Counter value for transactions
+CHK = crc16 over SHD + LRS + CV
+
+(example) Legic-Prime Layout 'gantner unsegmented user-credential'
+ +----+----+----+----+----+----+----+----+
+ 0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 08 |
+ +----+----+----+----+----+----+----+----+
+ 0x08|Stp0|Stp1|Stp2|Stp3|Stp4|Dat0|Dat1|uCRC| <- addr 0x08..0x0f is WRP
+ +----+----+----+----+----+----+----+----+
+ 0x10|emb0| <- this is only within wrp if addr 0x07==09
+ +----+
+MCD = Manufacturer ID
+MSN = Manufacturer SerialNumber
+60 ea = DCF Low + DCF high
+08 = raw byte which holds the bits for OLE,WRP,WRC,RD
+Stp = Stamp (could be more as 4 - up to 7)
+Dat = Online-Mapping Data
+uCRC = crc8 over addr 0x00..0x03+0x07..0x0E
+
+
+(example) Legic-Prime Layout 'gantner unsegmented Master-Token (IAM) with a stamp_len of 4'
+ +----+----+----+----+----+----+----+----+
+ 0x00|MCD |MSN0|MSN1|MSN2|MCC | 20 | f8 | 08 |
+ +----+----+----+----+----+----+----+----+
+ 0x08|Stp0|Stp1|Stp2|Stp3| 00 | 00 | 00 |CRC1|
+ +----+----+----+----+----+----+----+----+
+ 0x10| 00 | 00 | 00 | 00 | 00 |CRC2|
+ +----+----+----+----+----+----+
+MCD = Manufacturer ID
+MSN = Manufacturer SerialNumber
+60 ea = DCF Low + DCF high
+08 = raw byte which holds the bits for OLE,WRP,WRC,RD
+Stp = Stamp (could be more as 4 - up to 7)
+Dat = Online-Mapping Data
+CRC1 = crc8 over addr 0x00..0x03+0x07..0x0E (special 'gantner crc8')
+CRC2 = MCD + MSB0..2+ addr 0x06 + addr 0x05 + addr 0x07 + Stamp (regular Master-Token-CRC)
--]]
example = "script run legic"
author = "Mosci"
+version = "1.0.3"
+
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 Segment Manipulation Token-Data
+ ----------------- -------------------- -----------------
+ rt => read Tag as => add Segment mt => make Token
+ wt => write Tag es => edit Segment Header et => edit Token data
+ ed => edit Segment Data tk => toggle KGH-Flag
+ File I/O rs => remove Segment
+ ----------------- cc => check Segment-CRC
+ lf => load File ck => check KGH
+ sf => save File ds => dump Segments
+ xf => xor to File
+
+
+ (partially) known Segments Virtual Tags Script Output
+ --------------------------- ------------------------------- ------------------------
+ dlc => dump Legic-Cash ct => copy mainTag to backupTag tac => toggle ansicolors
+ elc => edit Legic-Cash tc => copy backupTag to mainTag
+ d3p => dump 3rd-Party-Cash tt => switch mainTag & backupTag
+ e3p => edit 3rd-Party-Cash di => dump mainTag
+ do => dump backupTag
+
+
- 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.
+
+ 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)
+
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)
+ tt: 'toggle tag' - copy mainTag to BackupTag and backupTag to mainTag
+
+ di: 'dump mainTag' - shows the current content of the 'virtual Tag'
+ do: 'dump backupTag' - shows the current content of the 'virtual outTag'
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)
+ ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data)
+ et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data)
+ mt: 'make Token' - create a Token 'from scratch' (guided)
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)
+dlc: 'dump Legic-Cash' - show balance and checksums of a Legic-Cash Segment
+elc: 'edit Legic-Cash' - edit values of a Legic-Cash Segment
+
+d3p: 'dump 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd-Party Cash Segment
+e3p: 'edit 3rd Party' - edit Data in 3rd-Party Cash Segment
+
+tac: 'toggle ansicolors'- switch on and off the colored text-output of this script
+ default can be changed by setting the variable 'colored_output' to false
]]
+currentTag="inTAG"
---- requirements
-local utils = require('utils')
-local getopt = require('getopt')
+---
+-- requirements
+local utils = require('utils')
+local getopt = require('getopt')
+local ansicolors = require('ansicolors')
---- global variables / defines
+---
+-- global variables / defines
local bxor = bit32.bxor
local bbit = bit32.extract
local input = utils.input
local confirm = utils.confirm
---- Error-Handling & Helper
+---
+-- init ansicolor-values & ansicolors switch
+local colored_output = true
+local acoff = ""
+local acgreen= ""
+local accyan = ""
+local acred = ""
+local acyellow = ""
+local acblue = ""
+local acmagenta = ""
+
+--- Helper ---
+---
+-- default colors (change to whatever you want)
+function load_colors(onoff)
+ if (onoff) then
+ -- colors
+ acgreen = ansicolors.green
+ accyan = ansicolors.cyan
+ acred = ansicolors.red
+ acyellow= ansicolors.yellow
+ acblue = ansicolors.blue
+ acmagenta= ansicolors.magenta
+ acoff = ansicolors.reset
+ else
+ -- 'no color'
+ acgreen = ""
+ accyan = ""
+ acred = ""
+ acyellow= ""
+ acblue = ""
+ acmagenta= ""
+ acoff = ""
+ end
+end
+
+---
+-- curency-codes for Legic-Cash-Segments (ISO 4217)
+local currency = {
+ ["03d2"]="EUR",
+ ["0348"]="USD",
+ ["033A"]="GBP",
+ ["02F4"]="CHF"
+}
+
+---
-- This is only meant to be used when errors occur
function oops(err)
- print("ERROR: ",err)
+ print(acred.."ERROR: "..acoff ,err)
return nil, err
end
-- Usage help
function help()
print(desc)
- print("Example usage")
- print(example)
+ print("Version: "..version)
+ print("Example usage: "..example)
end
---
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
+-- creates a 'deep copy' of a table (a=b only references)
+function deepCopy(object)
+ local lookup_table = {}
+ local function _copy(object)
+ if type(object) ~= "table" then
+ return object
+ elseif lookup_table[object] then
+ return lookup_table[object]
+ end
+ local new_table = {}
+ lookup_table[object] = new_table
+ for index, value in pairs(object) do
+ new_table[_copy(index)] = _copy(value)
+ end
+ return setmetatable(new_table, getmetatable(object))
+ end
+ return _copy(object)
end
---
--- xor byte (if addr >= 0x22 - start counting from 1 => 23)
+-- xor single byte
function xorme(hex, xor, index)
if ( index >= 23 ) then
return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
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)
+-- split csv-string into table
+local function split(str, sep)
+ local sep = sep or ','
+ local fields={}
+ local matchfunc = string.gmatch(str, "([^"..sep.."]+)")
+ if not matchfunc then return {str} end
+ for str in matchfunc do
+ table.insert(fields, str)
end
- end
- return tag
+ return fields
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
+-- put a string into a bytes-table
+function str2bytes(s)
+ local res={}
+ if (string.len(s)%2~=0) then return print("stamp should be a even hexstring e.g.: deadbeef or 0badc0de") end
+ for i=1, string.len(s), 2 do
+ table.insert(res, string.sub(s,i,(i+1)))
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
+ return res
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
+---
+-- 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
---
end
end
fhi:close()
+ if (bytes[7]=='00') then return false end
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={
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))
+ if (tag.Type=="SAM" and tag.raw=='9f') then
tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10))
+ elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then
+ tag.Stamp_len = tonumber(tag.raw,10)
+ end
tag.data=bytesToTable(bytes, 10, 13)
tag.Bck=bytesToTable(bytes, 14, 20)
tag.MTC=bytesToTable(bytes, 21, 22)
- print("Tag-Type: ".. tag.Type)
+ print(acgreen.."Tag-Type: ".. tag.Type..acoff)
if (tag.Type=="SAM" and #bytes>23) then
tag=segmentsToTag(bytes, tag)
- print((#tag.SEG+1).." Segment(s) found")
+ print(acgreen..(#tag.SEG+1).." Segment(s) found"..acoff)
+ -- unsegmented Master-Token
+ -- only tag-data
+ else
+ for i=0, #tag.Bck do
+ table.insert(tag.data, tag.Bck[i])
+ end
+ tag.data[#tag.data]=tag.MTC[0]
+ tag.Bck=nil
+ --tag.MTC[0]=tag.MTC[1]
+ --tag.MTC[1]=nil
end
- print(#bytes.." bytes for Tag processed")
+ print(accyan..#bytes.." bytes for Tag processed"..acoff)
return tag
end
return oops("tag is no table in: bytesToTag ("..type(tag)..")")
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
+
---
--- dump tag-system area
-function dumpCDF(tag)
- local res=""
- local i=0
- local raw=""
+-- read Tag-Table in bytes-table
+function tagToBytes(tag)
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]
+ 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
- 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.." "
+ -- backup data
+ if(istable(tag.Bck)) then
+ for i=0, #tag.Bck do
+ table.insert(bytes, tag.Bck[i])
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)..", "
+ -- 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
+ return bytes
+ end
+ return oops("tag is no table in tagToBytes ("..type(tag)..")")
+end
+
+--- PM3 I/O ---
+---
+-- read from pm3 into virtual-tag
+function readFromPM3()
+ local tag, bytes, infile
+ infile="legic.temp"
+ core.console("hf legic reader")
+ core.console("hf legic save "..infile)
+ tag=readFile(infile)
+ return tag
+end
+
+---
+-- write virtual Tag to real Tag
+function writeToTag(tag)
+ local bytes
+ local filename='MylegicClone.hex'
+ local taglen=22
+ if(utils.confirm(acred.."\nplace the (empty) Tag onto the PM3\nand confirm writing to this Tag: "..acoff) == false) then
+ return
+ end
+ -- get used bytes / tag-len
+ if(istable(tag.SEG)) then
+ if (istable(tag.Bck)) then
+ for i=0, #tag.SEG do
+ taglen=taglen+tag.SEG[i].len+5
+ end
+ end
+ local uid_old=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+ -- read new tag into memory so we can xor the new data with the new MCC
+ outTAG=readFromPM3()
+ outbytes=tagToBytes(outTAG)
+ -- copy 'inputbuffer' to 'outputbuffer'
+ tag.MCD = outbytes[1]
+ tag.MSN0 = outbytes[2]
+ tag.MSN1 = outbytes[3]
+ tag.MSN2 = outbytes[4]
+ tag.MCC = outbytes[5]
+ -- recheck all segments-crc/kghcrc (only on a credential)
+ if(istable(tag.Bck)) then
+ checkAllSegCrc(tag)
+ checkAllKghCrc(tag)
+ local uid_new=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+ for i=0, #tag.SEG do
+ if (check43rdPartyCash1(uid_old, tag.SEG[i].data)) then
+ io.write(accyan.."\nfixing known checksums"..acoff.." ... ")
+ if (fix3rdPartyCash1(uid_new, tag.SEG[i].data)) then
+ io.write(acgreen.." done\n"..acoff)
+ else oops("\nsomething went wrong at the repair of the 3rd-party-cash-segment") end
+ end
+ end
+ end
+ bytes=tagToBytes(tag)
+ -- master-token-crc
+ if (tag.Type~="SAM") then bytes[22]=calcMtCrc(bytes) end
+ if (bytes) then
+ print("write temp-file '"..filename.."'")
+ print(accyan)
+ writeFile(bytes, filename)
+ --writeToTag(bytes, taglen, 'MylegicClone.hex')
+ print(acoff)
+ end
+ end
+ -- write data to file
+ if (taglen > 0) then
+ WriteBytes = utils.input(acyellow.."enter number of bytes to write?"..acoff, taglen)
+ -- load file into pm3-buffer
+ if (type(filename)~="string") then filename=input(acyellow.."filename to load to pm3-buffer?"..acoff,"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'
+ print(acgreen..cmd..acoff)
+ core.console(cmd)
+ --print(cmd)
+ else
+ print(acgreen.."skip byte 0x05 - will be written next step"..acoff)
+ end
+ utils.Sleep(0.2)
+ end
+ end
+end
+
+--- File I/O ---
+---
+-- read file into virtual-tag
+function readFile(filename)
+ print(accyan)
+ 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])
+ print("create virtual tag from ".. #bytes .. " bytes")
+ -- create Tag for plain bytes
+ tag=createTagTable()
+ -- load plain bytes to tag-table
+ print(acoff)
+ tag=bytesToTag(bytes, tag)
+ end
+ end
+ return tag
+end
+
+---
+-- write bytes 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
+
+--- Map related ---
+---
+-- make tagMap
+function makeTagMap()
+ local tagMap={}
+ if (#tagMap==0) then
+ tagMap['name']=input(accyan.."enter Name for this Map: "..acoff , "newTagMap")
+ tagMap['mappings']={}
+ tagMap['crc8']={}
+ -- insert fixed Tag-CRC
+ table.insert(tagMap.crc8, {name='TAG-CRC', pos=5, seq={1, 4}})
+ tagMap['crc16']={}
+ end
+ print(accyan.."new tagMap created"..acoff)
+ return tagMap
+end
+
+---
+-- save mapping to file
+function saveTagMap(map, filename)
+ if (string.len(filename)>0) 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 fho,err = io.open(filename, "w")
+ if err then oops("OOps ... faild to open output-file ".. filename) end
+
+ -- write line to new file
+ for k, v in pairs(map) do
+ if (istable(v)) then
+ for k2, v2 in pairs(v) do
+ if (k=='mappings') then
+ fho:write(k..","..k2..","..v2['name']..","..v2['start']..","..v2['end']..","..((v2['highlight']) and "1" or "0").."\n")
+ elseif (k=="crc8") then
+ local tmp=""
+ tmp=k..","..k2..","..v2['name']..","..v2['pos']..","
+ tmp=tmp..tbl2seqstr(v2['seq'])
+ fho:write(tmp.."\n")
+ end
+ end
+ else
+ fho:write(k..","..v.."\n")
+ end
+ end
+ fho:close()
+ return true
+end
+
+---
+-- toggle higligh
+function toggleHighlight(tbl)
+ if (tbl['highlight']) then tbl['highlight']=false
+ else tbl['highlight']=true end
+ return tbl
+end
+
+---
+-- return table od seqence-string
+function seqstr2tbl(seqstr)
+ local s=split(seqstr)
+ local res={}
+ if (#s>=1) then
+ for sk, sv in pairs(s) do
+ s2=split(sv, '-')
+ if(#s2==2) then
+ table.insert(res, s2[1])
+ table.insert(res, s2[2])
+ end
+ end
+ end
+ return res
+end
+
+---
+-- return sequence-string from table
+function tbl2seqstr(seqtbl)
+ local res=""
+ if (istable(seqtbl)) then
+ for sk, sv in pairs(seqtbl) do
+ res=res..sv..((sk%2==0) and "," or "-")
+ end
+ if (string.sub(res, string.len(res))==",") then
+ res=string.sub(res, 1, string.len(res)-1)
+ end
+ end
+ return res
+end
+
+---
+-- read map-file into map
+function loadTagMap(filename)
+ local map={mappings={}, crc8={}, crc16={}}
+ local m=0
+ local c=0
+ local line, fields
+ local temp={}
+ local offset=0
+ if (file_check(filename)==false) then
+ return oops("input file: "..filename.." not found")
+ else
+ local fhi,err = io.open(filename)
+ while true do
+ line = fhi:read()
+ if line == nil then
+ break
+ else
+ fields=split(line)
+ end
+ if (#fields==2) then
+ if (fields[1]=='offset') then
+ offset=tonumber(fields[2],10)
+ end
+ -- map-name
+ map[fields[1]]=fields[2]
+ elseif (fields[1]=='mappings') then
+ m=m+1
+ temp={}
+ -- mapping
+ temp['name']=fields[3]
+ temp['start']=tonumber(fields[4], 10)
+ temp['end']=tonumber(fields[5], 10)
+ if(temp['start']>22) then
+ temp['start']=temp['start']+offset
+ temp['end']=temp['end']+offset
+ end
+ if (tonumber(fields[6], 10)==1) then temp['highlight']= true
+ else temp['highlight']= false end
+ table.insert(map['mappings'], m, temp)
+ elseif (fields[1]=='crc8') then
+ c=c+1
+ temp={}
+ -- crc8
+ temp['name']=fields[3]
+ temp['pos']=tonumber(fields[4], 10)+offset
+ local s=string.sub(line, string.len(fields[1]..","..fields[2]..","..fields[3]..",")+1, string.len(line))
+ temp['seq']=seqstr2tbl(s)
+ for k, v in pairs(temp['seq']) do
+ if(tonumber(v, 10)>22) then v=tonumber(v, 10)+offset end
+ temp['seq'][k]=tonumber(v, 10)
+ end
+ table.insert(map.crc8, temp)
+ end
+ end
+ fhi:close()
+ end
+ return map
+end
+
+---
+-- dump tagMap (mappings only)
+function dumpTagMap(tag, tagMap)
+ if(#tagMap.mappings>0) then
+ bytes=tagToBytes(tag)
+ local temp
+ local lastend=0
+ -- start display mappings
+ for k, v in pairs(tagMap.mappings) do
+ if ((lastend+1)<v['start']) then
+ print("...")
+ end
+ if (isPosCrc8(tagMap, v['start'])>0) then
+ if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, v['start']) ) ) then
+ io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acgreen..v['name']..acoff..":")
+ else
+ io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acred..v['name']..acoff..":")
+ end
+ else
+ io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..((v['highlight']) and acmagenta or acyellow)..v['name']..acoff..":")
+ end
+ temp=""
+ for i=((string.len(v['name']))/10), 2 do
+ temp=temp.."\t"
+ end
+ for i=v['start'], v['end'] do
+ temp=temp..bytes[i].." "
+ end
+ print(temp)
+ lastend=v['end']
+ end
+ end
+end
+
+---
+--
+function isPosCrc8(tagMap, pos)
+ local res=0
+ if (#tagMap.crc8>0) then
+ for k, v in pairs(tagMap.crc8) do
+ if(v['pos']==pos) then res=k end
+ end
+ end
+ return res
+end
+
+---
+-- check mapped crc
+function checkMapCrc8(tagMap, bytes, n)
+ local res=false
+ if (#tagMap.crc8>0) then
+ if(istable(tagMap.crc8[n])) then
+ temp=""
+ for k2, v2 in pairs(tagMap.crc8[n]) do
+ if (istable(v2)) then
+ temp=temp..tbl2seqstr(v2)
+ end
+ end
+ local tempres=""
+ local tempres=getSequences(bytes, temp)
+ tempres=("%02x"):format(utils.Crc8Legic(tempres))
+ if (bytes[tagMap.crc8[n]['pos']]==tempres) then
+ res=true
+ end
+ end
+ end
+ return res
+end
+
+---
+-- edit existing Map
+function editTagMap(tag, tagMap)
+ local t = [[
+ Data: dm = show dr = dump raw
+Mappings: im = insert am = add rm = remove
+ CRC8: ac8 = add sc8 = show rc8 = remove
+ : q = exit h = Help
+ ]]
+ --if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end
+ print("tagMap edit-mode submenu")
+ repeat
+ x=input('tagMap submenu:', 'h')
+ if (x=='h') then print(t)
+ elseif (x=='dm') then tagMmap=dumpTagMap(tag, tagMap)
+ elseif (x=='dr') then tagMmap=dumpMap(tag, tagMap)
+ elseif (x=='rc8') then
+ if (istable(tagMap.crc8)) then
+ local x1 = selectTableEntry(tagMap.crc8, "select number of CRC8 to remove:")
+ if (istable(tagMap.crc8[x1])) then
+ table.remove(tagMap.crc8, x1)
+ end
+ end
+ elseif (x=='ac8') then
+ local p=tonumber(input("enter byte-addr of crc8", '0'),10)
+ if (p>0) then
+ local i1=input("enter comma-seperated byte-sequences (e.g.: '1-4,23-26')", '1-4,23-26')
+ local s1=split(i1, ',')
+ if (#s1>0) then
+ local temp={seq={}}
+ for k, v in pairs(s1) do
+ v1=split(v, '-')
+ if(#v1==2) then
+ table.insert(temp.seq, v1[1])
+ table.insert(temp.seq, v1[2])
+ end
+ end
+ temp['pos']=p
+ temp['name']=input("enter a name for the CRC8", "CRC "..(#tagMap.crc8+1))
+ table.insert(tagMap.crc8, temp)
+ end
+ end
+ elseif (string.sub(x, 1, 3)=='sc8') then
+ local bytes=tagToBytes(tag)
+ local res, pos
+ -- trigger manually by sc8 <'4digit' checkadd> <'seqeuence-string'>
+ -- e.g.: sc8 0027 1-4,23-36
+ if (string.len(x)>=9) then
+ pos=tonumber(string.sub(x, 5, 8), 10)
+ x=string.sub(x, 9, string.len(x))
+ print("x: "..x.." - pos:"..pos)
+ else
+ x=selectTableEntry(tagMap.crc8, "select CRC:")
+ if(istable(tagMap.crc8[x])) then
+ pos=tagMap.crc8[x]['pos']
+ x=tbl2seqstr(tagMap.crc8[x]['seq'])
+ end
+ end
+ if (type(x)=='string') then
+ res=("%02x"):format(utils.Crc8Legic(getSequences(bytes, x)))
+ print(accyan.."Sequence:\t"..acoff..x)
+ print(accyan.."Bytes:\t\t"..acoff..getSequences(bytes, x))
+ print(accyan.."calculated: "..acoff..res..accyan.." bytes["..pos.."]: "..acoff..bytes[pos].." ("..compareCrc(utils.Crc8Legic(getSequences(bytes, x)), bytes[pos])..")")
+ end
+ elseif (x=="tm") then
+ x=selectTableEntry(tagMap.mappings, "select number of Mapping:")
+ tagMap.mappings[x]=toggleHighlight(tagMap.mappings[x])
+ elseif (x=='am') then tagMap=addMapping(tag, tagMap)
+ elseif (x=='im') then tagMap=addMapping(tag, tagMap, selectTableEntry(tagMap.mappings, "select List-Position for insert:"))
+ elseif (x=='rm') then tagMap=deleteMapping(tag, tagMap)
+ elseif (x=='mas') then tagMap=mapTag(tagMap); tagMap=mapAllSegments(tag, tagMap)
+ elseif (type(actions[string.sub(x, 3)])=='function') then actions[string.sub(x, 3)]()
+ end
+ until x=='q'
+ print("exit sub-Menu")
+ return tagMap
+end
+
+---
+-- dump raw mapped and unmapped
+function dumpMap(tag, tagMap)
+ local dstart=1
+ local dend, cnt
+ local bytes = tagToBytes(tag)
+ local stats = getSegmentStats(bytes)
+ dend=stats[#stats]['end']
+ print(accyan.."Tag uses "..dend.." bytes:"..acoff)
+ for i=dstart, dend do
+ if (check4MappedByte(i, tagMap) and not check4MapCrc8(i, tagMap) and not check4Highlight(i, tagMap)) then io.write(""..acyellow)
+ elseif (check4MapCrc8(i, tagMap)) then
+ if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, i) ) ) then
+ io.write(""..acgreen)
+ else
+ io.write(""..acred)
+ end
+ else
+ io.write(""..acoff)
+ end
+ -- highlighted mapping
+ if (check4Highlight(i, tagMap)) then io.write(""..acmagenta) end
+
+ io.write(bytes[i])
+ if (i%8==0) then io.write("\n")
+ else io.write(" ") end
+ end
+
+ io.write("\n"..acoff)
+end
+
+---
+-- show bytes used for crc-calculation
+function getSequences(bytes, seqstr)
+ if (type(seqstr)~="string") then seqstr=input("enter comma-seperated sequences (e.g.: '1-4,23-26')", '1-4,23-26') end
+ local seqs=split(seqstr, ',')
+ local res = ""
+ if(#seqs>0) then
+ for k, v in pairs(seqs) do
+ local seq = split(v,'-')
+ if (#seq>=2) then
+ for i=seq[1], seq[2] do
+ res=res..bytes[i].." "
+ end
+ end
+ if(string.len(res)>0) then res=res.." " end
+ end
+ else
+ oops("no sequence found in '"..seqstr.."'")
+ end
+ return res
+end
+
+---
+-- check if byte-addr is a know crc
+function check4MapCrc8(addr, tagMap)
+ local res=false
+ for i=1, #tagMap.crc8 do
+ if (addr == tagMap.crc8[i]['pos']) then
+ res=true
+ end
+ end
+ return res
+end
+
+---
+-- check if byte-addr is a know crc
+function check4MapCrc16(addr, tagMap)
+ local res=false
+ for i=1, #tagMap.crc16 do
+ if (addr == tagMap.crc16[i]['pos']) then
+ res=true
+ end
+ end
+ return res
+end
+
+---
+-- check if byte is mapped or not
+function check4MappedByte(addr, tagMap)
+ local res=false
+ for _, v in pairs(tagMap.mappings) do
+ if (addr >= v['start'] and addr <= v['end'] ) then
+ res= true
+ end
+ end
+ return res
+end
+
+---
+-- check if byte is highlighted or not
+function check4Highlight(addr, tagMap)
+ local res=false
+ for _, v in pairs(tagMap.mappings) do
+ if (addr >= v['start'] and addr <= v['end'] ) then
+ res= v['highlight']
+ end
+ end
+ return res
+end
+
+---
+-- add interactive mapping
+function addMapping(tag, tagMap, x)
+ if (type(x)~="number") then x=#tagMap.mappings+1 end
+ local bytes=tagToBytes(tag)
+ local myMapping={}
+ myMapping['name'] =input(accyan.."enter Maping-Name:"..acoff, string.format("mapping %d", #tagMap.mappings+1))
+ myMapping['start']=tonumber(input(accyan.."enter start-addr:"..acoff, '1'), 10)
+ myMapping['end'] =tonumber(input(accyan.."enter end-addr:"..acoff, #bytes), 10)
+ myMapping['highlight']=confirm("set highlighted")
+ table.insert(tagMap.mappings, x, myMapping)
+ return tagMap
+end
+
+---
+-- delete mapping
+function deleteMapping(tag, tagMap)
+ if(#tagMap.mappings>0) then
+ local d = selectTableEntry(tagMap.mappings, "select number of Mapping to remove:")
+ if (type(d)=='number') then
+ table.remove(tagMap.mappings, d)
+ else oops("deleteMapping: got type = "..type(d).." - expected type = 'number'")
+ end
+ end
+ return tagMap
+end
+
+---
+-- select a mapping from a tagmap
+function selectTableEntry(table, action)
+ if (type(action)~="string") then action="select number of item:" end
+ for k, v in pairs(table) do
+ print(accyan..k..acoff.."\t-> "..accyan..v['name']..acoff)
+ end
+ local res = tonumber(input(action , 0), 10)
+ if (istable(table[res])) then
+ return res
+ else
+ return false
+ end
+end
+
+---
+-- map all segments
+function mapAllSegments(tag, tagMap)
+ local bytes=tagToBytes(tag)
+ local WRP,WRC,WRPC
+ segs=getSegmentStats(bytes)
+ if (istable(segs)) then
+ for k, v in pairs(segs) do
+ -- wrp (write proteted) = byte 2
+ WRP = tonumber(bytes[v['start']+2],16)
+ -- wrc (write control) - bit 4-6 of byte 3
+ WRC = tonumber(bbit("0x"..bytes[v['start']+3],4,3),16)
+ --tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." HDR", v['start'], v['start']+3)
+ tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." CRC", v['start']+4, v['start']+4, true)
+ table.insert(tagMap.crc8, {name = 'Segment '..("%02d"):format(v['index']).." CRC", pos=v['start']+4, seq={1,4,v['start'],v['start']+3}} )
+ if(WRC>WRP) then
+ WRPC=WRC
+ tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRC", v['start']+5, v['start']+5+WRC-1, true)
+ elseif (WRP>WRC and WRC>0) then
+ WRPC=WRP
+ tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRC", v['start']+5, v['start']+5+WRC-1, true)
+ tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRP", v['start']+WRC+5, v['start']+5+WRP-1, true)
+ else
+ WRPC=WRP
+ tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRP", v['start']+5, v['start']+5+WRP-1, true)
+ end
+ tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." data", v['start']+5+WRPC, v['end'], false)
+
+ end
+ print(#segs.." Segments mapped")
+ else
+ oops("autoMapSegments failed: no Segments found")
+ end
+ return tagMap
+end
+
+---
+-- map all token data
+function mapTokenData(tagMap, mname, mstart, mend, mhigh)
+ --if ( not mhigh ) then mhigh=false end
+ local myMapping={}
+ myMapping['name'] =mname
+ myMapping['start']=mstart
+ myMapping['end'] =mend
+ myMapping['highlight']=mhigh
+ table.insert(tagMap.mappings, myMapping)
+ return tagMap
+end
+
+---
+-- map a map
+function mapTag(tagMap)
+ tagMap=makeTagMap()
+ tagMap=mapTokenData(tagMap, 'Tag-ID', 1, 4, true)
+ tagMap=mapTokenData(tagMap, 'Tag-CRC', 5, 5, false)
+ tagMap=mapTokenData(tagMap, 'DCF', 6, 7, true)
+ tagMap=mapTokenData(tagMap, 'THDR-Raw/Stamp-Len', 8, 8, true)
+ tagMap=mapTokenData(tagMap, 'SSC', 9, 9, true)
+ tagMap=mapTokenData(tagMap, 'Header', 10, 13, false)
+ tagMap=mapTokenData(tagMap, 'Backup', 14, 19, true)
+ tagMap=mapTokenData(tagMap, 'Bck-CRC', 20, 20, false)
+ tagMap=mapTokenData(tagMap, 'TokenTime', 21, 22, false)
+ return tagMap
+end
+
+--- Dump Data ---
+---
+-- dump virtual Tag-Data
+function dumpTag(tag)
+ local i, i2
+ local res
+ local dp=0
+ local raw=""
+ -- sytstem area
+ res =acyellow.."\nCDF: System Area"..acoff
+ res= res.."\n"..dumpCDF(tag)
+ -- segments (user-token area)
+ if(tag.Type=="SAM" and tag.raw=='9f') then
+ res = res..acyellow.."\n\nADF: User Area"..acoff
+ for i=0, #tag.SEG do
+ res=res.."\n"..dumpSegment(tag, i).."\n"
+ end
+ end
+ return res
+end
+
+---
+-- dump tag-system area
+function dumpCDF(tag)
+ local res=""
+ local i=0
+ local raw=""
+ local bytes
+ if (istable(tag)) then
+ res = res..accyan.."MCD: "..acoff..tag.MCD..accyan.." MSN: "..acoff..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..accyan.." MCC: "..acoff..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..((tag.raw=='9f') and (", SSC="..tag.SSC.."\n") or "\n")
+
+ -- credential (end-user tag)
+ if (tag.Type=="SAM" and tag.raw=='9f') 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 specific
+ elseif (tag.Type~="SAM") then
+ res = res .."Master-Token Area\nStamp: "
+ res= res..tag.SSC.." "
+ for i=0, tag.Stamp_len-2 do
+ res = res..tag.data[i].." "
+ end
+ res=res.."\nunused payload\n"
+ for i=0, (#tag.data-tag.Stamp_len-1) do
+ res = res..tag.data[i].." "
+ end
+ bytes=tagToBytes(tag)
+ local mtcrc=calcMtCrc(bytes)
+ res=res.."\nMaster-Token CRC: "
+ res = res ..tag.MTC[1].." ("..((tag.MTC[1]==mtcrc) and "valid" or "error")..")"
+
+
+ -- 'Gantner User-Credential' specific
+ elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then
+ print(acgreen.."Gantner Detected"..acoff)
+ 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" and tag.raw=="9f") 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..accyan.."Segment "..("%02d"):format(tag.SEG[i].index)..acoff..": "
+ 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")..")"
+ res = res .."("..(checkSegmentCrc(tag, i) and acgreen.."valid" or acred.."error") ..acoff..")"
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].." "
+ if ((tag.SEG[i].WRC>0)) then
+ res = res .."\nWRC protected area:\n"
+ for i2=dp, dp+tag.SEG[i].WRC-1 do
+ res = res..tag.SEG[i].data[i2].." "
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].." "
+ res = res .."\nRemaining write protected area:\n"
+ for i2=dp, dp+(tag.SEG[i].WRP-tag.SEG[i].WRC)-1 do
+ res = res..tag.SEG[i].data[i2].." "
dp=dp+1
end
end
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")..")"
+ res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and acgreen.."valid" or acred.."error") ..acoff..")"
else res = res..tag.SEG[i].data[dp] end
end
dp=0
end
---
--- check all segmnet-crc
-function checkAllSegCrc(tag)
- for i=0, #tag.SEG do
- crc=calcSegmentCrc(tag, i)
- tag.SEG[i].crc=crc
+-- return bytes 'sstrat' to 'send' from a table
+function dumpTable(tab, header, tstart, tend)
+ res=""
+ for i=tstart, tend do
+ res=res..tab[i].." "
end
+ if (string.len(header)==0) then return res
+ else return (header.." #"..(tend-tstart+1).."\n"..res) 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
+-- dump 3rd Party Cash
+function dump3rdPartyCash1(tag , seg)
+ local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+ local stamp=tag.SEG[seg].data[0].." "..tag.SEG[seg].data[1].." "..tag.SEG[seg].data[2]
+ local datastamp=tag.SEG[seg].data[20].." "..tag.SEG[seg].data[21].." "..tag.SEG[seg].data[22]
+ local balance=tonumber(tag.SEG[seg].data[32]..tag.SEG[seg].data[33] ,16)
+ local balancecrc=utils.Crc8Legic(uid..tag.SEG[seg].data[32]..tag.SEG[seg].data[33])
+ local mirror=tonumber(tag.SEG[seg].data[35]..tag.SEG[seg].data[36] ,16)
+ local mirrorcrc=utils.Crc8Legic(uid..tag.SEG[seg].data[35]..tag.SEG[seg].data[36])
+ local lastbal0=tonumber(tag.SEG[seg].data[39]..tag.SEG[seg].data[40] ,16)
+ local lastbal1=tonumber(tag.SEG[seg].data[41]..tag.SEG[seg].data[42] ,16)
+ local lastbal2=tonumber(tag.SEG[seg].data[43]..tag.SEG[seg].data[44] ,16)
+
+ test=""
+ -- display decoded/known stuff
+ print("\n------------------------------")
+ print("Tag-ID:\t\t "..uid)
+ print("Stamp:\t\t "..stamp)
+ print("UID-Mapping: \t\t"..("%06d"):format(tonumber(tag.SEG[seg].data[46]..tag.SEG[seg].data[47]..tag.SEG[seg].data[48], 16)))
+ print("------------------------------")
+ print("checksum 1:\t\t "..tag.SEG[seg].data[31].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 19, 30)), tag.SEG[seg].data[31])..")")
+ print("checksum 2:\t\t "..tag.SEG[seg].data[34].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 32, 33)), tag.SEG[seg].data[34])..")")
+ print("checksum 3:\t\t "..tag.SEG[seg].data[37].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 35, 36)), tag.SEG[seg].data[37])..")")
+
+ print("checksum 4:\t\t "..tag.SEG[seg].data[55].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 46, 54)), tag.SEG[seg].data[55])..")")
+ print("checksum 5:\t\t "..tag.SEG[seg].data[62].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 56, 61)), tag.SEG[seg].data[62])..")")
+ print("checksum 6:\t\t "..tag.SEG[seg].data[73].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 63, 72)), tag.SEG[seg].data[73])..")")
+ print("checksum 7:\t\t "..tag.SEG[seg].data[89].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[seg].data, "", 74, 88)), tag.SEG[seg].data[89])..")")
+ print("------------------------------")
+ print(string.format("Balance:\t\t %3.2f", balance/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[34])..")")
+ print(string.format("Shadow:\t\t\t %3.2f", mirror/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[37])..")")
+ print("------------------------------")
+ print(string.format("History 1:\t\t %3.2f", lastbal0/100))
+ print(string.format("History 2:\t\t %3.2f", lastbal1/100))
+ print(string.format("History 3:\t\t %3.2f", lastbal2/100))
+ print("------------------------------\n")
+end
+
+---
+-- dump Legic-Cash data
+function dumpLegicCash(tag, x)
+ if (istable(tag.SEG[x])) then
+ io.write("in Segment "..tag.SEG[x].index.." :\n")
+ print("--------------------------------\n\tLegic-Cash Values\n--------------------------------")
+ local limit, curr, balance, rid, tcv
+ -- currency of balance & limit
+ curr=currency[tag.SEG[x].data[8]..tag.SEG[x].data[9]]
+ -- maximum balance
+ limit=string.format("%4.2f", tonumber(tag.SEG[x].data[10]..tag.SEG[x].data[11]..tag.SEG[x].data[12], 16)/100)
+ -- current balance
+ balance=string.format("%4.2f", tonumber(tag.SEG[x].data[15]..tag.SEG[x].data[16]..tag.SEG[x].data[17], 16)/100)
+ -- reader-id who wrote last transaction
+ rid=tonumber(tag.SEG[x].data[18]..tag.SEG[x].data[19]..tag.SEG[x].data[20], 16)
+ -- transaction counter value
+ tcv=tonumber(tag.SEG[x].data[29], 16)
+ print("Currency:\t\t "..curr)
+ print("Limit:\t\t\t "..limit)
+ print("Balance:\t\t "..balance)
+ print("Transaction Counter:\t "..tcv)
+ print("Reader-ID:\t\t "..rid.."\n--------------------------------\n")
+ end
+end
+
+---
+-- raw 3rd-party
+function print3rdPartyCash1(tag, x)
+ if (istable(tag.SEG[x])) then
+ local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+ print("\n\t\tStamp : "..dumpTable(tag.SEG[x].data, "", 0 , 2))
+ print("\t\tBlock 0: "..dumpTable(tag.SEG[x].data, "", 3 , 18))
+ print()
+ print("\t\tBlock 1: "..dumpTable(tag.SEG[x].data, "", 19, 30))
+ print("checksum 1: Tag-ID .. Block 1 => LegicCrc8 = "..tag.SEG[x].data[31].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 19, 30)), tag.SEG[x].data[31])..")")
+ print()
+ print("\t\tBlock 2: "..dumpTable(tag.SEG[x].data, "", 32, 33))
+ print("checksum 2: Block 2 => LegicCrc8 = "..tag.SEG[x].data[34].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 32, 33)), tag.SEG[x].data[34])..")")
+ print()
+ print("\t\tBlock 3: "..dumpTable(tag.SEG[x].data, "", 35, 36))
+ print("checksum 3: Block 3 => LegicCrc8 = "..tag.SEG[x].data[37].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 35, 36)), tag.SEG[x].data[37])..")")
+ print()
+ print("\t\tyet unknown: "..tag.SEG[x].data[38])
+ print()
+ print("\t\tHisatory 1: "..dumpTable(tag.SEG[x].data, "", 39, 40))
+ print("\t\tHisatory 2: "..dumpTable(tag.SEG[x].data, "", 41, 42))
+ print("\t\tHisatory 3: "..dumpTable(tag.SEG[x].data, "", 43, 44))
+ print()
+ print("\t\tyet unknown: "..tag.SEG[x].data[45])
+ print()
+ print("\t\tKGH-UID HEX: "..dumpTable(tag.SEG[x].data, "", 46, 48))
+ print("\t\tBlock 4: "..dumpTable(tag.SEG[x].data, "", 49, 54))
+ print("checksum 4: Tag-ID .. KGH-UID .. Block 4 => LegicCrc8 = "..tag.SEG[x].data[55].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 46, 54)), tag.SEG[x].data[55])..")")
+ print()
+ print("\t\tBlock 5: "..dumpTable(tag.SEG[x].data, "", 56, 61))
+ print("checksum 5: Tag-ID .. Block 5 => LegicCrc8 = "..tag.SEG[x].data[62].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 56, 61)), tag.SEG[x].data[62])..")")
+ print()
+ print("\t\tBlock 6: "..dumpTable(tag.SEG[x].data, "", 63, 72))
+ print("checksum 6: Tag-ID .. Block 6 => LegicCrc8 = "..tag.SEG[x].data[73].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 63, 72)), tag.SEG[x].data[73])..")")
+ print()
+ print("\t\tBlock 7: "..dumpTable(tag.SEG[x].data, "", 74, 88))
+ print("checksum 7: Tag-ID .. Block 7 => LegicCrc8 = "..tag.SEG[x].data[89].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(tag.SEG[x].data, "", 74, 88)), tag.SEG[x].data[89])..")")
+ print()
+ print("\t\tBlock 8: "..dumpTable(tag.SEG[x].data, "", 90, 94))
end
end
+--- Token related --
---
--- 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"
+-- make token
+function makeToken()
+ local mt={
+ ['Type'] = {"SAM", "SAM63", "SAM64", "IAM", "GAM"},
+ ['DCF'] = {"60ea", "31fa", "30fa", "80fa", "f0fa"},
+ ['WRP'] = {"15", "2", "2", "2", "2"},
+ ['WRC'] = {"01", "02", "02", "00", "00"},
+ ['RD'] = {"01", "00", "00", "00", "00"},
+ ['Stamp'] = {"00", "00", "00", "00", "00"},
+ ['Segment'] = {"0d", "c0", "04", "00", "be", "01", "02", "03", "04", "01", "02", "03", "04"}
+ }
+ ttype=""
+ for k, v in pairs(mt.Type) do
+ ttype=ttype..k..") "..v.." "
+ end
+ mtq=tonumber(input("select number for Token-Type\n"..ttype, '1'), 10)
+ if (type(mtq)~="number") then return print("selection invalid!")
+ elseif (mtq>#mt.Type) then return print("selection invalid!")
+ else print("Token-Type '"..mt.Type[mtq].."' selected") end
+ local raw=calcHeaderRaw(mt.WRP[mtq], mt.WRC[mtq], mt.RD[mtq])
+ local mtCRC="00"
+
+ bytes={"01", "02", "03", "04", "cb", string.sub(mt.DCF[mtq], 0, 2), string.sub(mt.DCF[mtq], 3), raw,
+ "00", "00", "00", "00", "00", "00", "00", "00",
+ "00", "00", "00", "00", "00", "00"}
+ if (mtq==1) then
+ for i=0, #mt.Segment do
+ table.insert(bytes, mt.Segment[i])
end
+ bytes[9]="ff"
end
+ -- fill bytes
+ for i=#bytes, 1023 do table.insert(bytes, "00") end
+ -- if Master-Token -> calc Master-Token-CRC
+ if (mtq>1) then bytes[22]=calcMtCrc(bytes) end
+ local tempTag=createTagTable()
+ -- remove segment if MasterToken
+ if (mtq>1) then tempTag.SEG[0]=nil end
+ return bytesToTag(bytes, tempTag)
+end
+
+---
+-- edit token-data
+function editTag(tag)
+ -- for simulation it makes sense to edit everything
+ local edit_sim="MCD MSN0 MSN1 MSN2 MCC DCFl DCFh WRP WRC RD"
+ -- on real tags it makes only sense to edit DCF, WRP, WRC, RD
+ local edit_real="DCFl DCFh WRP WRC RD"
+ if (confirm(acyellow.."do you want to edit non-writeable values (e.g. for simulation)?"..acoff)) then
+ edit_tag=edit_sim
+ else edit_tag=edit_real end
+
+ if(istable(tag)) then
+ for k,v in pairs(tag) do
+ if(type(v)~="table" and type(v)~="boolean" and string.find(edit_tag, k)) then
+ tag[k]=input("value for: "..accyan..k..acoff, v)
+ end
+ end
+
+ if (tag.Type=="SAM") then ttype="Header"; else ttype="Stamp"; end
+ if (confirm(acyellow.."do you want to edit "..ttype.." Data?"..acoff)) then
+ -- master-token specific
+ if(istable(tag.Bck)==false) then
+ -- stamp-data length=(0xfc-DCFh)
+ -- on MT: SSC holds the Starting Stamp Character (Stamp0)
+ tag.SSC=input(ttype.."0: ", tag.SSC)
+ -- rest of stamp-bytes are in tag.data 0..n
+ for i=0, (tonumber(0xfc ,10)-("%d"):format('0x'..tag.DCFh))-2 do
+ tag.data[i]=input(ttype.. i+1 ..": ", tag.data[i])
+ end
+ else
+ --- on credentials byte7 should always be 9f and byte8 ff
+ -- on Master-Token not (even on SAM63/64 not)
+ -- tag.SSC=input(ttype.."0: ", tag.SSC)
+ for i=0, #tag.data do
+ tag.data[i]=input(ttype.. i ..": ", tag.data[i])
+ end
+ end
+ end
+
+ bytes=tagToBytes(tag)
+
+ --- check data-consistency (calculate tag.raw)
+ bytes[8]=calcHeaderRaw(tag.WRP, tag.WRC, tag.RD)
+
+ --- Master-Token specific
+ -- should be triggered if a SAM was converted to a non-SAM (user-Token to Master-Token)
+ -- or a Master-Token has being edited (also SAM64 & SAM63 - which are in fact Master-Token)
+ if(tag.Type~="SAM" or bytes[6]..bytes[7]~="60ea") then
+ -- calc new Master-Token crc
+ bytes[22]=calcMtCrc(bytes)
+ else
+ -- ensure tag.SSC set to 'ff' on credential-token (SAM)
+ bytes[9]='ff'
+ -- if a Master-Token was converted to a Credential-Token
+ -- lets unset the Time-Area to 00 00 (will contain Stamp-Data on MT)
+ bytes[21]='00'
+ bytes[22]='00'
+ end
+
+ tag=bytesToTag(bytes, tag)
+ end
+end
+
+---
+-- calculates header-byte (addr 0x07)
+function calcHeaderRaw(wrp, wrc, rd)
+ local res
+ wrp=("%02x"):format(tonumber(wrp, 10))
+ rd=tonumber(rd, 16)
+ res=("%02x"):format(tonumber(wrp, 16)+tonumber(wrc.."0", 16)+((rd>0) and tonumber("8"..(rd-1), 16) or 0))
return res
end
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
+-- clear beackup-area of a virtual tag
+function clearBackupArea(tag)
+ for i=1, #tag.Bck do
+ tag.Bck[i]='00'
end
- return seg
+ return tag
end
+--- Segment related --
---
-- get segmemnt-data from byte-table
function getSegmentData(bytes, start, index)
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
+ return segment
+ else return false;
+ end
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))
+-- get index, start-aadr, length and content
+function getSegmentStats(bytes)
+ local sStats = {}
+ local sValid, sLast, sLen, sStart, x
+ sStart=23
+ x=0
+ repeat
+ local s={}
+ -- valid = bit 6 of byte 1
+ sValid = bbit("0x"..bytes[sStart+1],6,1)
+ -- last = bit 7 of byte 1
+ sLast = bbit("0x"..bytes[sStart+1],7,1)
+ -- len = (byte 0)+(bit0-3 of byte 1)
+ sLen = tonumber(bbit("0x"..bytes[sStart+1],0,4)..bytes[sStart],16)
+ --print("index: "..("%02d"):format(x).." Len: "..sLen.." start:"..sStart.." end: "..(sStart+sLen-1))
+ s['index']=x
+ s['start']=sStart
+ s['end']=sStart+sLen-1
+ s['len']=sLen
+ if ( (sStart+sLen-1)>sStart ) then
+ table.insert(sStats, s)
+ end
+ sStart=sStart+sLen
+ x=x+1
+ until (sLast==1 or sValid==0 or x==126)
+ if (#sStats>0 ) then return sStats
+ else return false; end
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
+-- 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),8,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(tonumber((seg.WRC*16)+(seg.RD*128),10))
+ -- 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(acyellow.."Data-Length has being reduced:"..acgreen.." auto-removing "..acyellow.. #seg.data-(seg.len-5) ..acgreen .." bytes from Payload!"..acoff);
+ 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(acyellow.."Data-Length has being extended:"..acgreen.." auto-adding "..acyellow..(seg.len-5)-#seg.data ..acgreen .." bytes to Payload!"..acoff);
+ for i=#seg.data, (seg.len-5-1) do
+ table.insert(seg.data, '00')
+ end
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
+ return seg
end
---
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)
+ tag.SEG[index][k]=tonumber(input(accyan..k..acoff..": ", v),10)
end
end
else print("Segment with Index: "..("%02d"):format(index).." not found in Tag")
---
-- edit Segment Data
function editSegmentData(data)
+ local lc=check4LegicCash(data)
+ io.write("\n")
if (istable(data)) then
for i=0, #data-1 do
- data[i]=input("Data"..i..": ", data[i])
+ data[i]=input(accyan.."Data"..i..acoff..": ", data[i])
end
+ if (lc) then data=fixLegicCash(data) end
return data
else
print("no Segment-Data found")
-- helper to selecting a segment
function selectSegment(tag)
local sel
- if (istable(tag)) then
+ if (istable(tag.SEG[0])) then
print("availabe Segments:\n"..segmentList(tag))
sel=input("select Segment: ", '00')
sel=tonumber(sel,10)
return false
end
end
-
+
---
-- add segment
function addSegment(tag)
end
---
---
+-- get only the stamp-bytes of a segment
+function getSegmentStamp(seg, bytes)
+ local stamp=""
+ local stamp_len=7
+ --- the 'real' stamp on MIM is not really easy to tell for me since the 'data-block' covers stamp0..stampn+data0..datan
+ -- there a no stamps longer than 7 bytes & they are write-protected by default , and I have not seen user-credntials
+ -- with stamps smaller 3 bytes (except: Master-Token)
+ -- WRP -> Read/Write Protection
+ -- WRC -> Read/Write Condition
+ -- RD depends on WRC - if WRC > 0 and RD=1: only reader with matching #WRC of Stamp-bytes in thier Database have Read-Access to the Tag
+ if (seg.WRP<7) then stamp_len=(seg.WRP) end
+ for i=1, (stamp_len) do
+ stamp=stamp..seg.data[i-1]
+ end
+ if (bytes) then
+ stamp=str2bytes(stamp)
+ return stamp
+ else return stamp end
+end
+
+---
+-- edit stamp of a segment
+function editStamp(new_stamp, uid, data)
+ local stamp=str2bytes(new_stamp)
+ for i=0, #stamp-1 do
+ data[i]=stamp[i+1]
+ end
+ -- now fill in stamp
+ for i=0, (string.len(new_stamp)/2)-1 do
+ data[i]=stamp[i+1]
+ end
+ return fix3rdPartyCash1(uid, data)
+end
+
+---
+-- autoselect special/known segments
+function autoSelectSegment(tag, s)
+ local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+ local x=#tag.SEG+1
+ local res = false
+ io.write("autoSelect ")
+ --- search for desired segment-type
+ -- 3rd Party Segment
+ if (s=="3rdparty") then
+ repeat
+ io.write(". ")
+ x=x-1
+ res=check43rdPartyCash1(uid, tag.SEG[x].data)
+ until ( res or x==0 )
+ end
+ -- Legic-Cash Segment
+ if (s=="legiccash") then
+ repeat
+ io.write(". ")
+ x=x-1
+ res=check4LegicCash(tag.SEG[x].data)
+ until ( res or x==0 )
+ end
+ ---
+ -- segment found
+ if (res) then
+ io.write("\nautoselected Index: "..string.format("%02d", x).."\n")
+ return x
+ end
+ ---
+ -- nothing found
+ io.write(acyellow.."no Segment found\n"..acoff)
+ return -1
+end
+
+---
+-- delete segment (except segment 00)
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)
+ if (istable(tag.SEG[0])) then
+ 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
+end
+
+---
+-- edit uid 3rd party cash
+function edit3rdUid(mapid, uid, data)
+ mapid=("%06x"):format(tonumber(mapid, 10))
+ data[46]=string.sub(mapid, 1 ,2)
+ data[47]=string.sub(mapid, 3 ,4)
+ data[48]=string.sub(mapid, 5 ,6)
+ return fix3rdPartyCash1(uid, data)
+end
+
+---
+-- edit balance 3rd party cash
+function edit3rdCash(new_cash, uid, data)
+ new_cash=("%04x"):format(new_cash)
+ data[32]=string.sub(new_cash, 0,2)
+ data[33]=string.sub(new_cash, 3,4)
+ data[34]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
+ data[35]=string.sub(new_cash, 0,2)
+ data[36]=string.sub(new_cash, 3,4)
+ data[37]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
+ data[39]=string.sub(new_cash, 0,2)
+ data[40]=string.sub(new_cash, 3,4)
+ data[41]='00'
+ data[42]='00'
+ data[43]='00'
+ data[44]='00'
+ return fix3rdPartyCash1(uid, data)
+end
+
+---
+-- edit 3rd-party cash
+function edit3rdPartyCash1(tag, x)
+ local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
+ if (confirm("\nedit Balance?")) then
+ local new_cash=input("enter new Balance≤\nwithout comma and without currency-sign! (0-65535)", "100")
+ tag.SEG[x].data=edit3rdCash(new_cash, uid, tag.SEG[x].data)
+ end
+ -- change User-ID (used for online-account-mapping)
+ if (confirm("\nedit UserID-Mapping?")) then
+ local new_mapid=input("enter new UserID (6-digit value)", "012345")
+ tag.SEG[x].data=edit3rdUid(new_mapid, uid, tag.SEG[x].data)
+ end
+ if (confirm("\nedit Stamp?")) then
+ local new_stamp=input("enter new Stamp", getSegmentStamp(tag.SEG[x]))
+ tag.SEG[x].data=editStamp(new_stamp, uid, tag.SEG[x].data)
+ new_stamp=getSegmentStamp(tag.SEG[x], 'true')
+ print("stamp_bytes: "..#new_stamp)
+ -- replace stamp in 'block 1' also
+ io.write("editing stamp in Block 1 also ")
+ for i=20, (20+#new_stamp-1) do
+ tag.SEG[x].data[i]=new_stamp[i-19]
+ io.write(".");
+ end
+ print(" done")
+ -- fix known checksums
+ tag.SEG[x].data=fix3rdPartyCash1(uid, tag.SEG[x].data)
+ end
+ return tag
+end
+
+---
+-- edit Legic Cash
+function editLegicCash(data)
+ local limit, curr, balance, rid, tcv
+ -- currency of balance & limit
+ curr=currency[data[8]..data[9]]
+ -- maximum balance
+ limit=string.format("%4.2f", tonumber(data[10]..data[11]..data[12], 16)/100)
+ -- current balance
+ balance=string.format("%4.2f", tonumber(data[15]..data[16]..data[17], 16)/100)
+ -- reader-id who wrote last transaction
+ rid=tonumber(data[18]..data[19]..data[20], 16)
+ -- transaction counter value
+ tcv=tonumber(data[29], 16)
+
+ -- edit currency
+ if (confirm(accyan.."change Currency?"..acoff)) then
+ for k, v in pairs(currency) do io.write(k .. " = " .. v .. "\n") end
+ curr=input(accyan.."enter the 4-digit Hex for the new Currency:"..acoff, data[8]..data[9])
+ data[8]=string.sub(curr, 1, 2)
+ data[9]=string.sub(curr, 3, 4)
+ end
+
+ -- edit limit
+ if (confirm(accyan.."change Limit?"..acoff)) then
+ limit=string.format("%06x", input(accyan.."enter the Decimal for the new Limit:"..acoff, limit))
+ data[10]=string.sub(limit, 1, 2)
+ data[11]=string.sub(limit, 3, 4)
+ data[12]=string.sub(limit, 5, 6)
+ end
+
+ -- edit balance
+ if (confirm(accyan.."change Balance?"..acoff)) then
+ balance=string.format("%06x", input(accyan.."enter the Decimal for the new Balance:"..acoff, balance))
+ print("Balance: "..balance)
+ data[15]=string.sub(balance, 1, 2)
+ data[16]=string.sub(balance, 3, 4)
+ data[17]=string.sub(balance, 5, 6)
+ end
+
+ -- edit transaction-counter
+ if (confirm(accyan.."change Transaction-Counter?"..acoff)) then
+ tcv=string.format("%02x", input(accyan.."enter the 4-digit Hex for the new Currency:"..acoff, data[29]))
+ data[29]=tcv
+ end
+
+ -- edit reader.id
+ if (confirm(accyan.."change Last-Reader-ID?"..acoff)) then
+ rid=string.format("%06x", input(accyan.."enter the Decimal for the new Balance:"..acoff, rid))
+ print("Balance: "..balance)
+ data[18]=string.sub(rid, 1, 2)
+ data[19]=string.sub(rid, 3, 4)
+ data[20]=string.sub(rid, 5, 6)
+ end
+
+ return fixLegicCash(data)
+end
+
+---
+-- chack for signature of a 'Legic-Cash-Segment'
+function check4LegicCash(data)
+ if(#data==32) then
+ local stamp_len=(#data-25)
+ local stamp=""
+ for i=0, stamp_len-1 do
+ stamp=stamp..data[i].." "
+ end
+ if (data[7]=="01") then
+ if (("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12))) == data[13]..data[14]) then
+ if (("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20))) == data[21]..data[22]) then
+ if (("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29))) == data[30]..data[31]) then
+ io.write(accyan.."Legic-Cash Segment detected "..acoff)
+ return true
+ end
+ end
+ end
+ end
+ end
+ return false
+end
+
+---
+-- chack for signature of a '3rd Party Cash-Segment' - not all bytes know until yet !!
+function check43rdPartyCash1(uid, data)
+ if(#data==95) then
+ -- too explicit checking will avoid fixing ;-)
+ if (string.find(compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)), data[31]),"valid")) then
+ --if (compareCrc(utils.Crc8Legic(uid..data[32]..data[33]), data[34])=="valid") then
+ --if (compareCrc(utils.Crc8Legic(uid..data[35]..data[36]), data[37])=="valid") then
+ --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)), data[62])=="valid") then
+ --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)), data[89])=="valid") then
+ io.write(accyan.."3rd Party Cash-Segment detected "..acoff)
+ return true
+ --end
+ --end
+ --end
+ --end
+ end
+ end
+ return false
+end
+
+--- CRC related ---
+---
+-- build segmentCrc credentials
+function segmentCrcCredentials(tag, segid)
+ if (istable(tag.SEG[0])) then
+ 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
+ else return print(acyellow.."Master-Token / unsegmented Tag!"..acoff) end
+end
+
+---
+-- build kghCrc credentials
+function kghCrcCredentials(tag, segid)
+ if (istable(tag) and istable(tag.SEG[0])) then
+ 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
+end
+
+---
+-- compare two bytes
+function compareCrc(calc, guess)
+ calc=("%02x"):format(calc)
+ if (calc==guess) then return acgreen.."valid"..acoff
+ else return acred.."error "..acoff..calc.."!="..guess end
+end
+
+---
+-- compare 4 bytes
+function compareCrc16(calc, guess)
+ calc=("%04x"):format(calc)
+ if (calc==guess) then return acgreen.."valid"..acoff
+ else return acred.."error "..acoff..calc.."!="..guess end
+end
+
+---
+-- repair / fix crc's of a 'Legic-Cash-Segment'
+function fixLegicCash(data)
+ if(#data==32 and data[7]=="01") then
+ local crc1, crc2, crc3
+ -- set shadow-balance equal to balance
+ data[23]=data[15]
+ data[24]=data[16]
+ data[25]=data[17]
+ -- set shadow-last-reader to last-reader
+ data[26]=data[18]
+ data[27]=data[19]
+ data[28]=data[20]
+ -- calculate all crc's
+ crc1=("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12)))
+ crc2=("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20)))
+ crc3=("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29)))
+ -- set crc's
+ data[13]=string.sub(crc1, 1, 2)
+ data[14]=string.sub(crc1, 3, 4)
+ data[21]=string.sub(crc2, 1, 2)
+ data[22]=string.sub(crc2, 3, 4)
+ data[30]=string.sub(crc3, 1, 2)
+ data[31]=string.sub(crc3, 3, 4)
+ return data
+ end
+end
+
+---
+-- repair / fix (yet known) crc's of a '3rd Party Cash-Segment' - not all bytes know until yet !!
+function fix3rdPartyCash1(uid, data)
+ if(#data==95) then
+ -- checksum 1
+ data[31]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)))
+ -- checksum 2
+ data[34]=("%02x"):format(utils.Crc8Legic(uid..data[32]..data[33]))
+ -- checksum 3
+ data[37]=("%02x"):format(utils.Crc8Legic(uid..data[35]..data[36]))
+ -- checksum 4
+ data[55]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 46, 54)))
+ -- checksum 5
+ data[62]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)))
+ -- checksum 6
+ data[73]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 63, 72)))
+ -- checksum 7
+ data[89]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)))
+ return data
+ end
+end
+
+---
+-- calculate Master-Token crc
+function calcMtCrc(bytes)
+ --print(#bytes)
+ local cmd=bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[7]..bytes[6]..bytes[8]
+ local len=(tonumber(0xfc ,10)-("%d"):format('0x'..bytes[7]))
+ for i=1, len do
+ cmd=cmd..bytes[8+i]
+ end
+ local res=("%02x"):format(utils.Crc8Legic(cmd))
+ return res
+end
+
+---
+-- calculate segmentCRC for a given segment
+function calcSegmentCrc(tag, segid)
+ if (istable(tag.SEG[0])) then
+ -- check if a 'Kaber Group Header' exists
+ local data=segmentCrcCredentials(tag, segid)
+ return ("%02x"):format(utils.Crc8Legic(data))
+ end
+end
+
+---
+-- calcuate kghCRC for a given segment
+function calcKghCrc(tag, segid)
+ if (istable(tag.SEG[0])) then
+ -- check if a 'Kaber Group Header' exists
+ local i
+ local data=kghCrcCredentials(tag, segid)
+ return ("%02x"):format(utils.Crc8Legic(data))
+ end
+end
+
+---
+-- check all segmnet-crc
+function checkAllSegCrc(tag)
+ if (istable(tag.SEG[0])) then
+ for i=0, #tag.SEG do
+ crc=calcSegmentCrc(tag, i)
+ tag.SEG[i].crc=crc
+ end
+ else return print(acyellow.."Master-Token / unsegmented Tag"..acoff) end
+end
+
+---
+-- check all segmnet-crc
+function checkAllKghCrc(tag)
+ if (istable(tag.SEG[0])) then
for i=0, #tag.SEG do
- tag.SEG[i].index=("%02d"):format(i)
+ crc=calcKghCrc(tag, i)
+ if (tag.SEG[i].kgh) then
+ tag.SEG[i].data[#tag.SEG[i].data-1]=crc
+ end
end
end
- if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
- return tag
+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
+
+---
+-- 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(acred.."'Kaba Group header' detected but no Segment-Data found"..ansocolors.reset) end
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
- ]]
+
+ Data I/O Segment Manipulation Token-Data
+ ----------------- -------------------- ---------------------
+ rt => read Tag as => add Segment mt => make Token
+ wt => write Tag es => edit Segment Header et => edit Token data
+ ed => edit Segment Data tk => toggle KGH-Flag
+ File I/O rs => remove Segment
+ ----------------- cc => check Segment-CRC
+ lf => load File ck => check KGH
+ sf => save File ds => dump Segments
+ xf => xor to File
+
+
+ Virtual Tags tagMap (partial) known Segments
+ -------------------------------- --------------------- ---------------------------
+ ct => copy mainTag to backupTag mm => make (new) Map dlc => dump Legic-Cash
+ tc => copy backupTag to mainTag em => edit Map submenu elc => edit Legic-Cash
+ tt => switch mainTag & backupTag lm => load map from file d3p => dump 3rd-Party-Cash
+ di => dump mainTag sm => save map to file e3p => edit 3rd-Party-Cash
+ do => dump backupTag
+
+ h => this help q => quit
+ ]]
return t
end
---
-- modify Tag (interactive)
function modifyMode()
- local i, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes
+ local i, backupTAG, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes, tagMap
+
actions = {
+ ---
+ -- helptext
["h"] = function(x)
- print(modifyHelp().."\n".."tags im Memory:"..(istable(inTAG) and " inTAG" or "")..(istable(outTAG) and " outTAG" or ""))
+ print(" Version: "..version);
+ print(modifyHelp().."\n".."tags im Memory: "..(istable(inTAG) and ((currentTag=='inTAG') and acgreen.."*mainTAG"..acoff or "mainTAG") or "").." "..(istable(backupTAG) and ((currentTag=='backupTAG') and acgreen.."*backupTAG"..acoff or "backupTAG") or ""))
end,
- ["rt"] = function(x) inTAG=readFromPM3(); actions['di']('') end,
+ ---
+ -- read real Tag with PM3 into virtual 'mainTAG'
+ ["rt"] = function(x)
+ inTAG=readFromPM3();
+ --actions.di()
+ end,
+ ---
+ -- write content of virtual 'mainTAG' to real Tag with PM3
["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
+ writeToTag(inTAG)
end,
+ ---
+ -- copy mainTAG to backupTAG
["ct"] = function(x)
- print("copy virtual input-TAG to output-TAG")
- outTAG=inTAG
+ print(accyan.."copy mainTAG to backupTAG"..acoff)
+ backupTAG=deepCopy(inTAG)
end,
+ ---
+ -- copy backupTAG to mainTAG
["tc"] = function(x)
- print("copy virtual output-TAG to input-TAG")
- inTAG=outTAG
+ print(accyan.."copy backupTAG to mainTAG"..acoff)
+ inTAG=deepCopy(backupTAG)
end,
+ ---
+ -- toggle between mainTAG and backupTAG
+ ["tt"] = function(x)
+ -- copy main to temp
+ outTAG=deepCopy(inTAG)
+ -- copy backup to main
+ inTAG=deepCopy(backupTAG)
+ print(accyan.."toggle to "..accyan..((currentTag=='inTAG') and "backupTAG" or "mainTAG")..acoff)
+ if(currentTag=="inTAG") then currentTag='backupTAG'
+ else currentTag='inTAG' end
+ -- copy temp (main) to backup
+ backupTAG=deepCopy(outTAG)
+ end,
+ ---
+ -- load file into mainTAG
["lf"] = function(x)
- filename=input("enter filename: ", "legic.temp")
+
+ if (type(x)=='string' and file_check(x)) then filename=x
+ else filename=input("enter filename: ", "legic.temp") end
inTAG=readFile(filename)
+ -- check for existing tagMap
+ if (file_check(filename..".map")) then
+ if(confirm(accyan.."Mapping-File for "..acoff..filename..accyan.." found - load it also?"..acoff)) then
+ tagMap=loadTagMap(filename..".map")
+ end
+ end
end,
+ ---
+ -- save values of mainTAG to a file (xored with MCC of mainTAG)
["sf"] = function(x)
if(istable(inTAG)) then
outfile=input("enter filename:", "legic.temp")
end
end
end,
+ ---
+ -- save values of mainTAG to a file (xored with 'specific' MCC)
["xf"] = function(x)
if(istable(inTAG)) then
outfile=input("enter filename:", "legic.temp")
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,
+ ---
+ -- dump mainTAG (and all Segments)
+ ["di"] = function(x)
+ if (istable(inTAG)) then
+ local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+ if(istable(inTAG.SEG[0])) then
+ for i=0, #inTAG.SEG do
+ if(check43rdPartyCash1(uid, inTAG.SEG[i].data)) then
+ io.write(accyan.."in Segment index: "..inTAG.SEG[i].index ..acoff.. "\n")
+ elseif(check4LegicCash(inTAG.SEG[i].data)) then
+ io.write(accyan.."in Segment index: "..inTAG.SEG[i].index..acoff.."\n")
+ lc=true;
+ lci=inTAG.SEG[i].index;
+ end
+ end
+ end
+ print("\n"..dumpTag(inTAG).."\n")
+ if (lc) then actions["dlc"](lci) end
+ lc=false
+ end
+ end,
+ ---
+ -- dump backupTAG (and all Segments)
+ ["do"] = function(x) if (istable(backupTAG)) then print("\n"..dumpTag(backupTAG).."\n") end end,
+ ---
+ -- create a empty tagMap
+ ["mm"] = function(x)
+ -- clear existing tagMap and init
+ if (istable(inTAG)) then
+ tagMap=makeTagMap()
+ end
+ end,
+ ---
+ -- edit a tagMap
+ ["em"] = function(x)
+ if (istable(inTAG)==false) then
+ if (confirm("no mainTAG in memory!\nread from PM3?")) then
+ actions['rt']()
+ elseif (confirm("load from File?")) then
+ actions['lf']()
+ else return
+ end
+ end
+ if (istable(tagMap)==false) then actions['mm']() end
+ -- edit
+ tagMap=editTagMap(inTAG, tagMap)
+ end,
+ ---
+ -- save a tagMap
+ ["sm"] = function(x)
+ if (istable(tagMap)) then
+ if (istable(tagMap) and #tagMap.mappings>0) then
+ print(accyan.."Map contains "..acoff..#tagMap..accyan.." mappings"..acoff)
+ saveTagMap(tagMap, input(accyan.."enter filename:"..acoff, "Legic.map"))
+ else
+ print(acyellow.."no mappings in tagMap!"..acoff)
+ end
+ end
+ end,
+ ---
+ -- load a tagMap
+ ["lm"] = function(x)
+ tagMap=loadTagMap(input(accyan.."enter filename:"..acoff, "Legic.map"))
+ end,
+ ---
+ -- dump single segment
["ds"] = function(x)
- sel=selectSegment(inTAG)
- if (sel) then print("\n"..(dumpSegment(inTAG, sel) or "no Segments available").."\n") end
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
+ if (sel) then print("\n"..(dumpSegment(inTAG, sel) or acred.."no Segments available") ..acoff.."\n") end
end,
+ ---
+ -- edit segment header
["es"] = function(x)
- sel=selectSegment(inTAG)
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
if (sel) then
- if(istable(inTAG.SEG)) then
+ if(istable(inTAG.SEG[0])) then
inTAG=editSegment(inTAG, sel)
inTAG.SEG[sel]=regenSegmentHeader(inTAG.SEG[sel])
- else print("no Segments in Tag") end
+ else print(acyellow.."no Segments in Tag"..acoff) end
end
end,
+ ---
+ -- add segment
["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!")
+ else print(accyan.."Master-Token / unsegmented Tag!"..acoff)
end
end,
+ ---
+ -- remove segment
["rs"] = function(x)
- if (istable(inTAG)) then
- sel=selectSegment(inTAG)
+ if (istable(inTAG.SEG[0])) then
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
inTAG=delSegment(inTAG, sel)
for i=0, #inTAG.SEG do
inTAG.SEG[i]=regenSegmentHeader(inTAG.SEG[i])
end
end
end,
+ ---
+ -- edit data-portion of single segment
["ed"] = function(x)
- sel=selectSegment(inTAG)
- if (sel) then
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
+ if (istable(inTAG.SEG[sel])) then
inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data)
end
end,
- ["ts"] = function(x)
- sel=selectSegment(inTAG)
+ ---
+ -- edit Tag (MCD, MSN, MCC etc.)
+ ["et"] = function(x)
+ if (istable(inTAG)) then
+ editTag(inTAG)
+ end
+ end,
+ ---
+ -- make (dummy) Token
+ ["mt"] = function(x) inTAG=makeToken(); actions.di() end,
+ ---
+ -- fix segment-crc on single segment
+ ["ts"] = function(x)
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
regenSegmentHeader(inTAG.SEG[sel])
end,
- ["tk"] = function(x)
- sel=selectSegment(inTAG)
+ ---
+ -- toggle kgh-crc-flag on a single segment
+ ["tk"] = function(x)
+ if (istable(inTAG) and istable(inTAG.SEG[0])) then
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false
else inTAG.SEG[sel].kgh=true end
+ end
+ end,
+ ---
+ -- calculate LegicCrc8
+ ["k"] = function(x)
+ if (type(x)=="string" and string.len(x)>0) then
+ print(("%02x"):format(utils.Crc8Legic(x)))
+ end
+ end,
+ ---
+ -- noop
+ ["xb"] = function(x)
+ end,
+ ---
+ -- print string for LegicCrc8-calculation about single segment
+ ["xc"] = function(x)
+ if (istable(inTAG) and istable(inTAG.SEG[0])) then
+ if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
+ else sel=selectSegment(inTAG) end
+ print("k "..kghCrcCredentials(inTAG, sel))
+ end
+ end,
+ ---
+ -- fix legic-cash checksums
+ ["flc"] = function(x)
+ if (type(x)=="string" and string.len(x)>0) then x=tonumber(x,10)
+ else x=selectSegment(inTAG) end
+ inTAG.SEG[x].data=fixLegicCash(inTAG.SEG[x].data)
+ end,
+ ---
+ -- edit legic-cash values fixLegicCash(data)
+ ["elc"] = function(x)
+ x=autoSelectSegment(inTAG, "legiccash")
+ inTAG.SEG[x].data=editLegicCash(inTAG.SEG[x].data)
+ end,
+ ---
+ -- dump legic-cash human-readable
+ ["dlc"] = function(x)
+ -- if segment index was user defined
+ if (type(x)=="string" and string.len(x)>0) then
+ x=tonumber(x,10)
+ print(string.format("User-Selected Index %02d", x))
+ -- or try to find match
+ else x=autoSelectSegment(inTAG, "legiccash") end
+ -- dump it
+ dumpLegicCash(inTAG, x)
+ end,
+ ---
+ -- dump 3rd-party-cash-segment
+ ["d3p"] = function(x)
+ local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+ -- if segment index was user defined
+ if (type(x)=="string" and string.len(x)>0) then
+ x=tonumber(x,10)
+ print(string.format("User-Selected Index %02d", x))
+ -- or try to find match
+ else x=autoSelectSegment(inTAG, "3rdparty") end
+ if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
+ uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+ if (check43rdPartyCash1(uid, inTAG.SEG[x].data)) then
+ dump3rdPartyCash1(inTAG, x)
+ end
+ end
+ end,
+ ---
+ -- dump 3rd-party-cash-segment (raw blocks and checksums over 'known areas')
+ ["r3p"] = function(x)
+ local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+ -- if segment index was user defined
+ if (type(x)=="string" and string.len(x)>0) then
+ x=tonumber(x,10)
+ print(string.format("User-Selected Index %02d", x))
+ -- or try to find match
+ else x=autoSelectSegment(inTAG, "3rdparty") end
+ print3rdPartyCash1(inTAG, x)
+ end,
+ ---
+ -- edit 3rd-party-cash-segment values (Balance, Mapping-UID, Stamp)
+ ["e3p"] = function(x)
+ local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+ -- if segment index was user defined
+ if (type(x)=="string" and string.len(x)>0) then
+ x=tonumber(x,10)
+ print(string.format("User-Selected Index %02d", x))
+ -- or try to find match
+ else x=autoSelectSegment(inTAG, "3rdparty") end
+ if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
+ inTAG=edit3rdPartyCash1(inTAG, x)
+ dump3rdPartyCash1(inTAG, x)
+ end
+ end,
+ ---
+ -- force fixing 3rd-party-checksums
+ ["f3p"] = function(x)
+ if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
+ else x=selectSegment(inTAG) end
+ if (istable(inTAG.SEG[x])) then
+ local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
+ inTAG.SEG[x].data=fix3rdPartyCash1(uid, inTAG.SEG[x].data)
+ end
end,
- ["k"] = function(x)
- print(("%02x"):format(utils.Crc8Legic(x)))
+ ---
+ -- get stamp from single segment
+ ["gs"] = function(x)
+ if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
+ else x=selectSegment(inTAG) end
+ local stamp=getSegmentStamp(inTAG.SEG[x])
+ print("Stamp : "..stamp)
+ stamp=str2bytes(stamp)
+ print("lenght: "..#stamp)
end,
- ["xc"] = function(x)
- --get credential-string for kgh-crc on certain segment
- --usage: xc <segment-index>
- print("k "..kghCrcCredentials(inTAG, x))
+ ---
+ -- calculate crc16
+ ["c6"] = function(x) local crc16=string.format("%4.04x", utils.Crc16(x))
+ print(string.sub(crc16, 0,2).." "..string.sub(crc16, 3,4))
+ end,
+ ---
+ -- check & fix segments-crc of all segments
+ ["cc"] = function(x) if (istable(inTAG)) then checkAllSegCrc(inTAG) end end,
+ ---
+ -- set backup-area-bytes to '00'
+ ["cb"] = function(x)
+ if (istable(inTAG)) then
+ print(accyan.."purge BackupArea"..acoff)
+ inTAG=clearBackupArea(inTAG)
+ end
+ end,
+ ---
+ -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
+ ["ck"] = function(x) if (istable(inTAG)) then checkAllKghCrc(inTAG) end end,
+ ---
+ -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
+ ["tac"] = function(x)
+ if (colored_output) then
+ colored_output=false
+ else
+ colored_output=true
+ end
+ load_colors(colored_output)
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
+ -- defualt message / prompt
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))
+ -- command actions decisions (first match, longer commands before shorter)
+ if (type(actions[string.lower(string.sub(ic,0,3))])=='function') then
+ actions[string.lower(string.sub(ic,0,3))](string.sub(ic,5))
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
+ elseif (type(actions[string.lower(string.sub(ic,0,1))])=='function') then
+ actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3))
+ else actions.h('') end
until (string.sub(ic,0,1)=="q")
end
---- main
+---
+-- main function
function main(args)
+ -- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors')
+ load_colors(colored_output)
if (#args == 0 ) then modifyMode() end
--- variables
- local inTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
+ local inTAG, backupTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
-- just a spacer for better readability
print()
--- parse arguments
print(dumpTag(inTAG))
end
bytes=tagToBytes(inTAG)
- -- xor with given crc
if (cfs) then
+ -- xor willl be done in function writeFile
+ -- with the value of byte[5]
bytes[5]=crc
end
-- write to outfile
if (bytes) then
writeFile(bytes, outfile)
- -- reed new content into virtual tag
-
+ --- read real tag into virtual tag
+ -- inTAG=readFromPM3() end
+ --- or simply use the bytes that where wriiten
inTAG=bytesToTag(bytes, inTAG)
-- show new content
if (dfs) then
print("-----------------------------------------")
- print(dumpTag(outTAG))
+ print(dumpTag(inTAG))
end
end
end
end
---- start
+---
+-- script start
main(args)
\ No newline at end of file