3. 固件获取与提取:从Flash芯片读取、从OTA更新包提取、从开发板/调试接口获取
固件逆向的第一步,也是最关键的一步——你得先拿到固件。没有固件,后面所有的分析都是空谈。
我见过不少新手,兴致勃勃地买了开发板,结果发现芯片是加密的,读不出来。或者从网上下载了OTA包,解压出来一堆乱码。嗯,这些坑我都踩过。今天咱们就把固件获取的三种主流路径讲透。
3.1 从Flash芯片直接读取
这是最“硬核”的方式。你手上有硬件,直接拿编程器把Flash里的二进制内容读出来。说白了,就是物理提取。
3.1.1 识别Flash芯片型号
拿到板子先别急着上编程器。先找Flash芯片,看清楚型号。常见的封装有SOIC-8、SOIC-16、WSON-8等。我个人习惯先用手机微距镜头拍张照,放大看丝印。
举个例子,芯片上印着“W25Q64FVSSIG”,那就是Winbond的64Mbit SPI Flash。查数据手册,确认引脚定义和供电电压。
3.1.2 使用编程器读取
常用的编程器有CH341A、FT2232H、树莓派GPIO模拟等。我个人最常用的是FT2232H,稳定,速度快。
接线时注意:
- VCC:确认电压是3.3V还是1.8V,接错会烧芯片。
- GND:共地,必须接。
- CS/CLK/DI/DO:对应SPI的四根线。
读取命令示例(使用flashrom工具):
# 检测Flash芯片
flashrom -p ft2232_spi:type=2232H -V
# 读取整个Flash内容到文件
flashrom -p ft2232_spi:type=2232H -r firmware_dump.bin
# 如果芯片有写保护,先解除
flashrom -p ft2232_spi:type=2232H --wp-disable
3.1.3 处理读取异常
读出来的数据不对怎么办?常见情况:
- 全0xFF或全0x00:芯片没选中,或者CS引脚没拉低。
- 数据重复:地址线接触不良,或者时钟频率太高。
- 校验失败:读两遍,比对MD5。不一致就重新夹好夹子再读。
3.2 从OTA更新包提取
很多时候你拿不到硬件,只能拿到一个.bin或.zip的更新包。OTA包是固件逆向的“软入口”。
3.2.1 识别OTA包格式
OTA包通常不是裸固件,而是经过打包的。常见格式有:
| 格式 | 特征 | 常见于 |
|---|---|---|
| ZIP/RAR | 文件头PK/ Rar! | 路由器、智能家居 |
| TRX | 文件头“HDR0” | Broadcom方案路由器 |
| BIN (raw) | 无文件头,直接是固件 | 单片机、IoT设备 |
| 加密包 | 熵值高,无可见字符串 | 商业设备、摄像头 |
我一般先用binwalk扫描一下,看看里面有没有文件系统或已知签名。
binwalk -Me firmware_update.bin
3.2.2 解包与提取
如果是ZIP,直接解压。如果是TRX或自定义格式,需要分析头部结构。
举个例子,一个典型的TRX头部:
struct trx_header {
uint32_t magic; // 0x30524448 ("HDR0")
uint32_t len; // 总长度
uint32_t crc32; // CRC校验
uint32_t flag_version; // 标志位和版本
uint32_t offsets[3]; // 三个分区的偏移
};
用Python解析:
import struct
with open('firmware.trx', 'rb') as f:
header = f.read(28)
magic, length, crc, flag_ver, off0, off1, off2 = struct.unpack('<IIIIIII', header)
print(f"Magic: {hex(magic)}, Length: {length}, CRC: {hex(crc)}")
3.2.3 处理加密OTA包
遇到加密包怎么办?先别急着放弃。检查一下:
- 包内是否有公钥或证书?
- 是否有未加密的头部或尾部?
- 固件升级时,是否通过调试串口打印了明文?
我曾经分析过一个摄像头,它的OTA包前半部分是加密的,但尾部有一段明文日志,里面居然包含了AES密钥。嗯,有时候厂商的疏忽就是我们的突破口。
3.3 从开发板/调试接口获取
如果你有开发板,或者设备上有调试接口,那获取固件就方便多了。这是我最喜欢的方式,因为不用拆芯片。
3.3.1 利用JTAG/SWD接口
ARM芯片通常有SWD接口(SWDIO和SWCLK)。接上J-Link或ST-Link,可以直接读取Flash内容。
步骤:
- 找到板子上的SWD接口(通常是4针或5针排针)。
- 连接调试器:SWDIO->DIO,SWCLK->CLK,GND->GND,VCC->VCC(可选)。
- 使用OpenOCD或JLinkExe连接目标芯片。
OpenOCD示例:
# 启动OpenOCD,连接STM32F4系列
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg
# 在telnet或gdb中执行
flash read_bank 0 firmware_dump.bin 0x08000000 0x100000
3.3.2 利用U-Boot或Bootloader
如果设备启动后能进入U-Boot命令行,那固件基本就是白送的。
常用命令:
md:内存显示,可以查看Flash内容。sf read:从SPI Flash读取数据到内存。tftp:通过网络上传/下载固件。
示例:
# 在U-Boot中读取SPI Flash到内存地址0x82000000
sf probe 0
sf read 0x82000000 0x0 0x100000
# 通过TFTP上传到电脑
tftp 0x82000000 firmware_dump.bin 0x100000
3.3.3 利用串口打印
有些设备在启动时会通过串口打印固件内容。虽然慢,但聊胜于无。
我遇到过一台工业路由器,它的Bootloader会在启动时把整个Flash内容通过串口打印出来。我用Python写了个脚本,监听串口,把数据保存成文件。虽然花了20分钟,但固件到手了。
3.4 固件提取后的验证
拿到固件文件后,别急着分析。先验证一下完整性:
- 文件大小:是否和Flash容量匹配?
- 文件头:是否有已知的魔数(如0xFE 0xED 0x0C 0x0A)?
- 字符串:用
strings命令看看有没有可读的文本。 - 熵值分析:用
binwalk -E查看熵值分布,判断是否加密或压缩。
3.5 本章知识体系
下面这张图总结了固件获取的三种路径和关键步骤,方便你对照参考。
好了,三种路径都讲完了。从Flash芯片读取最直接,但需要硬件和耐心;OTA包提取最方便,但可能遇到加密;调试接口最快,但需要设备支持。我个人建议你三种方法都掌握,因为实际项目中你永远不知道会遇到哪种情况。