
Herramienta para reconstruir imágenes de flash SPI mediante capturas de analizador lógico
spidump analiza capturas de analizador lógico de un dispositivo que lee su flash NOR SPI/QSPI durante el arranque y reconstruye la imagen de la flash a partir del tráfico de lectura — sin necesidad de leer directamente el chip. El conjunto de comandos de la flash está modelado con Scapy, por lo que una transacción de lectura es el mismo objeto lógico ya sea que viaje por un cable o por cuatro.
| Formato | Cabecera | Notas |
|---|---|---|
| Tabla del analizador "SPI" de Saleae Logic 2 | name,type,start_time,...,mosi,miso | filas enable/result/disable, un result por byte |
| Tabla cruda por byte de Saleae | Time [s],Packet ID,MOSI,MISO | una fila por byte, agrupadas por Packet ID |
| Exportación de QSPI-Analyzer | Time [s],Packet ID, Transaction State, DATA, Lines Used | máquina de estados: 1=cmd, 2=addr, 3=dummy, 4=data |
El plugin QSPI-Analyzer para Saleae Logic / KingstVIS decodifica el framing en modo quad
(p. ej. 0x6B Fast Read Quad Output, 1-1-4) que un analizador SPI simple no puede.
# 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 imprime el formato detectado, un histograma de transacciones por opcode, el tamaño de flash elegido y la cobertura de lectura. Las regiones no leídas se rellenan con 0xFF (el estado de NOR borrada), por lo que la salida es segura para alimentar directamente a binwalk.
Una lectura de flash es la misma transacción lógica independientemente del ancho del bus: un opcode, una dirección y una secuencia de bytes de datos. Las dos trazas siguientes leen los mismos cuatro bytes (el magic de SquashFS hsqs en 0x2D0000, tomado de bigger-boot.csv) en modo single-lane y quad:
(Regenerar con python docs/diagrams/mkwave.py; requiere wavedrom-cli, y rsvg-convert para los PNGs.)
{cmd, addr, data, lines}.SPIFlashCmd (Scapy) modela la cabecera de opcode / dirección / dummy; los
opcodes de lectura (tanto single como quad: 0x03 0x0B 0x6B 0xEB 0x6C 0x0C …) son todos
entradas en READ_COMMANDS.reconstruct_image reproduce cada lectura en un bytearray, rastreando la cobertura.Un arranque solo lee lo que necesita, por lo que la cobertura es parcial por diseño — se recuperan las regiones que el dispositivo realmente tocó (bootloader, kernel, rootfs montado). Para un dispositivo que arranca en single-lane y luego cambia a quad, capture ambas etapas y empalme las.