diff --git a/client/luascripts/lf_em_tearoff_protect.lua b/client/luascripts/lf_em_tearoff_protect.lua new file mode 100644 index 000000000..87e16a1c8 --- /dev/null +++ b/client/luascripts/lf_em_tearoff_protect.lua @@ -0,0 +1,195 @@ +local getopt = require('getopt') +local ansicolors = require('ansicolors') + +copyright = 'Iceman' +author = [[ +'Author Iceman + CoAuthor Doegox +]] +version = 'v0.9.9' +desc = [[ +This is scripts loops though a tear attack and reads expected value. +]] +example = [[ + script run lf_em_tearoff_protect -n 2 -s 200 -e 400 + + Trying repeatedly for a fixed timing, forever or till success: + script run lf_em_tearoff_protect -s 400 -e 400 +]] +usage = [[ +script run lf_em_tearoff_protect [-h] [-n ] [-p ] [-s ] [-e ] +]] +arguments = [[ + -h This help + -n steps in milliseconds for each tear-off + -p (optional) use a password + -s initial start delay + -e end delay, must be larger or equal to start delay + end +]] + +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, err +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end + +local function main(args) + + --[[ + Basically it does the following, + + 1. hw tear + 2. lf em 4x05_write + 3. lf em 4x05_read + + The first two commands dont need a feedback from the system, so going with core.console commands. + Since the read needs demodulation of signal I opted to add that function from cmdlfem4x.c to the core lua scripting + core.em4x05_read(addr, password) + + --]] + local n, password, sd, ed + + for o, a in getopt.getopt(args, 'he:s:a:p:n:r:w:') do + if o == 'h' then return help() end + if o == 'n' then n = a end + if o == 'p' then password = a end + if o == 'e' then ed = tonumber(a) end + if o == 's' then sd = tonumber(a) end + end + + password = password or '' + if #password ~= 8 then + password = '' + end + + local word14, err14 = core.em4x05_read(14, password) + if err14 then + return oops(err14) + end + local word15, err15 = core.em4x05_read(15, password) + if err15 then + return oops(err15) + end + local rd_value = ('%08X'):format(bit.bor(word14,word15)) + local wr_value = '00000000' + n = n or 2 + sd = sd or 2000 + ed = ed or 2100 + + if sd > ed then + return oops('start delay can\'t be larger than end delay', sd, ed) + end + + print('==========================================') + print('Starting EM4x05 tear off :: target PROTECT') + + if password then + print('target pwd', password) + end + print('target stepping', n) + print('target delay', sd ,ed) + print('read value', rd_value) + print('write value', wr_value) + print('==========================================') + + local res_tear = 0 + local res_nowrite = 0 + + local set_tearoff_delay = 'hw tearoff --delay %d' + local enable_tearoff = 'hw tearoff --on' + + local wr_template = 'lf em 4x05_write %s %s %s' + + -- fix at one specific delay + if sd == ed then + n = 0 + end + + local tries = 0 + while sd <= ed do + + -- increase loop + sd = sd + n + + if tries == 20 then + tries = 0 + sd = sd + 1 + ed = ed + 1 + print(ansicolors.cyan..'[!] Tried 20 times, increased delay with 1us'..ansicolors.reset) + end + + io.flush() + if core.kbd_enter_pressed() then + print("aborted by user") + break + end + + core.clearCommandBuffer() + + local c = set_tearoff_delay:format(sd) + core.console(c); + core.console(enable_tearoff) + + c = wr_template:format(99, wr_value, password) + core.console(c) + + word14, err14 = core.em4x05_read(14, password) + if err14 then + return oops(err14) + end + + local wordstr14 = ('%08X'):format(word14) + + word15, err15 = core.em4x05_read(15, password) + if err15 then + return oops(err15) + end + + local wordstr15 = ('%08X'):format(word15) + + if (not (wordstr14 == '00000000' and wordstr15 == wr_value)) and (not (wordstr14 == wr_value and wordstr15 == '00000000')) then + print((ansicolors.yellow..'[!] TEAR OFF occurred:'..ansicolors.reset..' 14:%08X 15:%08X'):format(word14, word15)) + end + + if wordstr14 == rd_value then + if wordstr15 ~= rd_value and wordstr15 ~= wr_value then + print((ansicolors.red..'[!] tear off result: '..ansicolors.reset..' 14:%08X 15:%08X'):format(word14, word15)) + return oops('Success?') + end + + if wordstr15 == rd_value then + print(ansicolors.red..'[!] need to reset'..ansicolors.reset) + c = wr_template:format(99, wr_value, password) + core.console(c) + tries = 0 + end + else + print('...') + end + tries = tries + 1 + end +end + +--[[ +In the future, we may implement so that scripts are invoked directly +into a 'main' function, instead of being executed blindly. For future +compatibility, I have done so, but I invoke my main from here. +--]] +main(args)