first cleanup of hf_legic lua script. All io.open calls is problematic.\nfixed some colors in the help texts. makes it a bit easier to read\nfix loading files using the searchfile call\nstill problematic with save file calls since it should honor the user defined preference paths.

This commit is contained in:
iceman1001 2023-07-17 22:54:43 +02:00
commit d08c6f404b
2 changed files with 175 additions and 129 deletions

View file

@ -96,10 +96,63 @@ Known issues; needs to be fixed:
* last byte in last segment is handled incorrectly when it is the last bytes on the card itself (MIM256: => byte 256) * last byte in last segment is handled incorrectly when it is the last bytes on the card itself (MIM256: => byte 256)
--]] --]]
---
-- requirements
local utils = require('utils')
local getopt = require('getopt')
local ansicolors = require('ansicolors')
---
-- global variables / defines
local bxor = bit32.bxor
local bbit = bit32.extract
local input = utils.input
local confirm = utils.confirm
---
-- init ansicolor-values & ansicolors switch
local colored_output = true
local acoff = ""
local acgreen= ""
local accyan = ""
local acred = ""
local acyellow = ""
local acblue = ""
local acmagenta = ""
local acy = ansicolors.yellow
local acr = ansicolors.reset
--- 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
example = "script run hf_legic" example = "script run hf_legic"
author = "Mosci, uhei" author = "Mosci, uhei"
version = "1.0.4" version = "1.0.5"
desc = desc =
[[ [[
@ -109,23 +162,23 @@ it's kinda interactive with following commands in three categories:
Data I/O Segment Manipulation Token-Data Data I/O Segment Manipulation Token-Data
----------------- -------------------- ----------------- ----------------- -------------------- -----------------
rt => read Tag as => add Segment mt => make Token ]]..acy..[[rt]]..acr..[[ => read Tag ]]..acy..[[as]]..acr..[[ => add Segment ]]..acy..[[mt]]..acr..[[ => make Token
wt => write Tag es => edit Segment Header et => edit Token data ]]..acy..[[wt]]..acr..[[ => write Tag ]]..acy..[[es]]..acr..[[ => edit Segment Header ]]..acy..[[et]]..acr..[[ => edit Token data
ed => edit Segment Data tk => toggle KGH-Flag ]]..acy..[[ed]]..acr..[[ => edit Segment Data ]]..acy..[[tk]]..acr..[[ => toggle KGH-Flag
File I/O rs => remove Segment File I/O ]]..acy..[[rs]]..acr..[[ => remove Segment
----------------- cc => check Segment-CRC ----------------- ]]..acy..[[cc]]..acr..[[ => check Segment-CRC
lf => load bin File ck => check KGH ]]..acy..[[lf]]..acr..[[ => load bin File ]]..acy..[[ck]]..acr..[[ => check KGH
sf => save eml/bin File ds => dump Segments ]]..acy..[[sf]]..acr..[[ => save eml/bin File ]]..acy..[[ds]]..acr..[[ => dump Segments
xf => xor to File ]]..acy..[[xf]]..acr..[[ => xor to File
(partially) known Segments Virtual Tags Script Output (partially) known Segments Virtual Tags Script Output
--------------------------- ------------------------------- ------------------------ --------------------------- ------------------------------- ------------------------
dlc => dump Legic-Cash ct => copy mainTag to backupTag tac => toggle ansicolors ]]..acy..[[dlc]]..acr..[[ => dump Legic-Cash ]]..acy..[[ct]]..acr..[[ => copy mainTag to backupTag ]]..acy..[[tac]]..acr..[[ => toggle ansicolors
elc => edit Legic-Cash tc => copy backupTag to mainTag ]]..acy..[[elc]]..acr..[[ => edit Legic-Cash ]]..acy..[[tc]]..acr..[[ => copy backupTag to mainTag
d3p => dump 3rd-Party-Cash tt => switch mainTag & backupTag ]]..acy..[[d3p]]..acr..[[ => dump 3rd-Party-Cash ]]..acy..[[tt]]..acr..[[ => switch mainTag & backupTag
e3p => edit 3rd-Party-Cash di => dump mainTag ]]..acy..[[e3p]]..acr..[[ => edit 3rd-Party-Cash ]]..acy..[[di]]..acr..[[ => dump mainTag
do => dump backupTag ]]..acy..[[do]]..acr..[[ => dump backupTag
@ -169,55 +222,6 @@ tac: 'toggle ansicolors'- switch on and off the colored text-output of this scri
]] ]]
currentTag="inTAG" currentTag="inTAG"
---
-- requirements
local utils = require('utils')
local getopt = require('getopt')
local ansicolors = require('ansicolors')
---
-- global variables / defines
local bxor = bit32.bxor
local bbit = bit32.extract
local input = utils.input
local confirm = utils.confirm
---
-- 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) -- curency-codes for Legic-Cash-Segments (ISO 4217)
local currency = { local currency = {
@ -237,7 +241,11 @@ end
--- ---
-- Usage help -- Usage help
function help() function help()
print(desc) -- the proxmark3 client can't handle such long strings
-- by breaking up at specific points it still looks good.
print(string.sub(desc, 0, 1961))
print(string.sub(desc, 1962, 3925))
print(string.sub(desc, 3926, #desc))
print("Version: "..version) print("Version: "..version)
print("Example usage: "..example) print("Example usage: "..example)
end end
@ -305,18 +313,6 @@ function xorBytes(inBytes, crc)
end end
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
file_found:close()
return true
end
end
--- ---
-- split csv-string into table -- split csv-string into table
local function split(str, sep) local function split(str, sep)
@ -330,6 +326,23 @@ local function split(str, sep)
return fields return fields
end end
---
-- check availability of file
function file_check(file_name)
local arr = split(file_name, ".")
local path = core.search_file(arr[1], "."..arr[2])
if (path == nil) then return false end
local file_found = io.open(path, "r")
if file_found == nil then
return false, ""
else
file_found:close()
return true, path
end
end
--- ---
-- put a string into a bytes-table -- put a string into a bytes-table
function str2bytes(s) function str2bytes(s)
@ -358,8 +371,13 @@ end
function getInputBytes(infile) function getInputBytes(infile)
local line local line
local bytes = {} local bytes = {}
local fhi,err = io.open(infile,"rb")
if err then oops("failed to read from file ".. infile); return false; end local arr = split(infile, ".")
local path = core.search_file(arr[1], "."..arr[2])
if (path == nil) then oops("failed to read from file ".. infile); return false; end
local fhi,err = io.open(path,"rb")
if err then oops("failed to read from file ".. path); return false; end
file_data = fhi:read("*a"); file_data = fhi:read("*a");
for i = 1, #file_data do for i = 1, #file_data do
@ -367,7 +385,7 @@ function getInputBytes(infile)
end end
fhi:close() fhi:close()
if (bytes[7]=='00') then return false end if (bytes[7]=='00') then return false end
print(#bytes .. " bytes from "..infile.." loaded") print(#bytes .. " bytes from "..path.." loaded")
return bytes return bytes
end end
@ -613,12 +631,13 @@ local function readFile(filename)
print(accyan) print(accyan)
local bytes = {} local bytes = {}
local tag = {} local tag = {}
if file_check(filename) == false then
local res, path = file_check(filename)
if not res then
return oops("input file: "..acyellow..filename..acoff.." not found") return oops("input file: "..acyellow..filename..acoff.." not found")
end end
bytes = getInputBytes(filename) bytes = getInputBytes(path)
if bytes == false then return oops('couldnt get input bytes') end if bytes == false then return oops('couldnt get input bytes') end
-- make plain bytes -- make plain bytes
@ -640,12 +659,14 @@ local function save_BIN(data, filename)
local fn = filename..ext local fn = filename..ext
-- Make sure we don't overwrite a file -- Make sure we don't overwrite a file
while file_check(fn) do local res, path = file_check(fn)
while res == false do
fn = filename..ext:gsub(ext, "-"..tostring(counter)..ext) fn = filename..ext:gsub(ext, "-"..tostring(counter)..ext)
counter = counter + 1 counter = counter + 1
res, path = file_check(fn)
end end
outfile = io.open(fn, 'wb') outfile = io.open(path, 'wb')
local i = 1 local i = 1
while data[i] do while data[i] do
@ -660,17 +681,19 @@ end
-- write bytes to file -- write bytes to file
function writeFile(bytes, filename) function writeFile(bytes, filename)
local emlext = ".eml" local emlext = ".eml"
local res, path
if (filename ~= 'MyLegicClone') then if (filename ~= 'MyLegicClone') then
if (file_check(filename..emlext)) then res, path = file_check(filename..emlext)
local answer = confirm("\nthe output-file "..filename..emlext.." already exists!\nthis will delete the previous content!\ncontinue?") if res then
local answer = confirm("\nthe output-file "..path.." already exists!\nthis will delete the previous content!\ncontinue?")
if not answer then return print("user abort") end if not answer then return print("user abort") end
end end
end end
local line local line
local bcnt = 0 local bcnt = 0
local fho, err = io.open(filename..emlext, "w") local fho, err = io.open(path, "w")
if err then if err then
return oops("OOps ... failed to open output-file ".. filename..emlext) return oops("OOps ... failed to open output-file ".. path)
end end
bytes = xorBytes(bytes, bytes[5]) bytes = xorBytes(bytes, bytes[5])
@ -692,11 +715,10 @@ function writeFile(bytes, filename)
end end
fho:close() fho:close()
-- save binary
local fn_bin, fn_bin_num = save_BIN(bytes, filename)
print("\nwrote "..acyellow..(#bytes * 3)..acoff.." bytes to " ..acyellow..filename..emlext..acoff) print("\nwrote "..acyellow..(#bytes * 3)..acoff.." bytes to " ..acyellow..filename..emlext..acoff)
-- save binary
local fn_bin, fn_bin_num = save_BIN(bytes, filename)
if fn_bin and fn_bin_num then if fn_bin and fn_bin_num then
print("\nwrote "..acyellow..fn_bin_num..acoff.." bytes to BINARY file "..acyellow..fn_bin..acoff) print("\nwrote "..acyellow..fn_bin_num..acoff.." bytes to BINARY file "..acyellow..fn_bin..acoff)
end end
@ -731,10 +753,16 @@ function readFromPM3()
infile=getRandomTempName() infile=getRandomTempName()
core.console("hf legic dump -f "..infile) core.console("hf legic dump -f "..infile)
tag=readFile(infile..".bin") tag=readFile(infile..".bin")
os.remove(infile)
os.remove(infile..".bin") res, path = file_check(infile..".bin")
os.remove(infile..".eml") if not res then return nil end
os.remove(infile..".json") os.remove(path)
res, path = file_check(infile..".eml")
os.remove(path)
res, path = file_check(infile..".json")
os.remove(path)
return tag return tag
end end
@ -758,16 +786,20 @@ end
--- ---
-- save mapping to file -- save mapping to file
local function saveTagMap(map, filename) local function saveTagMap(map, filename)
local res, path
if #filename > 0 then if #filename > 0 then
if file_check(filename) then res, path = file_check(filename)
local answer = confirm("\nthe output-file "..acyellow..filename..acoff.." alredy exists!\nthis will delete the previous content!\ncontinue?") if res then
local answer = confirm("\nthe output-file "..acyellow..path..acoff.." alredy exists!\nthis will delete the previous content!\ncontinue?")
if not answer then return print("user abort") end if not answer then return print("user abort") end
end end
end end
local line local line
local fho,err = io.open(filename, "w") local fho,err = io.open(path, "w")
if err then oops("OOps ... failed to open output-file "..acyellow..filename..acoff) end if err then oops("OOps ... failed to open output-file "..acyellow..path..acoff) end
-- write line to new file -- write line to new file
for k, v in pairs(map) do for k, v in pairs(map) do
@ -842,10 +874,13 @@ function loadTagMap(filename)
local line, fields local line, fields
local temp={} local temp={}
local offset=0 local offset=0
if not file_check(filename) then
return oops("input file: "..acyellow..filename..acoff.." not found") local res, path = file_check(filename)
else if not res then
local fhi,err = io.open(filename) return oops("input file: "..acyellow..filename..acoff.." not found")
else
local fhi,err = io.open(path)
while true do while true do
line = fhi:read() line = fhi:read()
if line == nil then if line == nil then
@ -971,6 +1006,7 @@ Mappings: im = insert am = add rm = remove
CRC8: ac8 = add sc8 = show rc8 = remove CRC8: ac8 = add sc8 = show rc8 = remove
: q = exit h = Help : q = exit h = Help
]] ]]
--if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end --if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end
print("tagMap edit-mode submenu") print("tagMap edit-mode submenu")
repeat repeat
@ -2293,25 +2329,25 @@ function modifyHelp()
Data I/O Segment Manipulation Token-Data Data I/O Segment Manipulation Token-Data
----------------- -------------------- --------------------- ----------------- -------------------- ---------------------
rt => read Tag as => add Segment mt => make Token ]]..acy..[[rt]]..acr..[[ => read Tag ]]..acy..[[as]]..acr..[[ => add Segment ]]..acy..[[mt]]..acr..[[ => make Token
wt => write Tag es => edit Segment Header et => edit Token data ]]..acy..[[wt]]..acr..[[ => write Tag ]]..acy..[[es]]..acr..[[ => edit Segment Header ]]..acy..[[et]]..acr..[[ => edit Token data
ed => edit Segment Data tk => toggle KGH-Flag ]]..acy..[[ed]]..acr..[[ => edit Segment Data ]]..acy..[[tk]]..acr..[[ => toggle KGH-Flag
File I/O rs => remove Segment File I/O ]]..acy..[[rs]]..acr..[[ => remove Segment
----------------- cc => check Segment-CRC ----------------- ]]..acy..[[cc]]..acr..[[ => check Segment-CRC
lf => load bin File ck => check KGH ]]..acy..[[lf]]..acr..[[ => load bin File ]]..acy..[[ck]]..acr..[[ => check KGH
sf => save eml/bin File ds => dump Segments ]]..acy..[[sf]]..acr..[[ => save eml/bin File ]]..acy..[[ds]]..acr..[[ => dump Segments
xf => xor to File ]]..acy..[[xf]]..acr..[[ => xor to File
Virtual Tags tagMap (partial) known Segments Virtual Tags tagMap (partial) known Segments
-------------------------------- --------------------- --------------------------- -------------------------------- --------------------- ---------------------------
ct => copy mainTag to backupTag mm => make (new) Map dlc => dump Legic-Cash ]]..acy..[[xf]]..acr..[[ => copy mainTag to backupTag ]]..acy..[[mm]]..acr..[[ => make (new) Map ]]..acy..[[dlc]]..acr..[[ => dump Legic-Cash
tc => copy backupTag to mainTag em => edit Map submenu elc => edit Legic-Cash ]]..acy..[[xf]]..acr..[[ => copy backupTag to mainTag ]]..acy..[[em]]..acr..[[ => edit Map submenu ]]..acy..[[elc]]..acr..[[ => edit Legic-Cash
tt => switch mainTag & backupTag lm => load map from file d3p => dump 3rd-Party-Cash ]]..acy..[[xf]]..acr..[[ => switch mainTag & backupTag ]]..acy..[[lm]]..acr..[[ => load map from file ]]..acy..[[d3p]]..acr..[[ => dump 3rd-Party-Cash
di => dump mainTag sm => save map to file e3p => edit 3rd-Party-Cash ]]..acy..[[xf]]..acr..[[ => dump mainTag ]]..acy..[[sm]]..acr..[[ => save map to file ]]..acy..[[e3p]]..acr..[[ => edit 3rd-Party-Cash
do => dump backupTag ]]..acy..[[xf]]..acr..[[ => dump backupTag
h => this help q => quit ]]..acy..[[h]]..acr..[[ => this help ]]..acy..[[q]]..acr..[[ => quit
]] ]]
return t return t
end end
@ -2325,8 +2361,10 @@ function modifyMode()
--- ---
-- helptext -- helptext
["h"] = function(x) ["h"] = function(x)
print(" Version: "..version); print(" Version: "..acgreen..version..acr);
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 "")) print(modifyHelp())
print("\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 ""))
print("")
end, end,
--- ---
-- read real Tag with PM3 into virtual 'mainTAG' -- read real Tag with PM3 into virtual 'mainTAG'
@ -2375,8 +2413,9 @@ function modifyMode()
end end
inTAG=readFile(filename) inTAG=readFile(filename)
-- check for existing tagMap -- check for existing tagMap
if (file_check(filename..".map")) then local res, path = file_check(filename..".map")
if(confirm(accyan.."Mapping-File for "..acoff..filename..accyan.." found - load it also?"..acoff)) then if res then
if(confirm(accyan.."Mapping-File for "..acoff..path..accyan.." found - load it also?"..acoff)) then
tagMap=loadTagMap(filename..".map") tagMap=loadTagMap(filename..".map")
end end
end end
@ -2388,7 +2427,7 @@ function modifyMode()
outfile = input("enter filename:", "hf-legic-"..inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2) outfile = input("enter filename:", "hf-legic-"..inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2)
bytes = tagToBytes(inTAG) bytes = tagToBytes(inTAG)
--bytes=xorBytes(bytes, inTAG.MCC) --bytes=xorBytes(bytes, inTAG.MCC)
if (bytes) then if (bytes) then
writeFile(bytes, outfile) writeFile(bytes, outfile)
end end
end end
@ -2743,10 +2782,13 @@ end
function main(args) function main(args)
-- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors') -- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors')
load_colors(colored_output) load_colors(colored_output)
if (#args == 0 ) then modifyMode() end if (#args == 0 ) then modifyMode() end
--- variables --- variables
local inTAG, backupTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs local inTAG, backupTAG, outTAG, outfile, interactive, crc
-- just a spacer for better readability local ofs=false
local cfs=false
local dfs=false
-- just a spacer for better readability
print() print()
--- parse arguments --- parse arguments
for o, a in getopt.getopt(args, 'hrmi:do:c:') do for o, a in getopt.getopt(args, 'hrmi:do:c:') do
@ -2755,7 +2797,7 @@ function main(args)
-- read tag from PM3 -- read tag from PM3
if o == "r" then inTAG=readFromPM3() end if o == "r" then inTAG=readFromPM3() end
-- input file -- input file
if o == "i" then inTAG=readFile(a) end if o == "i" then inTAG=readFile(a) end
-- dump virtual-Tag -- dump virtual-Tag
if o == "d" then dfs=true end if o == "d" then dfs=true end
-- interacive modifying -- interacive modifying
@ -2793,7 +2835,10 @@ function main(args)
-- write to outfile -- write to outfile
if (bytes) then if (bytes) then
writeFile(bytes, outfile)
if (outfile) then
writeFile(bytes, outfile)
end
--- read real tag into virtual tag --- read real tag into virtual tag
-- inTAG=readFromPM3() end -- inTAG=readFromPM3() end
--- or simply use the bytes that where wriiten --- or simply use the bytes that where wriiten

View file

@ -1269,8 +1269,9 @@ static int l_searchfile(lua_State *L) {
size_t size; size_t size;
// data // data
const char *filename = luaL_checklstring(L, 1, &size); const char *filename = luaL_checklstring(L, 1, &size);
if (size == 0) if (size == 0) {
return returnToLuaWithError(L, "Must specify filename"); return returnToLuaWithError(L, "Must specify filename");
}
const char *suffix = luaL_checklstring(L, 2, &size); const char *suffix = luaL_checklstring(L, 2, &size);
char *path; char *path;