用于通过逻辑分析仪捕获重建 SPI flash 镜像的工具
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 将它们拼接起来。数据通道宽度与操作码
# 不匹配的读取(例如四线读取被单通道分析仪错误解码)会被
# 自动丢弃,因此混合捕获不会污染结果。
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 会打印检测到的格式、按操作码统计的事务直方图、选定的闪存大小以及读取覆盖率。未读取的区域会填充为 0xFF(NOR 擦除后的状态),因此输出可直接送入 binwalk。
无论线宽如何,闪存读取都是同一个逻辑事务:一个操作码、一个地址,以及一串数据字节。下面两个跟踪读取的是相同的四个字节(位于 0x2D0000 的 SquashFS 魔数 hsqs,取自 bigger-boot.csv),分别以单通道和四线模式进行:
(使用 python docs/diagrams/mkwave.py 重新生成;需要 wavedrom-cli,生成 PNG 还需要 rsvg-convert。)
{cmd, addr, data, lines}。SPIFlashCmd(Scapy)对操作码 / 地址 / 空周期头部建模;读取
操作码(单线和四线:0x03 0x0B 0x6B 0xEB 0x6C 0x0C …)都只是
READ_COMMANDS 中的条目。reconstruct_image 将每次读取重放到 bytearray 中,并跟踪覆盖率。启动过程只读取它需要的内容,因此覆盖率在设计上就是部分的——你恢复的是设备实际访问过的区域(引导加载程序、内核、已挂载的 rootfs)。对于先以单通道启动再切换到四线的设备,请捕获两个阶段并将它们拼接起来。