3. 固件获取与提取:从Flash芯片读取、从OTA更新包提取、从开发板/调试接口获取

固件逆向的第一步,也是最关键的一步——你得先拿到固件。没有固件,后面所有的分析都是空谈。

我见过不少新手,兴致勃勃地买了开发板,结果发现芯片是加密的,读不出来。或者从网上下载了OTA包,解压出来一堆乱码。嗯,这些坑我都踩过。今天咱们就把固件获取的三种主流路径讲透。

3.1 从Flash芯片直接读取

这是最“硬核”的方式。你手上有硬件,直接拿编程器把Flash里的二进制内容读出来。说白了,就是物理提取。

3.1.1 识别Flash芯片型号

拿到板子先别急着上编程器。先找Flash芯片,看清楚型号。常见的封装有SOIC-8、SOIC-16、WSON-8等。我个人习惯先用手机微距镜头拍张照,放大看丝印。

举个例子,芯片上印着“W25Q64FVSSIG”,那就是Winbond的64Mbit SPI Flash。查数据手册,确认引脚定义和供电电压。

小技巧: 如果丝印被磨掉或涂黑,别慌。用万用表测引脚对地电阻,或者查PCB走线连到主控的哪些引脚,反向推断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
注意: 有些Flash芯片有“状态寄存器保护位”,读之前一定要检查。我曾经遇到过一块板子,读出来的全是0xFF,折腾了半天才发现是状态寄存器被锁了。

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)}")
关键点: OTA包里的固件可能是“差分更新”的,只包含变化的部分。这时候你需要拿到旧版本固件,用差分算法(如bsdiff)反向还原。

3.2.3 处理加密OTA包

遇到加密包怎么办?先别急着放弃。检查一下:

  • 包内是否有公钥或证书?
  • 是否有未加密的头部或尾部?
  • 固件升级时,是否通过调试串口打印了明文?

我曾经分析过一个摄像头,它的OTA包前半部分是加密的,但尾部有一段明文日志,里面居然包含了AES密钥。嗯,有时候厂商的疏忽就是我们的突破口。

3.3 从开发板/调试接口获取

如果你有开发板,或者设备上有调试接口,那获取固件就方便多了。这是我最喜欢的方式,因为不用拆芯片。

3.3.1 利用JTAG/SWD接口

ARM芯片通常有SWD接口(SWDIO和SWCLK)。接上J-Link或ST-Link,可以直接读取Flash内容。

步骤:

  1. 找到板子上的SWD接口(通常是4针或5针排针)。
  2. 连接调试器:SWDIO->DIO,SWCLK->CLK,GND->GND,VCC->VCC(可选)。
  3. 使用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
警告: 有些芯片有读保护(RDP Level 1或2)。Level 1可以用漏洞绕过,Level 2基本没戏,只能换芯片。我建议先读一下选项字节,确认保护级别。

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查看熵值分布,判断是否加密或压缩。
我的习惯: 每次读取完固件,我都会做一次MD5校验,并记录读取时的环境参数(温度、电压、夹子型号)。这样如果后续发现数据有问题,可以快速复现。

3.5 本章知识体系

下面这张图总结了固件获取的三种路径和关键步骤,方便你对照参考。

固件获取与提取知识体系 固件获取与提取核心路径 从Flash芯片读取 从OTA更新包提取 从调试接口获取 识别芯片型号 编程器读取 异常处理 识别格式 解包提取 加密处理 JTAG/SWD U-Boot 串口打印 固件验证与完整性检查 核心原则 先软后硬:优先尝试OTA和调试接口,最后才拆芯片。每次读取都要做MD5校验。

好了,三种路径都讲完了。从Flash芯片读取最直接,但需要硬件和耐心;OTA包提取最方便,但可能遇到加密;调试接口最快,但需要设备支持。我个人建议你三种方法都掌握,因为实际项目中你永远不知道会遇到哪种情况。