3. 升级协议与通信接口
做固件逆向分析,第一步就是拿到固件。这听起来简单,但实际坑不少。我见过太多人卡在第一步——要么读出来的数据是错的,要么直接把芯片搞废了。这一章,咱们就把固件获取这件事彻底讲透。
3.1 固件存储介质:Flash 与 EEPROM
嵌入式设备里,固件通常存在两种介质里:Flash 和 EEPROM。它们都是非易失性存储,但脾气完全不同。
| 特性 | Flash | EEPROM |
|---|---|---|
| 存储密度 | 高(MB~GB级) | 低(KB级) |
| 擦写单位 | 块/扇区(通常4KB~256KB) | 字节 |
| 擦写寿命 | 10万次左右 | 100万次左右 |
| 读取速度 | 快(50~100MB/s) | 较慢(1~2MB/s) |
| 典型用途 | 主固件、文件系统 | 配置参数、校准数据 |
Flash 又分 NOR 和 NAND 两种。NOR Flash 支持随机访问,可以直接在上面执行代码,很多 Bootloader 就放在 NOR Flash 里。NAND Flash 密度大、成本低,但坏块多,需要 ECC 纠错。我拆过一个工业路由器,它的 Bootloader 放在 NOR 里,主固件放在 NAND 里——这种组合很常见。
3.2 硬件读取方法
3.2.1 编程器读取
编程器是最直接的读取方式。把芯片拆下来,夹到编程器上,点一下读取就完事了。但这里有个问题——拆芯片。
我建议你准备一把热风枪和一把烙铁。QFP 封装的芯片用热风枪吹下来,SOIC 封装的用烙铁拖焊。拆之前拍个照片,记下芯片方向。我曾经有一次把芯片装反了,通电瞬间闻到一股焦味……嗯,那片子直接报废了。
常用的编程器有这些:
- CH341A: 便宜,支持常见 SPI Flash,但速度慢,适合小容量芯片
- TL866系列: 支持芯片多,速度快,我主力用这个
- RT809F: 电视主板维修常用,支持在线读取
- Xgecu T48: 高端货,几乎支持所有芯片,价格也感人
3.2.2 JTAG/SWD 读取
如果不想拆芯片,JTAG 或 SWD 是更好的选择。JTAG 有 4 根线(TMS、TCK、TDI、TDO),SWD 只有 2 根(SWDIO、SWCLK)。ARM Cortex-M 系列基本都支持 SWD。
读取流程大致是这样:
- 找到板子上的 JTAG/SWD 接口(通常是 10pin 或 20pin 排针)
- 连接调试器(J-Link、ST-Link、OpenOCD 都行)
- 用工具读取 Flash 内容
用 OpenOCD 读取 Flash 的命令示例:
# 连接目标芯片
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg
# 在 OpenOCD 命令行中
flash read_bank 0 firmware.bin 0x08000000 0x100000
# 或者用脚本一次性搞定
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg \
-c "init" \
-c "flash read_bank 0 firmware.bin 0x08000000 0x100000" \
-c "shutdown"
这里要注意,有些芯片有读保护(RDP)。如果 RDP 等级是 1 或 2,JTAG/SWD 是读不出 Flash 的。我遇到过一台设备,RDP 等级设为 2,这意味着读保护不可逆——只能拆芯片用编程器硬读,或者找漏洞绕过。
3.3 软件提取方法
3.3.1 通过 Bootloader 提取
很多设备的 Bootloader 支持固件升级,这给了我们一个提取机会。常见的 Bootloader 有 U-Boot、RedBoot、Barebox 等。
以 U-Boot 为例,如果设备启动时能进入 U-Boot 命令行,可以用这些命令导出固件:
# 查看 Flash 分区
U-Boot> mtdparts
# 读取 Flash 到内存
U-Boot> sf read 0x82000000 0x0 0x100000
# 通过 tftp 上传到电脑
U-Boot> tftp 0x82000000 firmware.bin 0x100000
但现实往往没那么顺利。很多设备会锁掉 U-Boot 的控制台,或者只给一个阉割版的命令行。我拆过一个 IP Camera,它的 U-Boot 只支持两个命令:boot 和 update。这种情况下,要么找漏洞提权,要么走硬件路线。
3.3.2 通过系统命令提取
如果设备已经跑起了 Linux,而且你能拿到 shell(通过 SSH、Telnet 或串口),那提取固件就简单多了。
常用的命令组合:
# 查看分区表
cat /proc/mtd
# 或者
cat /proc/partitions
# 备份整个 Flash
dd if=/dev/mtd0 of=/tmp/bootloader.bin
dd if=/dev/mtd1 of=/tmp/firmware.bin
# 如果设备有网络,直接传到电脑
scp /tmp/firmware.bin user@192.168.1.100:/home/user/
# 或者用 nc 传
# 电脑端监听:nc -l -p 1234 > firmware.bin
# 设备端发送:cat /tmp/firmware.bin | nc 192.168.1.100 1234
这里有个坑——有些设备会限制 dd 命令的权限,或者把 /dev/mtd 设备节点的权限设成 600。我遇到过一台设备,root 用户都读不了 /dev/mtd0,后来发现是 SELinux 在作怪。关掉 SELinux 或者改策略就解决了。
dd 命令,可以用 cat 配合 head 和 tail 来分段读取。比如 cat /dev/mtd0 | head -c 65536 > part1.bin。虽然慢,但能用。
3.4 知识体系总览
下面这张图把固件获取的整个知识体系串起来了。你可以把它当作一个决策树——从拿到设备开始,一步步选择最合适的提取方法。
3.5 方法选择决策指南
面对一台新设备,怎么选方法?我一般按这个顺序试:
- 先试软件方法: 如果能拿到 shell,优先用系统命令提取。省事,不用拆机。
- 再试 Bootloader: 如果 shell 拿不到,看启动时能不能进 Bootloader 命令行。
- 最后上硬件: 软件路走不通,再拆机用编程器或 JTAG。
你想想看,拆机是有风险的——弄坏焊盘、搞丢电阻电容、甚至把多层板搞分层,这些我都见过。所以能不动烙铁就不动烙铁。
好了,这一章的内容就到这里。记住我说的——拿到固件只是第一步,后面还有解包、分析、调试等着你。但这一步走稳了,后面的路就好走了。