
로직 분석기 캡처를 통해 SPI 플래시 이미지를 재구성하는 도구
spidump는 부팅 시 SPI/QSPI NOR 플래시를 읽는 디바이스의 로직 분석기 캡처를 파싱하여 읽기 트래픽으로부터 플래시 이미지를 재구성합니다 — 칩을 직접 읽을 필요가 없습니다. 플래시 명령어 집합은 Scapy로 모델링되므로, 읽기 트랜잭션은 한 개의 와이어로 전송되든 네 개로 전송되든 동일한 논리적 객체입니다.
| 형식 | 헤더 | 비고 |
|---|---|---|
| Saleae Logic 2 "SPI" 분석기 테이블 | name,type,start_time,...,mosi,miso | enable/result/disable 행, 바이트당 하나의 result |
| Saleae 원시 바이트별 테이블 | Time [s],Packet ID,MOSI,MISO | 바이트당 한 행, Packet ID로 그룹화 |
| QSPI-Analyzer 내보내기 | Time [s],Packet ID, Transaction State, DATA, Lines Used | 상태 머신: 1=cmd, 2=addr, 3=dummy, 4=data |
Saleae Logic / KingstVIS용 QSPI-Analyzer 플러그인은 일반 SPI 분석기가 처리할 수 없는 쿼드 모드 프레이밍(예: 0x6B Fast Read Quad Output, 1-1-4)을 디코딩합니다.
# 표준 단일 레인 SPI 캡처 (Logic 2 SPI 분석기 내보내기)
python main.py examples/bigger-boot.csv -o recovered_flash.bin -v
# 쿼드 SPI 캡처 (QSPI-Analyzer 내보내기) — 동일한 명령, 형식 자동 감지
python main.py big-quad-spi-analyzed-dump.txt -o quad.bin -v
# 단일 레인 구간(U-Boot)과 쿼드 구간(rootfs)을 하나의 이미지로 병합.
# 단일 레인으로 부팅한 후 QSPI로 전환하는 디바이스는 각 영역을
# 서로 다른 캡처에 남기며, --merge가 이를 이어 붙입니다. 데이터 레인 폭이
# opcode와 일치하지 않는 읽기(예: 쿼드 읽기를 단일 레인 분석기가 잘못 디코딩한 경우)는
# 자동으로 제거되므로, 혼합 캡처가 결과를 오염시킬 수 없습니다.
python main.py single_lane_boot.csv --merge quad_boot.txt -o full.bin -v
# 칩 선택 프레이밍이 없는 원시 내보내기(Packet ID 상수)는
# 유휴 갭(중앙값 바이트 주기의 4배)을 기준으로 자동 분할됩니다. 임계값은
# --gap SECONDS로 재정의하거나, --gap 0으로 Packet ID 그룹화를 강제할 수 있습니다
python main.py raw_boot.csv --gap 2e-6 -o raw.bin -v
# 자동 크기 추정이 잘못된 경우 플래시 크기 / 채움 바이트를 강제 지정
python main.py capture.txt --flash-size 0x1000000 --fill 0xFF -o out.bin
-v는 감지된 형식, opcode별 트랜잭션 히스토그램, 선택된 플래시 크기, 읽기 커버리지를 출력합니다. 읽지 않은 영역은 0xFF(지워진 NOR 상태)로 채워지므로, 출력을 binwalk에 바로 입력해도 안전합니다.
플래시 읽기는 와이어 폭에 관계없이 동일한 논리적 트랜잭션입니다: opcode, 주소, 그리고 데이터 바이트의 연속입니다. 아래 두 트레이스는 동일한 4바이트(0x2D0000에 있는 SquashFS 매직 hsqs, bigger-boot.csv에서 가져옴)를 단일 레인 및 쿼드 모드로 읽습니다:
(python docs/diagrams/mkwave.py로 재생성; wavedrom-cli가 필요하며, PNG의 경우 rsvg-convert가 필요합니다.)
{cmd, addr, data, lines}로 변환합니다.SPIFlashCmd(Scapy)가 opcode / 주소 / 더미 헤더를 모델링합니다. 읽기
opcode(단일 및 쿼드: 0x03 0x0B 0x6B 0xEB 0x6C 0x0C …)는 모두
READ_COMMANDS의 항목일 뿐입니다.reconstruct_image가 모든 읽기를 bytearray에 재생하며 커버리지를 추적합니다.부팅은 필요한 것만 읽으므로 커버리지는 설계상 부분적입니다 — 디바이스가 실제로 접근한 영역(부트로더, 커널, 마운트된 rootfs)을 복구합니다. 단일 레인으로 부팅한 후 쿼드로 전환하는 디바이스의 경우, 두 구간을 모두 캡처하여 이어 붙이십시오.