Skip to content

lf t55xx dump returns bit-rotated blocks - the offset is cached from detect and never re-derived #3512

Description

@mfcarroll

Describe the bug

lf t55xx dump and lf t55xx rdbl extract each 32-bit block at a bit offset cached from the last lf t55xx detect, and never re-derive it from the buffer actually being decoded. When a later read's demodulation starts on a different bit boundary than the detect-time capture did, the returned word is a bit rotation of the true block - silently, with no error. detect is unaffected because it searches for the offset in the same buffer it just demodulated.

client/src/cmdlft55xx.c (unchanged at master fb3ec70):

bool GetT55xxBlockData(uint32_t *blockdata) {
    if (g_DemodBufferLen == 0)
        return false;
    uint8_t idx = config.offset;          // <-- cached by the LAST detect, on a DIFFERENT capture
    if (idx + 32 > g_DemodBufferLen) { ... return false; }
    *blockdata = PackBits(0, 32, g_DemodBuffer + idx);
    return true;
}

config.offset is only ever assigned in the detect path (config.offset = tests[i].offset;), where it is found by searching for a plausible configuration block.

Observed output - same tag, same placement, seconds apart. 001070C0 was written to block 0, detect reads it correctly, the first dump is correct then the second dump returns 0020E180, which is exactly rol1(001070C0):

[usb] pm3 --> lf t55xx write -b 0 -d 001070C0 --verify
[=] Writing page 0  block: 00  data: 0x001070C0
[!] ⚠️  Write could not validate the written data          <-- the write actually SUCCEEDED

[usb] pm3 --> lf t55xx detect
[=]  Modulation........ FSK2a
[=]  Block0............ 001070C0 (auto detect)             <-- detect is correct

[usb] pm3 --> lf t55xx dump
[+]  00 | 001070C0 |                                       <-- correct
[+]  05 | B532CCCC |                                       <-- rol1 of 5A996666
[usb] pm3 --> lf t55xx dump
[+]  00 | 0020E180 |                                       <-- rol1 of 001070C0
[+]  05 | 5A996666 |                                       <-- now correct

Three consequences, in increasing severity:

  1. lf t55xx dump returns wrong data that looks valid - no checksum, no warning.
  2. lf t55xx write --verify reports successful writes as failures, because it validates by reading back through the same path (see the first line above). Users re-write, or conclude a tag is faulty.
  3. lf t55xx restore of an affected dump writes the rotated data back permanently, so a tag can be made to hold a rotated copy of its own contents. This turns a read bug into data loss.

To Reproduce

  1. Place a T5577 on the LF antenna.
  2. Write a payload that is not byte-periodic - this matters, A5A5A5A5 lands on itself under many wrong rotations and hides the bug: lf t55xx write -b 1 -d 1D996666 --verify … through -b 6 -d 99699696 --verify
  3. Set an ASK/Manchester config and dump, to establish ground truth: lf t55xx write -b 0 -d 00088040 --verify then lf t55xx detect then lf t55xx dump → all blocks read back correctly.
  4. Switch to PSK1, the worst-affected modulation: lf t55xx write -b 0 -d 00081040 --verify
  5. lf t55xx detect → block 0 is reported correctly.
  6. lf t55xx dump twice → most blocks come back rotated, and not the same way in both dumps.

Repeat with -b 0 -d 001070C0 (FSK2a) and -b 0 -d 00080040 (DIRECT/NRZ) for the other affected cases.

Expected behavior

lf t55xx dump should return the block contents, or fail. It should not return a rotation of the block contents indistinguishable from a correct read. Specifically, the bit offset used to extract a block should be derived from the buffer being decoded, not inherited from an earlier capture. If that is not possible for data blocks, the read should refuse when the demodulator's start position cannot be established.

lf t55xx write --verify should not report a successful write as a validation failure.

Screenshots

Not applicable. All output is text and included above.

Desktop

  • OS: macOS 26.6.1, Apple Silicon (aarch64)
  • Proxmark: generic device (AT91SAM7S512 Rev A)
  • Firmware platform: reproduced on both PLATFORM=PM3GENERIC and PLATFORM=PM3RDV4 - see below.

hw version - the build all measurements below were taken on:

 [ Client ]
  Iceman/master/fb3ec70ae-dirty-suspect 2026-08-24 09:44:53 47337e9e7
  compiler: Clang/LLVM Apple LLVM 21.0.0 (clang-2100.1.1.101) OS:OSX ARCH:aarch64
 [ ARM ]
  Bootrom.... Iceman/master/fb3ec70ae-dirty-suspect 2026-08-24 09:44:52 47337e9e7
  OS......... Iceman/master/fb3ec70ae-dirty-suspect 2026-08-24 09:44:51 47337e9e7
  Compiler... GCC 13.3.1 20240614
 [ FPGA ]
 fpga_pm3_lf.ncd image 2s30vq100 18-07-2026 08:10:12
 [ Hardware ]
  --= uC: AT91SAM7S512 Rev A

Note: the -dirty is not a local patch: git status in that tree showed only tools/ePassport/ePassport deleted, which is the macOS case-collision reported in #3509. git status over client/ armsrc/ common/ common_arm/ include/ was empty and no client or firmware source was modified.

hw status:

[#] Memory
[#]   BigBuf_size............. 36480
[#]   Available memory........ 36480
[#] Current FPGA image
[#]   mode.................... fpga_pm3_hf.ncd image 2s30vq100 18-07-2026 08:10:12
[#] Flash memory
[#]   Memory size............. 1024 Kb ( 16 pages * 64k )
[#] LF Sampling config
[#]   [q] divisor............. 95 ( 125.00 kHz )
[#]   [b] bits per sample..... 8
[#]   [d] decimation.......... 1
[#]   [a] averaging........... yes
[#]   [t] trigger threshold... 0

hw tune (bare antenna, nothing on the pad):

[=] -------- LF Antenna ----------
[+] 125.00 kHz ........... 22.22 V
[+] 134.83 kHz ........... 14.44 V
[+] 117.65 kHz optimal.... 26.91 V
[+] Approx. Q factor measurement
[+] Frequency bandwidth... 6.6
[+] Peak voltage.......... 4.7
[!] Contradicting measures seem to indicate you're running a PM3_RDV4 firmware on a generic device
[!] False positives is possible but please check your setup
[+] LF antenna............ ok
[=] -------- HF Antenna ----------
[+] 13.56 MHz............. 61.23 V
[+] Approx. Q factor measurement
[+] Peak voltage.......... 10.7
[+] HF antenna ( ok )

Additional context

It is confined to the demodulators whose start point is not deterministic, which is what points at the cached offset rather than at signal quality. From DecodeT55xxBlock():

modulation demod call start determinism
ASK/Manchester ASKDemod_ext(..., &ST) anchored on the sequence terminator
Biphase ASKbiphaseDemod(0, ...) explicit offset = 0
FSK2a FSKrawDemod(clk, inv, 10, 8, ...) first fc transition found
DIRECT/NRZ NRZrawDemod(..., maxErr=1, ...) first detectable edge, not self-clocking
PSK1 PSKDemod(..., maxErr=6, ...) phase-ambiguous, and the only demod given maxErr = 6

Measured across four T5577 tags, two manufacturers, six modulations, all on fb3ec70. Each tag: write the payload, set ASK/Manchester and dump for ground truth, then set each modulation and dump twice. Every field classified against all 31 rotations of the true value; unwritable blocks excluded. Figures are corrupted fields / fields scored:

modulation blue-dual
(Invengo 0x45)
copper-coin
(SiCraft 0x39 ICR0)
orange-tag
(SiCraft ICR2)
white-coin
(SiCraft ICR0)
ASK/Manchester 0/24 0/24 0/16 0/24
Biphase 0/24 0/24 0/16 0/24
FSK2a mb6 4/24 rol1 0/24 3/16 ror1 7/24 ror1
FSK2a mb2 9/24 rol1 0/24 3/16 ror1 6/24 ror1
PSK1 11/12 † 22/24 ror4/ror5 14/16 ror5 22/24 ror4/ror5
DIRECT/NRZ 19/24 rol1 2/24 1/16 1/24

† half of blue-dual's PSK1 dumps returned no readable blocks at all - a total read failure rather than a misalignment.

  • Manchester and Biphase are clean on every tag - 0 of 88 fields each.
  • It is intermittent. Consecutive dumps of the same tag at the same placement disagree field by field.
  • It is ±1 bit, not a fixed off-by-one. Both directions appear on the same tag in the same run. PSK1 gives ror4 and ror5 on the same tag.
  • DIRECT is silicon-correlated (19/24 on Invengo, ≤2/24 on all three Silicon Craft tags); FSK2a is not - copper-coin is clean while white-coin, of identical manufacturer, part and IC revision (byte-identical page-1 b1 = E03900D0), reaches 7/24.

Firmware platform is a controlled variable, not a caveat. The default build is PLATFORM=PM3RDV4 (common_arm/Makefile.hal), and on generic hardware that makes hw tune print "Contradicting measures seem to indicate you're running a PM3_RDV4 firmware on a generic device". To remove that as an explanation, the whole probe was re-run on the same tag and the same commit with PLATFORM=PM3GENERIC (which clears the warning and drops the smartcard section from hw status):

config PM3RDV4 PM3GENERIC
Manchester clean 0/24 clean 0/24
Biphase clean 0/24 clean 0/24
FSK2a mb6 7/24 ror1 4/24 ror1
FSK2a mb2 6/24 ror1 4/24 ror1
PSK1 22/24 ror5×13 ror4×8 22/24 ror5×13 ror4×4
DIRECT 1/24 1/24

PSK1 is 22 of 24 on both platforms, with the same dominant rotation. FSK2a moved (7→4, 6→4), which is consistent with it being the coupling-sensitive case rather than with the platform fixing anything. The controls stayed clean. Antenna coupling was equivalent across the two runs - hw tune read 22.22 V and 22.21 V at 125 kHz.

The 2026-07-30 DSP rework did not fix it. 19cc98c reworked PSKDemod, FSKrawDemod, NRZrawDemod and ASKbiphaseDemod. The same tag measured on both sides of it:

config v4.21611 (Apr) fb3ec70 (Aug)
Manchester / Biphase clean clean
FSK2a mb6 / mb2 5/24, 17/24 7/24, 6/24
PSK1 22/24 22/24

No claim is made that the rate changed - the phenomenon is intermittent and that is one run per version.

On a fix. I have not sent a patch, deliberately: detect derives the offset by recognising a plausible configuration block, which works for block 0 and has nothing to latch onto on blocks 1–7, so "search per read" is not a drop-in. What the right design is - re-run detect internally, carry an alignment confidence out of the demodulator, or refuse when the start is not deterministic - seems a maintainer's call. Happy to test any candidate against the matrix above; the harness that produced it is scripted and re-runnable.

Related, but not duplicates

Limitations

  • One Proxmark, a generic (non-RDV4) device. Given Degraded or Non-Functional Write Performance for Some T5577 Credentials #182, hardware revision may matter.
  • No genuine Atmel ATA5577 tested - all four tags are clones (Invengo, Silicon Craft).
  • In the FSK2a result, form factor, individual die and placement are mutually confounded. The data shows the trigger is not silicon but does not show what it is.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions