☰
单片机固件提取与逆向分析实战指南:从调试接口到SPI Flash
2026/10/9 11:10:37 网站建设 项目流程

1. 固件提取与分析方法全景概览

1.1 为什么固件分析是嵌入式开发者的必修课

做单片机开发的人,迟早会遇到一个绕不开的场景:手里有一块板子,上面跑着一个功能很完整的产品固件,但你没有源码,没有原理图,甚至连芯片型号都只能靠猜。这时候你想知道它到底怎么实现的、通信协议是什么、有没有后门、能不能改一个功能,唯一的路径就是——把固件从芯片里弄出来,然后拆开看。

这件事在行业里通常被叫做“固件提取”或“固件分析”,但很多人习惯用“破解”这个词来概括。严格来说,这两者有交集但不完全等同。固件提取侧重的是把二进制数据从存储介质中读出来;固件分析侧重的是理解这些数据在做什么;而破解往往特指绕过某种保护机制,比如读保护、加密、签名校验。三者经常串在一起做,所以圈子里就笼统地叫“破解固件”了。

我这些年折腾过不少芯片,从最早的STC89C52到后来的STM32、GD32、HC32F460,再到一些带外部SPI Flash的方案,踩过的坑比成功的次数多得多。这篇文章就把我实际用过、验证过的方法系统梳理一遍,不搞理论空谈,每个方法都讲清楚适用场景、操作步骤和实际成功率。

注意:本文所有内容仅用于合法的产品调试、安全评估、教学研究和自有设备维护。未经授权对他人设备进行固件提取和分析,可能违反法律法规,请自行确认授权边界。

1.2 固件“破解”到底在破什么

很多人一上来就问“怎么破解单片机固件”,但这个问题太宽了。你得先搞清楚你要破的是什么层面的东西。根据我的经验,固件层面的保护大致分四个层级:

保护层级典型手段破解难度常见芯片
物理层封装打磨、环氧树脂覆盖高安全芯片、金融IC卡
电气层读保护熔丝、调试口禁用中STM32、GD32、HC32
数据层固件加密、校验签名中高带Bootloader的产品
协议层通信加密、认证握手中物联网设备、无线模块

大部分中小型产品的固件保护停留在电气层,也就是靠芯片自带的读保护(Read Out Protection,简称RDP)来挡住你。这种保护在实验室条件下往往有办法绕过,但到了数据层加密,事情就复杂得多,很多时候需要结合侧信道分析或者找到密钥泄露点。

我个人的经验是:先判断保护层级,再选方法。上来就拆芯片打磨,大概率是把事情做复杂了。很多时候一个调试口就能解决的问题,没必要动烙铁。

1.3 常见单片机固件存储介质与访问方式

在动手之前,你得知道固件存在哪里。不同芯片、不同方案的存储方式差别很大,直接决定了你用什么工具、走什么路径。

片内Flash型:这是最常见的,51单片机、STM32、GD32、HC32F460这些,程序直接存在芯片内部的Flash里。访问方式通常是通过调试接口(SWD、JTAG、ISP)或者芯片自带的Bootloader。

片外SPI Flash型:很多物联网设备、路由器、摄像头方案会把固件放在外部的SPI NOR Flash或NAND Flash里,比如W25Q系列、MX25系列。这种最好办,直接夹子夹上去读就行,连芯片都不用拆。

OTP型:一次性可编程,多见于早期OTP单片机,比如一些遥控器、玩具方案。这种基本读不出来,只能通过逆向逻辑或者暴力枚举。

Mask ROM型:掩膜ROM,出厂就固化了,常见于大批量低成本方案。理论上可以通过显微拍照加腐蚀的方式提取,但成本极高,个人玩家基本不用考虑。

我下面讲的方法,主要覆盖片内Flash和片外SPI Flash这两种,因为它们占了实际项目的九成以上。

2. 基于调试接口的固件提取实操

2.1 SWD与JTAG接口的识别与接线

SWD和JTAG是ARM Cortex-M系列芯片最常用的调试接口。很多产品为了调试方便,会在PCB上留出测试点,甚至直接留一个标准排针。你要做的第一件事就是找到它们。

SWD接口通常需要四根线:SWCLK、SWDIO、GND、VCC(有些只需要三根,VCC可以不接,但建议接上以便芯片供电稳定)。JTAG需要更多线,但SWD在Cortex-M上更常见。

找接口的方法有几个:

  • 看PCB丝印,有时候会标“SWD”“DEBUG”“J1”之类的字样
  • 用万用表测通断,找和芯片对应引脚相连的测试点
  • 查芯片数据手册,确认SWCLK和SWDIO对应的引脚编号
  • 如果板子上有未焊接的排针孔,大概率就是调试口

我遇到过一块板子,调试口被故意用焊锡短接了,需要先用吸锡带清理干净才能接。还有的板子把调试口引脚复用成了普通GPIO,这种情况下需要先让芯片进入Bootloader模式,或者通过特定时序唤醒调试口。

实操心得:接线之前一定先确认目标板供电电压。3.3V的芯片接5V调试器,轻则通信失败,重则烧毁调试口。我习惯用带电平转换的调试器,比如J-Link OB或者ST-Link V3,省心很多。

2.2 使用OpenOCD读取Flash内容

OpenOCD是我最常用的开源调试工具,支持几乎所有主流调试器和芯片。下面以STM32F103为例,讲一下完整流程。

首先安装OpenOCD,Linux下直接apt install openocd,Windows下下载预编译包解压即可。然后准备一个配置文件,指定调试器和目标芯片:

openocd -f interface/stlink.cfg -f target/stm32f1x.cfg

如果连接正常,你会看到类似这样的输出:

Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32f1x.cpu on 3333

接下来用telnet连接OpenOCD的4444端口,或者直接用gdb连接3333端口。我习惯用telnet,因为操作更直接:

telnet localhost 4444

连接成功后,先halt住CPU:

> halt

然后读取Flash内容。STM32F103的Flash起始地址是0x08000000,大小根据型号不同,一般是64KB或128KB:

> flash read_bank 0 firmware.bin 0 0x20000

这条命令的意思是:从bank 0的偏移0开始,读取0x20000字节(128KB),保存到firmware.bin。读完之后用resume让芯片继续运行,然后退出。

如果芯片有读保护,OpenOCD会报错,类似“target not halted”或者“flash protected”。这时候就需要先解除保护,方法在后面讲。

2.3 ST-Link Utility与J-Flash的图形化操作

如果你不想敲命令,ST-Link Utility(现在叫STM32CubeProgrammer)和J-Flash都是很好的图形化选择。ST-Link Utility的用法很简单:连接目标板,选择芯片型号,点击“Connect”,然后选择“Target”菜单里的“Read Memory”或者直接“Save As”保存整个Flash。

J-Flash的功能更强一些,支持更多芯片和更灵活的读取范围设置。我一般用J-Flash来读非ST的芯片,比如GD32、HC32这些,因为ST-Link Utility对第三方芯片支持有限。

图形化工具的好处是直观,坏处是遇到保护机制时错误信息不够详细,排查起来不如OpenOCD方便。我的建议是:新手先用图形化工具熟悉流程,遇到问题再切到OpenOCD看详细日志。

2.4 读保护解除的几种思路

读保护是芯片厂商提供的一种保护机制,开启后调试接口无法读取Flash内容。不同芯片的解除方法不一样,但核心思路无非几种:

全片擦除解除法:很多芯片的读保护在触发全片擦除后会自动解除。比如STM32F1系列,通过调试器发送“Mass Erase”命令,芯片会擦除整个Flash并清除读保护位。代价是原有固件没了,所以这个方法只适用于你不需要保留原固件、只是想重新烧录自己程序的情况。

调试口时序绕过法:某些芯片在读保护开启后,调试口会在复位后的特定时间窗口内保持可用。如果你能在窗口期内halt住CPU,就能读取Flash。这个方法需要精确控制复位时序,通常用脚本自动化实现。

电压毛刺法:通过给芯片供电引脚施加特定波形的电压毛刺,干扰保护逻辑的执行,使其跳过读保护检查。这个方法对硬件要求较高,需要示波器和可编程电源,成功率也因芯片而异。

Bootloader漏洞法:有些芯片的出厂Bootloader存在漏洞,可以通过特定命令序列绕过读保护。这个方法需要仔细研究芯片的Bootloader协议,有时候厂商会在后续版本中修复。

注意:读保护解除涉及对芯片的非正常操作,有一定风险导致芯片永久锁死。操作前建议先用同型号废片练手,确认流程无误后再上目标板。

3. 片外SPI Flash固件读取实战

3.1 识别SPI Flash型号与引脚定义

片外SPI Flash的读取比片内Flash简单得多,因为不需要和芯片的调试逻辑打交道,直接对Flash芯片操作就行。第一步是找到Flash芯片并确认型号。

常见的SPI NOR Flash封装有SOP-8、SOP-16、WSON-8等。SOP-8最常见,引脚定义基本标准化:

引脚名称功能
1CS片选
2DO数据输出
3WP写保护
4GND地
5DI数据输入
6CLK时钟
7HOLD保持
8VCC电源

芯片表面通常会印有型号,比如“W25Q128JVSIQ”“MX25L6433F”等。如果字迹模糊,可以用放大镜或者手机微距镜头拍照辨认。实在看不清,可以根据封装和引脚定义猜一个兼容型号,大部分SPI Flash的读取指令是兼容的。

3.2 使用CH341A编程器读取固件

CH341A是性价比最高的SPI Flash编程器,几十块钱就能买到,配合免费软件可以读写绝大多数SPI NOR Flash。操作流程如下:

首先把Flash芯片从板子上拆下来,或者用SOIC-8夹子直接在板子上夹。在板子上夹的好处是不用拆焊,但要注意板子上其他电路可能影响读取,比如并联的电阻电容。如果读取不稳定,还是拆下来读最可靠。

然后把芯片装到CH341A的转接座上,注意方向:芯片的1脚要对准转接座上的标记。打开软件(比如AsProgrammer或NeoProgrammer),选择芯片型号,点击“读取”,等待完成即可。

如果软件里没有你的芯片型号,可以选一个容量相同、指令集兼容的型号代替。比如W25Q128和MX25L128的读取指令基本一致,可以互换。

实操心得:CH341A的3.3V输出有时候不太准,实测可能到3.5V甚至更高。虽然大部分Flash能承受,但长期来看有风险。我一般会外接一个稳定的3.3V电源,把CH341A的VCC跳线断开,只用地和信号线。

3.3 在线读取与离线读取的取舍

在线读取(不拆芯片)的优点是快、风险低,缺点是可能受板子上其他电路干扰。离线读取(拆芯片)的优点是干净、可靠,缺点是需要热风枪或烙铁,操作不当可能损坏焊盘。

我的判断标准是:如果板子上Flash的CS、CLK、DI、DO四根线没有和其他芯片强驱动冲突,优先在线读。具体判断方法是看原理图,或者用万用表测这四根线对地的阻抗。如果阻抗很低(比如小于100欧姆),说明有强驱动,在线读可能失败。

另外,有些板子的Flash供电是受主控控制的,主控不运行时Flash没电,这种情况下需要给Flash单独供电,或者让主控进入某个特定状态。

3.4 读取数据的校验与拼接

读出来的数据一定要校验,否则后面分析全是白费功夫。校验方法有几个:

  • 看数据里有没有明显的文件系统特征,比如“FAT”“LittleFS”“SPIFFS”等字样
  • 用binwalk扫描,看能不能识别出已知的文件头
  • 计算MD5,和已知固件对比(如果有的话)
  • 读两次,对比两次结果是否一致

如果固件分布在多个Flash芯片上,或者一个Flash里分了多个分区,还需要做数据拼接。拼接的时候要注意地址映射关系,有时候芯片厂商会做地址重映射,不能简单按物理地址拼接。

4. 固件分析的核心工具链

4.1 binwalk与固件解包

binwalk是固件分析的入门工具,能自动识别固件中的文件系统、压缩包、可执行文件等。基本用法:

binwalk firmware.bin

它会输出一个扫描结果,列出所有识别到的签名。如果看到“Squashfs filesystem”“JFFS2 filesystem”“gzip compressed data”之类的条目,就可以进一步提取:

binwalk -e firmware.bin

提取出来的内容会放在_firmware.bin.extracted目录下。如果是Squashfs,可能需要unsquashfs进一步解包;如果是JFFS2,需要jefferson工具。

binwalk不是万能的,遇到自定义格式或者加密固件就无能为力了。这时候需要手动分析,从二进制层面找线索。

4.2 Ghidra与IDA的逆向分析

拿到可执行代码后,就需要反汇编和反编译。Ghidra是NSA开源的逆向工具,免费且功能强大;IDA Pro是商业工具,但生态更成熟。我一般先用Ghidra做初步分析,遇到复杂逻辑再切IDA。

对于ARM Cortex-M固件,加载时要注意设置正确的基地址。比如STM32的Flash起始地址是0x08000000,加载到Ghidra时要把这个地址作为Image Base。否则所有地址引用都会错位。

反编译出来的代码可读性取决于编译优化等级。O0编译的代码最接近源码,O2/O3优化后会丢失很多结构信息。如果固件是O2编译的,反编译结果可能比较难看,需要结合汇编一起分析。

4.3 串口日志与动态调试

静态分析之外,动态调试往往能更快定位关键逻辑。串口日志是最简单的动态信息源,很多产品会在启动时输出调试信息,包括版本号、内存布局、外设初始化状态等。

如果串口没有输出,可以尝试以下方法:

  • 用逻辑分析仪抓SPI/I2C/UART波形,看有没有通信数据
  • 通过调试器attach到运行中的芯片,设置断点观察寄存器状态
  • 修改固件中的某个判断逻辑,重新烧录后观察行为变化

动态调试的前提是你能重新烧录固件。如果芯片有读保护且无法解除,动态调试就很难进行。

5. 常见问题与排查技巧实录

5.1 调试器连接失败排查表

现象可能原因排查方法
无法识别调试器USB驱动问题检查设备管理器,重装驱动
能识别调试器但连不上芯片接线错误用万用表测SWCLK/SWDIO通断
连上后立即断开芯片处于低功耗模式尝试复位后立即连接
报“target not halted”读保护开启检查选项字节,尝试解除保护
读取数据全为0xFFFlash为空或地址错误确认Flash起始地址和大小
读取数据全为0x00供电不足或时钟问题检查VCC和GND,降低SWD时钟

5.2 固件加密的识别与应对

如果你读出来的固件看起来像随机数据,但芯片确实有程序在运行,那大概率是加密了。识别方法:用熵值分析工具(比如binwalk -E)看数据熵值,如果接近8.0,说明是高熵数据,很可能是加密或压缩过的。

应对加密固件,有几个方向:

  • 找解密逻辑:如果芯片内部有解密程序,那密钥一定在芯片里。可以通过动态调试找到解密函数的调用点,dump出密钥。
  • 找密钥泄露点:有些产品的密钥硬编码在固件里,或者通过不安全的通信通道传输。抓包分析可能找到线索。
  • 侧信道分析:通过功耗或电磁辐射分析,推断密钥信息。这个方法门槛较高,需要专业设备。

5.3 芯片锁死后的补救措施

芯片锁死是常见事故,通常是因为误操作了选项字节或者触发了不可逆的保护机制。补救措施取决于芯片型号:

  • STM32系列:大部分情况下可以通过Mass Erase恢复,但固件会丢失
  • GD32系列:类似STM32,但有些型号的读保护一旦开启就无法解除
  • HC32系列:需要参考具体型号的手册,部分型号支持通过特定引脚组合恢复

如果芯片彻底锁死,最后的办法是换芯片。所以操作前一定要备份,能读就先读,读不了再想别的办法。

5.4 法律与道德边界

最后说一个容易被忽略但很重要的问题:授权。你自己买的设备,自己折腾,没问题。但如果是别人的设备,或者公司资产,一定要先确认你有没有权限做这件事。

我见过有人因为帮朋友“修”路由器,顺手读了固件,结果发现里面有一些不该看的东西,最后搞得很难堪。技术本身没有对错,但使用技术的场景有边界。动手之前,先问自己一句:这件事我有没有被授权?

6. 从固件到源码的还原思路

6.1 识别编译器与优化等级

反编译的第一步是判断固件是用什么编译器、什么优化等级编译的。这直接影响你后续的分析策略。

判断编译器的方法:看启动代码的特征。ARMCC(Keil MDK)和GCC的启动代码风格不同,函数序言和尾声也有差异。比如ARMCC常用PUSH {R4,LR},GCC常用PUSH {R4,LR}但寄存器分配策略不同。

优化等级的判断:O0编译的代码会有大量冗余的栈操作,O2/O3会做指令重排和函数内联。如果反编译结果里函数边界模糊,说明优化等级较高。

6.2 恢复符号与函数命名

没有符号表的固件分析起来很痛苦,所有函数都是FUN_08000123这种名字。恢复符号的方法:

  • 找字符串引用:字符串常量往往能提示函数功能,比如“Init UART”“CRC Error”
  • 找库函数特征:标准库函数(memcpy、strlen等)有固定的指令模式,可以用工具自动识别
  • 找中断向量表:中断处理函数的地址在向量表里,可以根据中断类型命名

Ghidra和IDA都有自动识别库函数的功能,能省不少事。

6.3 关键逻辑的定位与验证

定位关键逻辑的方法:从你关心的功能入手,比如“设备怎么验证密码”“通信协议怎么加密”。通过字符串、常量、系统调用等线索,逐步缩小范围。

验证的方法:修改固件中的某个判断,重新烧录,看行为是否变化。比如把密码比较函数的返回值强制改成1,看能不能绕过验证。这个方法需要你能重新烧录固件,且芯片没有签名校验。

7. 固件安全防护的逆向思考

7.1 从攻击者视角看固件保护

折腾了这么多破解方法,反过来想,如果你要保护自己的固件,应该怎么做?我的建议是分层防护:

  • 第一层:开启芯片读保护,挡住大部分业余玩家
  • 第二层:固件加密,即使读出来也看不懂
  • 第三层:签名校验,防止固件被篡改后重新烧录
  • 第四层:关键密钥存储在安全区域,不放在主Flash里

7.2 常见防护措施的绕过成本评估

防护措施绕过成本适用场景
读保护低消费类产品
固件加密中工业控制
签名校验中高物联网设备
安全芯片高金融、车规
物理防护极高军事、航天

7.3 给固件开发者的实用建议

如果你正在开发产品,以下几点值得注意:

  • 调试口在产品出厂后一定要禁用或物理断开
  • 不要在固件里硬编码密钥,用安全芯片或者动态派生
  • 开启签名校验,防止固件被替换
  • 定期更新固件,修复已知漏洞
  • 保留调试日志,但不要在生产固件里输出敏感信息

我在实际项目中遇到过因为调试口没关导致固件被读的情况,也见过因为密钥硬编码导致整个产品线被逆向的案例。这些教训说明,固件安全不是可选项,而是产品设计的一部分。

最后分享一个小技巧:如果你只是想做固件分析学习,建议从二手市场买一些便宜的开发板或者旧路由器练手。这些设备通常没有太强的保护,适合入门。等熟练了再挑战更复杂的芯片。记住,技术是用来创造价值的,不是用来搞破坏的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询