diff --git a/client/luascripts/Paxton_clone.lua b/client/luascripts/Paxton_clone.lua index dcf99e5bf..523eb07c3 100644 --- a/client/luascripts/Paxton_clone.lua +++ b/client/luascripts/Paxton_clone.lua @@ -1,4 +1,3 @@ - local getopt = require('getopt') local utils = require('utils') local ac = require('ansicolors') @@ -8,11 +7,14 @@ local dir = os.getenv('HOME') .. '/.proxmark3/logs/' local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) local log_file_path = dir .. "Paxton_log.txt" local nam = "" +local pm3 = require('pm3') +p = pm3.pm3() local command = core.console +command('clear') author = ' Author: jareckib - 30.01.2025' tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' -version = ' version v1.18' +version = ' version v1.19' desc = [[ The script automates the copying of Paxton fobs read - write. It also allows manual input of data for blocks 4-7. @@ -228,13 +230,10 @@ end local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) while true do - print(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2" .. ac.reset) - print(dash) - io.write(" Your choice "..ac.cyan.."(1/2): "..ac.reset) + io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset) local choice = io.read() if choice == "1" then - print(dash) - print(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..") io.read() print(dash) command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846") @@ -242,20 +241,18 @@ local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846") command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846") elseif choice == "2" then - print(dash) - print(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") io.read() - print(dash) - command("lf em 410x clone --id " .. padded_hex_id) + p:console("lf em 410x clone --id " .. padded_hex_id) + print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset) else print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset) goto ask_again end while true do print(dash) - io.write(" Make next RFID Fob"..ac.cyan.." (y/n)"..ac.reset.." > "..ac.yellow) + io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset) local another = io.read() - io.write(ac.reset..'') if another:lower() == "n" then if was_option_3 then print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist") @@ -303,7 +300,6 @@ local function main(args) if o == 'h' then return help() end end command('clear') - print() print(dash) print(ac.green .. ' Select option: ' .. ac.reset) print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') @@ -324,12 +320,11 @@ local function main(args) local show_place_message = true while true do if show_place_message then - print(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..') - print(dash) + io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..') end io.read() - command('lf hitag read --ht2 -k BDF5E846') - command('clear') + print(dash) + p:console('lf hitag read --ht2 -k BDF5E846') if not logfile then error(" No files in this directory") end @@ -343,12 +338,11 @@ local function main(args) end end if empty_block then - print(dash) - print(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..') - print(dash) + io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..') show_place_message = false else - print(dash) + print(' Readed blocks:') + print() for i = 4, 7 do if blocks[i] then print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset)) @@ -364,7 +358,6 @@ local function main(args) print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) end - print(dash) print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) print(dash) handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) diff --git a/client/luascripts/lf_t55xx_fix.lua b/client/luascripts/lf_t55xx_fix.lua index a77cb2b9b..e9f379c1f 100644 --- a/client/luascripts/lf_t55xx_fix.lua +++ b/client/luascripts/lf_t55xx_fix.lua @@ -5,17 +5,20 @@ local os = require('os') local dash = string.rep('--', 32) local dir = os.getenv('HOME') .. '/.proxmark3/logs/' local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) +local pm3 = require('pm3') +p = pm3.pm3() local command = core.console +command('clear') author = ' Author: jareckib - 15.02.2025' -version = ' version v1.00' +version = ' version v1.02' desc = [[ This simple script first checks if a password has been set for the T5577. It uses the dictionary t55xx_default_pwds.dic for this purpose. If a password is found, it uses the wipe command to erase the T5577. Then the reanimation procedure is applied. If the password is not found or doesn't exist the script only performs the reanimation procedure. The script revives 99% of blocked tags. - ]] +]] usage = [[ script run lf_t55xx_fix ]] @@ -44,6 +47,25 @@ local function read_log_file(logfile) return content end +local function sleep(n) + os.execute("sleep " ..tonumber(n)) +end + +function wait(msec) + local t = os.clock() + repeat + until os.clock() > t + msec * 1e-3 +end + +local function timer(n) + while n > 0 do + io.write("::::: "..ac.yellow.. tonumber(n) ..ac.yellow.." sec "..ac.reset..":::::\r") + sleep(1) + io.flush() + n = n-1 + end +end + local function extract_password(log_content) for line in log_content:gmatch("[^\r\n]+") do local password = line:match('%[%+%] found valid password: %[ (%x%x%x%x%x%x%x%x) %]') @@ -54,48 +76,69 @@ local function extract_password(log_content) return nil end -local function reanimate_t5577(password) - if password then - command('clear') - print(dash) - print(" Using found password to wipe: " .. password) - print(dash) - command('lf t55 wipe -p ' .. password) - else - command('clear') - print(dash) - print(ac.yellow.." No valid password found, proceeding with reanimation."..ac.reset) - print(dash) - end - command('lf t55 write -b 0 -d 000880E8 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -t -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r0 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r1 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r2 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --r3 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -p 00000000') - command('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000') - command('lf t55 detect') - local file = io.open(logfile, "w+") +local function reset_log_file() + local file = io.open(logfile, "w+") file:write("") file:close() - print(dash) - print('all done!') +end + +local function reanimate_t5577(password) + if password then + p:console('lf t55 wipe -p ' .. password) + print("T5577 wiped using a password: " ..ac.green.. password ..ac.reset) + else + print(ac.yellow.."No valid password found, proceeding with reanimation."..ac.reset) + end + + p:console('lf t55 write -b 0 -d 000880E8 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -t -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r0 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r1 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r2 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --r3 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -p 00000000') + p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000') + reset_log_file() end local function main(args) for o, a in getopt.getopt(args, 'h') do if o == 'h' then return help() end end - command('lf t55 chk') + p:console('clear') + print(dash) + print('I am initiating the repair process for '..ac.cyan..'T5577'..ac.reset) + io.write("Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + print(dash) + print("::: "..ac.cyan.."Hold on, I'm searching for a password in the dictionary"..ac.reset.." :::") + print(dash) + p:console('lf t55 chk') local log_content = read_log_file(logfile) local password = log_content and extract_password(log_content) or nil reanimate_t5577(password) + p:console('lf t55 detect') + p:console('lf t55 read -b 0') + timer(5) + local success = false + for line in p.grabbed_output:gmatch("[^\r\n]+") do + if line:find("00 | 000880E0 |") then + success = true + break + end + end + + if success then + print('Recovery of '..ac.cyan..'T5577'..ac.reset..' was successful !!!') + else + print('Recovery of '..ac.cyan..'T5577'..ac.reset..' was unsuccessful !!!') + end + print(dash) end -main(args) +main(args) \ No newline at end of file diff --git a/client/luascripts/paxton_clone.lua b/client/luascripts/paxton_clone.lua new file mode 100644 index 000000000..ee410a11c --- /dev/null +++ b/client/luascripts/paxton_clone.lua @@ -0,0 +1,462 @@ +local getopt = require('getopt') +local utils = require('utils') +local ac = require('ansicolors') +local os = require('os') +local dash = string.rep('--', 32) +local dir = os.getenv('HOME') .. '/.proxmark3/logs/' +local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) +local log_file_path = dir .. "Paxton_log.txt" +local nam = "" +local pm3 = require('pm3') +p = pm3.pm3() +local command = core.console +command('clear') + +author = ' Author: jareckib - 30.01.2025' +tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' +version = ' version v1.18' +desc = [[ + The script automates the copying of Paxton fobs read - write. + It also allows manual input of data for blocks 4-7. + The third option is reading data stored in the log file and create new fob. + Additionally, the script calculates the ID for downgrading Paxton to EM4102. + + ]] +usage = [[ + script run paxton_clone +]] +arguments = [[ + script run paxton_clone -h : this help +]] + +local debug = true + +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end + +local function help() + print() + print(author) + print(tutorial) + print(version) + print(desc) + print(ac.cyan..' Usage'..ac.reset) + print(usage) + print(ac.cyan..' Arguments'..ac.reset) + print(arguments) +end + +local function read_log_file(logfile) + local file = io.open(logfile, "r") + if not file then + error(" Could not open the file") + end + local content = file:read("*all") + file:close() + return content +end + +local function parse_blocks(result) + local blocks = {} + for line in result:gmatch("[^\r\n]+") do + local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)") + if block_num and block_data then + block_num = tonumber(block_num) + block_data = block_data:gsub("%s+", "") + blocks[block_num] = block_data + end + end + return blocks +end + +local function hex_to_bin(hex_string) + local bin_string = "" + local hex_to_bin_map = { + ['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011", + ['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111", + ['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011", + ['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111" + } + for i = 1, #hex_string do + bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)] + end + return bin_string +end + +local function remove_last_two_bits(binary_str) + return binary_str:sub(1, #binary_str - 2) +end + +local function split_into_5bit_chunks(binary_str) + local chunks = {} + for i = 1, #binary_str, 5 do + table.insert(chunks, binary_str:sub(i, i + 4)) + end + return chunks +end + +local function remove_parity_bit(chunks) + local no_parity_chunks = {} + for _, chunk in ipairs(chunks) do + if #chunk == 5 then + table.insert(no_parity_chunks, chunk:sub(2)) + end + end + return no_parity_chunks +end + +local function convert_to_hex(chunks) + local hex_values = {} + for _, chunk in ipairs(chunks) do + if #chunk > 0 then + table.insert(hex_values, string.format("%X", tonumber(chunk, 2))) + end + end + return hex_values +end + +local function convert_to_decimal(chunks) + local decimal_values = {} + for _, chunk in ipairs(chunks) do + table.insert(decimal_values, tonumber(chunk, 2)) + end + return decimal_values +end + +local function find_until_before_f(hex_values) + local result = {} + for _, value in ipairs(hex_values) do + if value == 'F' then + break + end + table.insert(result, value) + end + return result +end + +local function process_block(block) + local binary_str = hex_to_bin(block) + binary_str = remove_last_two_bits(binary_str) + local chunks = split_into_5bit_chunks(binary_str) + local no_parity_chunks = remove_parity_bit(chunks) + return no_parity_chunks +end + +local function calculate_id_net(blocks) + local all_hex_values = {} + for _, block in ipairs(blocks) do + local hex_values = convert_to_hex(process_block(block)) + for _, hex in ipairs(hex_values) do + table.insert(all_hex_values, hex) + end + end + local selected_hex_values = find_until_before_f(all_hex_values) + if #selected_hex_values == 0 then + error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5') + end + local combined_hex = table.concat(selected_hex_values) + if not combined_hex:match("^%x+$") then + error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5') + end + local decimal_id = tonumber(combined_hex) + local stripped_hex_id = string.format("%X", decimal_id) + local padded_hex_id = string.format("%010X", decimal_id) + return decimal_id, padded_hex_id +end + +local function calculate_id_switch(blocks) + local all_decimal_values = {} + for _, block in ipairs(blocks) do + local decimal_values = convert_to_decimal(process_block(block)) + for _, dec in ipairs(decimal_values) do + table.insert(all_decimal_values, dec) + end + end + if #all_decimal_values < 15 then + error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7') + end + local id_positions = {9, 11, 13, 15, 2, 4, 6, 8} + local id_numbers = {} + for _, pos in ipairs(id_positions) do + table.insert(id_numbers, all_decimal_values[pos]) + end + local decimal_id = tonumber(table.concat(id_numbers)) + local padded_hex_id = string.format("%010X", decimal_id) + return decimal_id, padded_hex_id +end + +local function name_exists_in_log(name) + local file = io.open(log_file_path, "r") + if not file then + return false + end + local pattern = "^Name:%s*" .. name .. "%s*$" + for line in file:lines() do + if line:match(pattern) then + file:close() + return true + end + end + file:close() + return false +end + +local function log_result(blocks, em410_id, name) + local log_file = io.open(log_file_path, "a") + if log_file then + log_file:write("Name: " .. name .. "\n") + log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n") + for i = 4, 7 do + log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil")) + end + log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil")) + log_file:write('--------------------------\n') + log_file:close() + print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset) + else + print(" Failed to open log file for writing.") + end +end + +local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + while true do + io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset) + local choice = io.read() + if choice == "1" then + io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + print(dash) + command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846") + command("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846") + command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846") + command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846") + elseif choice == "2" then + io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + p:console("lf em 410x clone --id " .. padded_hex_id) + print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset) + else + print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset) + goto ask_again + end + while true do + print(dash) + io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset) + local another = io.read() + if another:lower() == "n" then + if was_option_3 then + print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist") + return + end + print() + print(ac.green .. " Saving Paxton_log file..." .. ac.reset) + while true do + io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow) + name = io.read() + io.write(ac.reset..'') + if name == nil or name:match("^%s*$") then + print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.') + else + if name_exists_in_log(name) then + print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.') + else + break + end + end + end + log_result(blocks, padded_hex_id, name) + print(ac.green .. " Log saved successfully!" .. ac.reset) + local file = io.open(logfile, "w+") + file:write("") + file:close() + return + elseif another:lower() == "y" then + goto ask_again + else + print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset) + end + end + ::ask_again:: + end +end + +local function is_valid_hex(input) + return #input == 8 and input:match("^[0-9A-Fa-f]+$") +end + +local function main(args) + while true do + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + command('clear') + print(dash) + print(ac.green .. ' Select option: ' .. ac.reset) + print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') + print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7') + print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data") + print(dash) + while true do + io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset) + input_option = io.read() + if input_option == "1" or input_option == "2" or input_option == "3" then + break + else + print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset) + end + end + local was_option_3 = false + if input_option == "1" then + local show_place_message = true + while true do + if show_place_message then + io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..') + end + io.read() + print(dash) + p:console('lf hitag read --ht2 -k BDF5E846') + if not logfile then + error(" No files in this directory") + end + local result = read_log_file(logfile) + local blocks = parse_blocks(result) + local empty_block = false + for i = 4, 7 do + if not blocks[i] then + empty_block = true + break + end + end + if empty_block then + io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..') + show_place_message = false + else + print(' Readed blocks:') + print() + for i = 4, 7 do + if blocks[i] then + print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset)) + end + end + local decimal_id, padded_hex_id + if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then + print(dash) + print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) + else + print(dash) + print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) + end + print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) + print(dash) + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + break + end + end + elseif input_option == "2" then + local blocks = {} + for i = 4, 7 do + while true do + io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow) + local input = io.read() + input = input:upper() + if is_valid_hex(input) then + blocks[i] = input + break + else + print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).') + end + end + end + local decimal_id, padded_hex_id + if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then + print(ac.reset.. dash) + print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) + else + print(ac.reset.. dash) + print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) + end + print(dash) + print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) + print(dash) + if not padded_hex_id then + print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided') + return + end + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + break + elseif input_option == "3" then + was_option_3 = true + local retries = 3 + while retries > 0 do + io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) + local user_input = io.read() + io.write(ac.reset..'') + if user_input == nil or user_input:match("^%s*$") then + print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!') + end + local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$" + local file = io.open(log_file_path, "r") + if not file then + print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.') + return + end + local lines = {} + for line in file:lines() do + table.insert(lines, line) + end + file:close() + local found = false + for i = 1, #lines do + if lines[i]:match(name_clean) then + nam = user_input + local blocks = { + [4] = lines[i + 2]:match("Block 4: (.+)"), + [5] = lines[i + 3]:match("Block 5: (.+)"), + [6] = lines[i + 4]:match("Block 6: (.+)"), + [7] = lines[i + 5]:match("Block 7: (.+)") + } + local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)") + print(dash) + print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset) + for j = 4, 7 do + print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset)) + end + print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset) + print(dash) + local decimal_id, padded_hex_id = em4102_id, em4102_id + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam) + found = true + break + end + end + if not found then + retries = retries - 1 + else + break + end + end + if retries == 0 then + print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset) + end + end + print(dash) + print(' Exiting script Lua...') + return + end +end + +main(args) \ No newline at end of file