
Strumento per la ricostruzione di immagini SPI flash tramite acquisizioni con analizzatore logico
spidump analizza le catture di un logic analyzer di un dispositivo che legge la sua flash NOR SPI/QSPI all'avvio e ricostruisce l'immagine della flash dal traffico di lettura — senza bisogno di una lettura diretta del chip. Il set di comandi della flash è modellato con Scapy, quindi una transazione di lettura è lo stesso oggetto logico sia che viaggi su un filo che su quattro.
| Formato | Intestazione | Note |
|---|---|---|
| Tabella dell'analizzatore "SPI" di Saleae Logic 2 | name,type,start_time,...,mosi,miso | righe enable/result/disable, un result per byte |
| Tabella grezza per-byte di Saleae | Time [s],Packet ID,MOSI,MISO | una riga per byte, raggruppate per Packet ID |
| Export di QSPI-Analyzer | Time [s],Packet ID, Transaction State, DATA, Lines Used | macchina a stati: 1=cmd, 2=addr, 3=dummy, 4=data |
Il plugin QSPI-Analyzer per Saleae Logic / KingstVIS decodifica il framing in modalità quad
(ad es. 0x6B Fast Read Quad Output, 1-1-4) che un semplice analizzatore SPI non riesce a fare.
# Standard single-lane SPI capture (Logic 2 SPI analyzer export)
python main.py examples/bigger-boot.csv -o recovered_flash.bin -v
# Quad SPI capture (QSPI-Analyzer export) — same command, format auto-detected
python main.py big-quad-spi-analyzed-dump.txt -o quad.bin -v
# Merge a single-lane leg (U-Boot) with a quad leg (rootfs) into one image.
# Devices that boot single-lane then switch to QSPI leave each region in a
# different capture; --merge splices them. Reads whose data lane width doesn't
# match the opcode (e.g. quad reads a single-lane analyzer mis-decoded) are
# dropped automatically, so a mixed capture can't poison the result.
python main.py single_lane_boot.csv --merge quad_boot.txt -o full.bin -v
# Raw exports with no chip-select framing (Packet ID constant) are split on
# idle gaps automatically (4x median byte period); override the threshold with
# --gap SECONDS, or --gap 0 to force Packet ID grouping
python main.py raw_boot.csv --gap 2e-6 -o raw.bin -v
# Force a flash size / fill byte if the auto-sizing guesses wrong
python main.py capture.txt --flash-size 0x1000000 --fill 0xFF -o out.bin
-v stampa il formato rilevato, un istogramma delle transazioni per opcode, la dimensione della flash scelta e la copertura delle letture. Le regioni non lette vengono riempite con 0xFF (lo stato della NOR cancellata), quindi l'output è sicuro da passare direttamente a binwalk.
Una lettura della flash è la stessa transazione logica indipendentemente dalla larghezza del bus: un opcode, un indirizzo e una sequenza di byte di dati. Le due tracce seguenti leggono gli stessi quattro byte (il magic di SquashFS hsqs a 0x2D0000, preso da
bigger-boot.csv) in modalità single-lane e quad:
(Rigenera con python docs/diagrams/mkwave.py; richiede wavedrom-cli, e rsvg-convert per i PNG.)
{cmd, addr, data, lines}.SPIFlashCmd (Scapy) modella l'intestazione opcode / indirizzo / dummy; gli
opcode di lettura (sia single che quad: 0x03 0x0B 0x6B 0xEB 0x6C 0x0C …) sono tutti
semplicemente voci in READ_COMMANDS.reconstruct_image riproduce ogni lettura in un bytearray, tenendo traccia della copertura.Un avvio legge solo ciò di cui ha bisogno, quindi la copertura è parziale per design — si recuperano le regioni che il dispositivo ha effettivamente toccato (bootloader, kernel, rootfs montato). Per un dispositivo che si avvia in single-lane e poi passa a quad, cattura entrambe le fasi e uniscile.