Fix lf em 4x05 dump writing corrupted (byte-swapped) dump files#3409
Conversation
em4x05_print_blocks() byte-swaps its buffer in place to display the blocks. In CmdEM4x05Dump() that call runs before pm3_save_dump(), so the buffer handed to the saver has been swapped out of the canonical on-disk order and the saved .bin/.json reloads with every block reversed. The read loop stores each word as data[addr] = BSWAP_32(word), which is what the loader in CmdEM4x05View() expects (it reads blocks MSB-first via bytes_to_num()). The extra in-place swap in the print routine undoes that right before the save. It also corrupts the auto-generated filename, which is built from BSWAP_32(data[1]) after the print call. Symmetric words such as 0x00000000 survive the swap, so it's easy to miss. Give em4x05_print_blocks() a private copy to swap and print, and take the input as const so it can't mutate the caller's buffer. On-screen output is unchanged; the dump path now saves the untouched buffer. Fixes RfidResearchGroup#3403
|
The proxspace failure isn't from this change. The last two Windows Build and Test runs on master fail the same job: https://github.com/RfidResearchGroup/proxmark3/actions/runs/28683540784 and https://github.com/RfidResearchGroup/proxmark3/actions/runs/28682164863. Ubuntu, macOS and CodeQL are green here. |
| // dlen is a uint8_t, so 64 words is enough to hold any valid input. | ||
| uint32_t d[64] = {0}; | ||
| memcpy(d, src, dlen); | ||
| uint8_t *data = (uint8_t *)d; |
There was a problem hiding this comment.
Why fiddle with the extra pointer if you gonna use a local copy?
and whats up with the uint32_t ?
There was a problem hiding this comment.
Both are needed, not extra: the rest of the function reads the buffer two different ways. d[i] is used for the byte-swapped 32-bit words (block values, lock bits, protection checks), and data + (i * EM4X05_BLOCK_SIZE) is used a few lines down for reverse_array_copy(), which wants a byte pointer for the ASCII display column. That dual usage already existed before this PR - I just moved it from operating on the caller's buffer to operating on a local copy, so the byte-swap doesn't corrupt what CmdEM4x05Dump() later writes to disk.
uint32_t specifically because d[i] = BSWAP_32(d[i]) is a 32-bit word swap - has to be a 4-byte type for that to mean anything, and the block-indexed reads (d[EM4305_PROT1_BLOCK] etc.) further down are all word-sized too.
data is just (uint8_t *)d - a byte-typed alias over the same local array, not a second buffer.
What
lf em 4x05 dumpwrites a corrupted dump file. Every 32-bit block in the saved.bin/.jsoncomes out byte-reversed, so reloading it (withlf em 4x05 viewor anything else) gives the wrong data.Why
em4x05_print_blocks()byte-swaps its buffer in place to print the blocks:In
CmdEM4x05Dump()that print call runs beforepm3_save_dump(). The read loop stores each word asdata[addr] = BSWAP_32(word), which is the order the loader inCmdEM4x05View()expects — it reads blocks MSB-first viabytes_to_num(). The extra in-place swap in the print routine undoes that right before the save, so the file lands in the wrong byte order.It also corrupts the auto-generated filename, which is built from
BSWAP_32(data[1])after the print call.Symmetric words like
0x00000000survive a byte-swap unchanged, which is why this is easy to miss at a glance.Fix
Give
em4x05_print_blocks()a private copy to swap and print, and take the input asconstso it can't mutate the caller's buffer. On-screen output is byte-for-byte identical; the dump path now saves the untouched buffer. Both call sites stay correct —viewfrees its buffer right after and never relied on the mutation.Checking it
Modelling the exact transforms (
BSWAP_32, the little-endian save, and the loader's MSB-firstbytes_to_num) and round-tripping a full 16-block tag:0102030404030201✗01020304✓DEADBEEFEFBEADDE✗DEADBEEF✓0000000000000000✓00000000✓Before the fix the saved dump reloads byte-swapped; after, it round-trips cleanly.
Possible follow-up (not part of this change)
While in here I noticed
CmdEM4x05View()passes asize_t bytes_readinto theuint8_t dlenparameter, which silently truncates on an oversized or malformed dump file. That's pre-existing and untouched by this PR — flagging it as a separate hardening item so it doesn't look overlooked.Fixes #3403