2、固件提取技术:硬件提取方法与软件提取方法
固件审计的第一步,说白了就是先把固件搞到手。你连二进制都拿不到,后面分析得再漂亮也是白搭。我这些年做过的项目里,大概有七成情况能用软件方法搞定,剩下三成就得动烙铁和逻辑分析仪了。这一章咱们就把两种路子都捋一遍。
2.1 硬件提取方法
硬件提取,就是直接跟芯片物理层打交道。我刚开始干这行时,总觉得拆芯片是件很酷的事,后来发现拆坏过几块板子就老实了。嗯,这里要注意——硬件提取是最后的手段,但也是最有保障的手段。
2.1.1 SPI Flash 读取
SPI Flash 是嵌入式设备里最常见的固件载体。你想想看,一个 8 脚的小芯片,里面可能装着整个路由器的操作系统。读取它的方法其实很直接:用编程器夹住或者焊线出来,然后通过软件把数据 dump 下来。
操作步骤:
- 识别芯片型号——看丝印,比如 Winbond 25Q128、MXIC 25L6406E。记下型号后去查 datasheet,确认电压和引脚定义。
- 连接编程器——我建议用 SOP8 夹子夹住芯片,如果夹不住就飞线。注意方向,别把 1 脚搞反了。
- 读取数据——用 flashrom、SPI Flash 编程器软件(如 CH341A 配套工具)执行读取命令。
- 校验完整性——读两遍,比对 MD5。不一致就重新夹。
常见 SPI Flash 引脚定义:
| 引脚编号 | 名称 | 功能 |
|---|---|---|
| 1 | CS# | 片选(低电平有效) |
| 2 | DO/IO1 | 数据输出 |
| 3 | WP# | 写保护 |
| 4 | GND | 地 |
| 5 | DI/IO0 | 数据输入 |
| 6 | CLK | 时钟 |
| 7 | HOLD# | 保持 |
| 8 | VCC | 电源(通常 3.3V) |
2.1.2 JTAG/SWD 调试接口
JTAG 和 SWD 是调试接口,但也能用来提取固件。我记得有一次遇到一个设备,SPI Flash 被 BGA 封装盖住了,根本没法夹。后来发现板子上有 JTAG 接口,直接用 OpenOCD 把整个 Flash 内容读出来了。
JTAG 与 SWD 对比:
| 特性 | JTAG | SWD |
|---|---|---|
| 引脚数 | 5(TCK、TMS、TDI、TDO、nTRST) | 2(SWCLK、SWDIO) |
| 速度 | 较慢 | 较快 |
| 适用芯片 | ARM、MIPS、FPGA 等 | ARM Cortex-M 系列 |
| 调试能力 | 完整(断点、单步、内存读写) | 完整 |
| 提取固件 | 通过内存 dump 或 Flash 控制器读取 | 通过内存 dump 或 Flash 控制器读取 |
用 OpenOCD 提取固件的典型命令:
# 连接目标板
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg
# 在 OpenOCD 终端中执行
flash read_bank 0 firmware.bin 0x08000000 0x100000
# 或者通过 GDB 连接后 dump 内存
(gdb) target remote :3333
(gdb) dump binary memory firmware.bin 0x08000000 0x08100000
2.2 软件提取方法
软件提取,就是通过设备上运行的操作系统或 Bootloader 来获取固件。这个方法风险低、速度快,但前提是你得有办法进入系统。
2.2.1 系统命令提取
如果你已经拿到了设备的 shell(不管是 telnet、SSH 还是串口),那提取固件就简单多了。说白了,就是找到固件分区,然后把它拷贝出来。
常用命令:
cat /proc/mtd——查看 MTD 分区表,确认固件分区名称和大小。dd if=/dev/mtdblock0 of=/tmp/firmware.bin——用 dd 命令把分区 dump 出来。scp /tmp/firmware.bin user@host:/path/——通过 SCP 传到本地。nc -l -p 1234 < /tmp/firmware.bin——用 netcat 发送,本地用nc target_ip 1234 > firmware.bin接收。
2.2.2 Bootloader 提取
Bootloader 是设备上电后第一个运行的程序。很多 Bootloader(如 U-Boot、RedBoot)都内置了内存读写和 Flash 操作命令。如果你能通过串口进入 Bootloader 的交互界面,那提取固件就手到擒来了。
U-Boot 常用命令:
# 查看 Flash 信息
U-Boot> flinfo
# 从 Flash 读取到内存
U-Boot> sf read 0x82000000 0x0 0x100000
# 从内存通过串口发送(YMODEM 协议)
U-Boot> loady 0x82000000 0x100000
# 或者用 tftp 上传到服务器
U-Boot> tftp 0x82000000 firmware.bin 0x100000
help 或 ? 看看支持哪些命令。不同版本的 U-Boot 命令集差异很大。我遇到过一台老设备,U-Boot 版本太旧,连 sf 命令都没有,最后只能用 cp.b 从 Flash 地址直接拷贝到内存。
2.3 知识体系总览
下面这张图把固件提取的两种路径和关键节点串起来了。你一看就明白什么时候该用什么方法。
2.4 方法选择决策树
在实际项目中,怎么选最省事?我一般按这个顺序来:
- 先试软件——有 shell 吗?有就 dd 或 scp。能进 Bootloader 吗?有就用 U-Boot 命令。
- 再试 SPI Flash——软件进不去?拆壳看 Flash 芯片,用夹子或编程器读。
- 最后 JTAG——Flash 被封装盖住了?或者有读保护?上 JTAG 试试。