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

做固件逆向这么多年,我经常被问到同一个问题:「固件到底从哪来?」

说实话,这个问题比想象中复杂。你想想看,一个嵌入式设备摆在面前,固件可能藏在Flash芯片里,可能躲在OTA更新包里,也可能通过调试接口偷偷溜出来。每种方式都有自己的门道,也有各自的坑。

今天我就把这三种主流获取方式掰开揉碎了讲清楚。嗯,都是实战中摸爬滚打出来的经验。

3.1 从Flash芯片读取固件

这是最直接的方式——把芯片拆下来,用编程器读。但直接归直接,操作起来有不少讲究。

3.1.1 识别Flash芯片型号

拿到一块PCB板,先找Flash芯片。常见的封装有SOIC-8、SOIC-16、WSON-8、BGA等。芯片表面通常会印着型号,比如Winbond的W25Q128、Macronix的MX25L25635F。

我个人习惯先用手机微距镜头拍张照,放大看型号。有些芯片表面被磨掉了标记——嗯,遇到过不少次,厂家故意防逆向。这时候就得靠经验判断了:

  • 引脚数量:8脚通常是SPI Flash,48脚以上可能是NAND Flash
  • 供电电压:3.3V是主流,1.8V多见于低功耗设备
  • 容量标识:128、256这些数字往往代表Mbit
小技巧:不确定型号时,可以用万用表测VCC和GND引脚,确认供电电压。我曾经遇到过标称3.3V的芯片,实际是1.8V的,差点烧了芯片。

3.1.2 使用编程器读取

常用的编程器有CH341A、FT2232H、树莓派(没错,它也能当编程器用)。连接方式很简单:

# 使用flashrom工具读取SPI Flash
flashrom -p ch341a_spi -r firmware.bin

# 指定芯片型号(如果自动检测失败)
flashrom -p ch341a_spi -c "W25Q128.V" -r firmware.bin

# 验证读取结果
flashrom -p ch341a_spi -v firmware.bin

这里有个关键点:读取速度。默认速度可能太快导致数据出错。我一般把SPI频率降到1MHz以下,虽然慢点,但数据可靠。

警告:有些Flash芯片有写保护引脚(WP#)和保持引脚(HOLD#),必须拉高到VCC才能正常读取。我曾经因为没接这两个引脚,读出来的数据全是0xFF,折腾了半天才发现问题。

3.1.3 在线读取 vs 离线读取

方式 优点 缺点
在线读取(夹子夹住) 无需拆焊,操作简单 受电路干扰,可能读错
离线读取(拆下芯片) 数据准确,不受干扰 需要焊接,可能损坏芯片

我的建议是:优先用在线方式试读,如果数据异常再拆下来。毕竟拆焊一次,板子就少一分完整性。

3.2 从OTA更新包提取固件

很多设备不让你拆,但会通过OTA推送更新。这反而是个突破口——更新包就在你手里,就看你能不能解开。

3.2.1 抓取OTA更新包

方法主要有三种:

  1. 抓包:用Wireshark或Burp Suite拦截设备与服务器的通信
  2. 日志分析:查看设备日志中的下载链接
  3. 逆向App:从手机App中提取更新包的URL

我记得有一次分析某智能摄像头,更新包藏在App的so文件里,硬编码了一个加密的URL。折腾了两天才找到解密密钥——其实就是设备序列号的前8位。

3.2.2 解包OTA文件

OTA更新包常见的格式有:

  • ZIP压缩包:直接unzip解压
  • 加密ZIP:需要找到密码或解密算法
  • 差分更新包:需要结合旧版本还原
  • 自定义格式:头部有魔数,需要逆向解析
# 解压标准ZIP
unzip update.zip -d firmware_dir/

# 查看文件头(判断格式)
hexdump -C update.bin | head -20

# 如果是差分更新,使用bsdiff还原
bspatch old_firmware.bin new_firmware.bin update.patch
重点:看到PK开头(0x50 0x4B)就是ZIP格式。看到一串乱码?那八成是加密了。别慌,先找找设备里有没有硬编码的密钥。

3.2.3 处理加密和签名

现在大部分厂商都会对OTA包加密签名。常见的手法:

  • AES加密:密钥可能藏在Bootloader或App中
  • RSA签名:验证固件完整性,但通常不加密内容
  • 自定义算法:异或、位移等简单变换

我曾经遇到过一个奇葩案例:厂商用「固件大小」作为AES密钥的一部分。我试了各种方法都解不开,最后发现密钥是文件大小+固定字符串的MD5值。你说这设计思路...嗯,确实够「巧妙」的。

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

如果你手头有开发板,或者设备上留有调试接口,那获取固件就轻松多了。

3.3.1 利用JTAG/SWD接口

JTAG和SWD是嵌入式设备的「后门」。只要找到接口,就能直接读取内存和Flash。

# 使用OpenOCD通过JTAG读取Flash
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg \
  -c "init" \
  -c "flash read_bank 0 firmware.bin 0 0x100000" \
  -c "shutdown"

找JTAG接口有个诀窍:看PCB上有没有排针或测试点,通常会有TCK、TMS、TDI、TDO、GND这几个信号。用万用表量一下,TCK和TMS一般会连接到CPU的对应引脚。

经验之谈:有些设备会禁用JTAG接口(通过熔丝位或efuse)。但别灰心,SWD接口往往还开着。SWD只需要SWDIO和SWCLK两根线,更容易找到。

3.3.2 通过UART Bootloader读取

很多MCU出厂自带Bootloader,通过UART就能读取固件。比如STM32的系统Bootloader:

# 使用stm32flash工具读取
stm32flash -r firmware.bin /dev/ttyUSB0

# 指定起始地址和长度
stm32flash -S 0x08000000:0x100000 -r firmware.bin /dev/ttyUSB0

进入Bootloader的方式各不相同:有的需要拉高BOOT0引脚,有的需要在启动时发送特定字符。我建议先查芯片手册,别盲目尝试。

3.3.3 利用调试漏洞

有些设备虽然锁了调试接口,但存在漏洞可以绕过。比如:

  • 电压毛刺攻击:在CPU启动时注入电压干扰,跳过安全检查
  • 时钟毛刺攻击:改变时钟频率,导致指令执行错误
  • 激光攻击:用激光照射芯片特定区域,改变存储单元状态

这些方法比较高级,需要专门的设备。我个人只在实验室里玩过几次电压毛刺,成功率大概30%左右。说白了,还是找软件漏洞更靠谱。

3.4 本章知识体系

下面这张图总结了固件获取的三种途径和关键步骤:

固件获取与提取知识体系 从Flash芯片读取 识别芯片型号 编程器连接与读取 在线 vs 离线读取 从OTA更新包提取 抓取更新包 解包(ZIP/差分/加密) 解密与签名绕过 从调试接口获取 JTAG/SWD接口 UART Bootloader 调试漏洞利用 获取原始固件二进制文件 固件分析:文件系统提取 → 内核解析 → 漏洞挖掘 ⚠ 注意:读取前先备份!注意静电防护!确认电压匹配!

3.5 避坑指南

最后,分享几个我踩过的坑:

  • 别信芯片标签:标称128Mbit的芯片,实际可能只有64Mbit。读之前先确认容量。
  • OTA包可能不完整:有些更新包只包含差异部分,需要结合旧固件才能还原。
  • 调试接口可能被锁:别一上来就焊线,先测测接口有没有反应。
  • 注意固件校验:读出来的固件先算个MD5,和官方版本对比一下。

我曾经有一次,花了一整天读了一个Flash芯片,结果发现读出来的数据全是0xFF——原来是夹子没夹好,虚焊了。从那以后,我每次读取完都会先检查文件头,确认不是空数据。

好了,固件获取的方法就讲到这里。记住,没有万能的方案,只有最适合当前设备的方案。多试几种方法,总有一条路能走通。


专注资料整理