Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 82 additions & 38 deletions client/luascripts/lf_t55xx_multiwriter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ 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 command = core.console
local pm3 = require('pm3')
p = pm3.pm3()
command('clear')
author = ' Author: jareckib - 12.03.2025'
version = ' version v1.03'
author = ' jareckib - 12.03.2025'
version = ' v1.06'
mod = ' 20.03.2025'
desc = [[

This simple script stores 1, 2 or 3 different EM4102 on a single T5577.
There is an option to enter the number engraved on the fob in decimal form.
The script can therefore be useful if the original EM4102 doesn't work but
Expand All @@ -31,15 +35,35 @@ arguments = [[

local function help()
print()
print(author)
print(version)
print(ac.yellow..' Author:'..ac.reset..author)
print(ac.yellow..' Version:'..ac.reset..version)
print(ac.yellow..' Modification date:'..ac.reset..mod)
print(desc)
print(ac.cyan .. ' Usage' .. ac.reset)
print(usage)
print(ac.cyan .. ' Arguments' .. ac.reset)
print(arguments)
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.cyan.."::::: "..ac.yellow.. tonumber(n) ..ac.yellow.." sec "..ac.cyan..":::::\r"..ac.reset)
sleep(1)
io.flush()
n = n-1
end
end

local function reset_log_file()
local file = io.open(logfile, "w+")
file:write("")
Expand Down Expand Up @@ -70,17 +94,14 @@ local function hex_to_bin(hex_value)
if not hex_value:match("^[A-Fa-f0-9]+$") or #hex_value ~= 10 then
error("Invalid UID format. Must be a valid 5-byte HEX value.")
end

local decimal_value = tonumber(hex_value, 16)
if not decimal_value then
error("Error: Invalid HEX conversion.")
end

local binary = ""
for i = 39, 0, -1 do
binary = binary .. ((decimal_value & (1 << i)) ~= 0 and "1" or "0")
end

if #binary ~= 40 then
error("Unexpected UID length after conversion to binary.")
end
Expand All @@ -95,13 +116,11 @@ local function encode_uid(uid)
local uid_bin = hex_to_bin(uid)
local start_bits = '1' .. string.rep('1', 8)
local data_with_parity = ''

for i = 1, 40, 4 do
local nibble = uid_bin:sub(i, i + 3)
local parity_bit = even_parity(nibble)
data_with_parity = data_with_parity .. nibble .. parity_bit
end

local col_parity_bits = ''
for i = 1, 4 do
local col_bits = ''
Expand All @@ -110,10 +129,8 @@ local function encode_uid(uid)
end
col_parity_bits = col_parity_bits .. even_parity(col_bits)
end

local stop_bit = '0'
local full_bin = start_bits .. data_with_parity .. col_parity_bits .. stop_bit

return string.format("%X", tonumber(full_bin, 2))
end

Expand All @@ -129,43 +146,37 @@ local function get_uid_from_user()
io.write(ac.yellow .. "Invalid choice. Please enter (1) or (2) " .. ac.reset)
end
until choice == "1" or choice == "2"

if choice == "1" then
local format
repeat
io.write("Choose format HEX or DEC (engraved ID) "..ac.cyan.."(h/d) "..ac.reset)
format = io.read():lower()

if format ~= "h" and format ~= "d" then
print(ac.yellow .. "Invalid choice. Choose format HEX or DEC" .. ac.reset)
end
until format == "h" or format == "d"

while true do
io.write("Enter 10-character UID: "..ac.green)
local uid = io.read():upper()

if format == "h" and uid:match("^[A-F0-9]+$") and #uid == 10 then
print(ac.reset.."Entered UID: " ..ac.green.. uid .. ac.reset)
return uid
elseif format == "d" and uid:match("^%d%d%d%d%d%d%d%d%d%d$") then
return string.format("%010X", tonumber(uid))
else
print(ac.yellow .. "Invalid UID format. Please enter exactly 10 characters in selected format." .. ac.reset)
print(ac.yellow .. "Invalid UID format. Enter exactly 10 characters in selected format." .. ac.reset)
end
end
elseif choice == "2" then
io.write("Place original FOB on coil for reading and press" ..ac.cyan.." Enter..." .. ac.reset)
io.read()

while true do
reset_log_file()
command('lf em 410x read')
p:console('lf em 410x read')
local log_content = read_log_file(logfile)
local uid = extract_uid(log_content)

if uid and #uid == 10 then
return uid
print("Readed EM4102 ID: " ..ac.green.. uid ..ac.reset)
return uid
else
io.write(ac.yellow .. "Error reading UID. Please adjust FOB position and press Enter..." .. ac.reset)
io.read()
Expand All @@ -175,33 +186,62 @@ local function get_uid_from_user()
end
end

local function verify_written_data(blocks, block0_value, uid_count)
reset_log_file()
p:console('lf t55 detect')
for i = 0, #blocks do
p:console('lf t55 read -b ' .. i)
end
local log_content = read_log_file(logfile)
local verified = true
local pattern_block0 = "%[%s*%+%]%s*00%s*|%s*([A-F0-9]+)"
local found_block0 = log_content:match(pattern_block0)
if not found_block0 or found_block0:upper() ~= block0_value:upper() then
print("Error in block 0 ...expected: " .. ac.green .. block0_value .. ac.reset .. " ...found: " .. ac.green .. (found_block0 or "N/A") .. ac.reset)
verified = false
end
for i = 1, #blocks do
local expected_block = blocks[i]
local pattern = "%[%s*%+%]%s*" .. string.format("%02X", i) .. " |%s*([A-F0-9]+)"
local found_block = log_content:match(pattern)
if not found_block or found_block:upper() ~= expected_block:upper() then
print("Error in block " .. i .. " ...expected: " .. ac.green .. expected_block .. ac.reset .. " ...found: " .. ac.green .. (found_block or "N/A") .. ac.reset)
verified = false
end
end
return verified
end

local function write(blocks, block0_value)
p:console('lf t55xx write -b 0 -d ' .. block0_value)
for i = 1, #blocks do
p:console('lf t55xx write -b ' .. i .. ' -d ' .. blocks[i])
end
end

local function main(args)
for o, a in getopt.getopt(args, 'h') do
if o == 'h' then return help() end
if o == 'h' then
return help()
end
end
local blocks = {}
local uid_count = 0

for i = 1, 3 do
local uid = get_uid_from_user()
local encoded_hex = encode_uid(uid)

blocks[#blocks + 1] = encoded_hex:sub(1, 8)
blocks[#blocks + 1] = encoded_hex:sub(9, 16)

uid_count = uid_count + 1

if i < 3 then
local next_choice
repeat
io.write(ac.reset.."Do you want to add another UID? "..ac.cyan.."(y/n) "..ac.reset)
next_choice = io.read():lower()

if next_choice ~= "y" and next_choice ~= "n" then
print(ac.yellow .. "Invalid choice. Please enter (y) for yes or (n) for no." .. ac.reset)
end
until next_choice == "y" or next_choice == "n"

if next_choice == "y" then
print(dash)
print(ac.yellow .. (i == 1 and "::: Second UID :::" or "::: Third UID :::") .. ac.reset)
Expand All @@ -210,18 +250,22 @@ local function main(args)
end
end
end

local block0_value = (uid_count == 1) and "00148040" or (uid_count == 2) and "00148080" or "001480C0"

io.write("Place "..ac.cyan.."T5577"..ac.reset.." tag on coil for writing and press"..ac.cyan.." Enter..."..ac.reset)
io.write("Place the " .. ac.cyan .. "T5577" .. ac.reset .. " tag on the coil for writing and press " .. ac.cyan .. "Enter..." .. ac.reset)
io.read()

command('lf t55xx write -b 0 -d ' .. block0_value)
for i = 1, #blocks do
command('lf t55xx write -b ' .. i .. ' -d ' .. blocks[i])
write(blocks, block0_value)
print(dash)
timer(3)
local verified = verify_written_data(blocks, block0_value, uid_count)
while not verified do
print("Verification failed." .. ac.reset .. " Please adjust the " .. ac.cyan .. "T5577" .. ac.reset .. " position and try again.")
io.write("Press " .. ac.cyan .. "Enter" .. ac.reset .. " to retry...")
io.read()
write(blocks, block0_value)
timer(3)
verified = verify_written_data(blocks, block0_value, uid_count)
end
print(dash)
print(ac.green .. "Successfully written " .. uid_count .. " EM4102 UID(s) to T5577" .. ac.reset)
print(ac.green .. "Successfully written " .. ac.reset .. uid_count .. ac.green .. " EM4102 UID(s) to T5577" .. ac.reset)
end

main(args)
main(args)
Loading