From 907522f0aea86ed1f57662549dbeab4daf0d6380 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 16 Jul 2026 20:01:24 -0700 Subject: [PATCH] feat(lf): test-mode T55xx write (opcode 01) for recovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `test` flag to lf.t55.writebl that sets the firmware test-mode bit, sending the datasheet's opcode-01 reconfiguration write (§5.10.3) instead of the standard opcode-10 write. This is the path that can rewrite a tag a normal write can't (corrupted/locked config), when the master key still permits it. Matches the C client's `lf t55 write -t`. Default off; routine writes are unaffected. Co-Authored-By: Claude Opus 4.8 --- pm3py/core/lf.py | 13 +++++++++++-- tests/test_lf.py | 11 +++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pm3py/core/lf.py b/pm3py/core/lf.py index 2baacb5..da0e008 100644 --- a/pm3py/core/lf.py +++ b/pm3py/core/lf.py @@ -25,8 +25,15 @@ class T55xxCommands: } async def writebl(self, block: int, data: int, page: int = 0, - password: int | None = None, downlink_mode: int = 0) -> dict: - """Write a T55xx block.""" + password: int | None = None, downlink_mode: int = 0, + test: bool = False) -> dict: + """Write a T55xx block. + + ``test`` uses the test-mode write (downlink opcode 01) instead of the standard opcode 10. + Test mode is the datasheet's reconfiguration path (§5.10.3) — it can rewrite a tag that a + normal write can't (a corrupted/locked config), provided the master key still permits it + (allowed for key 9, denied once key 6 is set). Use it for recovery, not routine writes. + """ pwd = password if password is not None else 0 # Firmware t55xx_write_block_t: data(4)+pwd(4)+blockno(1)+flags(1). # flags: 0x01 PwdMode, 0x02 Page, 0x04 test, 0x18 downlink_mode<<3. @@ -35,6 +42,8 @@ class T55xxCommands: flags |= 0x01 if page: flags |= 0x02 + if test: + flags |= 0x04 flags |= (downlink_mode & 0x03) << 3 payload = struct.pack("