1. 固件逆向分析基础:固件提取、固件解包、文件系统识别、固件基地址定位

各位同学,欢迎来到《固件字符串交叉引用与状态机恢复实战》的第一章。

做固件逆向,说白了就是跟二进制数据打交道。你拿到一个.bin文件,里面是密密麻麻的十六进制,怎么下手?我刚开始接触这行的时候,也对着屏幕发过呆。后来踩的坑多了,慢慢总结出一套固定的流程。

这一章,我们就来聊聊固件逆向的四个基本功:提取、解包、识别文件系统、定位基地址。这四个步骤,就像盖楼打地基,缺一不可。

核心观点:固件逆向的成败,80%取决于前期准备。基地址定错了,后面所有反汇编都是废的。

1.1 固件提取——从硬件到二进制

固件提取,就是把芯片里的程序读出来。方法有很多,我按常用程度排个序:

  1. 编程器直读:最直接。把Flash芯片吹下来,放到编程器上读。我遇到过最坑的一次,是某款路由器用了加密芯片,读出来全是0xFF——人家做了读保护。
  2. JTAG/SWD调试接口:如果板子上有调试接口,直接用OpenOCD或JLink读。注意,有些厂商会禁用调试口,或者加了密码锁。
  3. Bootloader串口导出:很多设备在uboot阶段提供串口命令行,输入md或dump命令就能导出内存。这个方法我经常用,前提是你得知道怎么进uboot。
  4. OTA固件抓包:从手机App或云端下载固件更新包。通常是加密或压缩的,需要先解密。

我的习惯:拿到一个新设备,先搜一下有没有公开的固件包。很多厂商的官网就有下载,省去拆机焊接的麻烦。

1.2 固件解包——拆开二进制的外壳

提取出来的固件,通常不是裸的二进制。它可能被压缩、加密,或者打包成特定格式。我们需要把它拆开,才能看到真正的文件系统。

常用的工具有这些:

工具 适用场景 个人评价
binwalk 自动扫描固件中的文件签名 首选工具,但有时会误报
dd 手动切割指定偏移的数据 配合binwalk结果使用
firmware-mod-kit 解包/打包常见路由器固件 老牌工具,支持很多格式
jefferson 解包JFFS2文件系统 专门对付JFFS2,很好用
squashfs-tools 解包SquashFS文件系统 Linux嵌入式最常见

具体怎么操作?我一般这么来:

# 第一步:用binwalk扫描固件
binwalk firmware.bin

# 第二步:如果看到SquashFS签名,直接提取
binwalk -Me firmware.bin

# 第三步:如果binwalk没识别出来,手动检查头部
hexdump -C firmware.bin | head -20

为什么会识别不出来?我遇到过一种情况:厂商把文件系统的魔数改了,binwalk的默认签名库里没有。这时候就得靠经验,看文件头部的特征——比如SquashFS的hsqs被改成了abcd,手动改回来就能解了。

注意:binwalk的-Me参数会递归解包,如果固件里嵌套了多个文件系统,它会全部解开。但有时候递归太深,会解出大量小文件,注意磁盘空间。

1.3 文件系统识别——找到真正的代码

解包之后,你会看到一堆目录和文件。但哪些才是真正的固件代码?

常见的嵌入式文件系统有这几种:

  • SquashFS:只读压缩文件系统,Linux嵌入式设备最爱。特点是启动快、占用小。
  • JFFS2 / UBIFS:可读写文件系统,用于存放配置和日志。我见过有些设备把内核放在SquashFS里,配置放在UBIFS里。
  • CramFS:老式压缩文件系统,现在很少见了。但一些老设备还在用。
  • initramfs:内存文件系统,通常用于早期启动阶段。

识别方法很简单:

  1. 看binwalk的输出,它会告诉你偏移和类型。
  2. 用file命令检查提取出来的文件。
  3. 手动查看文件头部魔数——比如SquashFS的魔数是hsqs,JFFS2的魔数是0x1985。

避坑指南:我曾经遇到一个固件,binwalk识别出两个SquashFS分区。第一个是空的,第二个才是真正的文件系统。原因是厂商在固件里塞了一个占位分区。所以,别只看第一个结果,要检查每个分区的实际内容。

1.4 固件基地址定位——反汇编的第一步

文件系统解出来了,代码也找到了。但还有一个关键问题:这个二进制文件,加载到内存的哪个地址?

基地址定错了,反汇编出来的代码全是错的。指令会被拆错,函数边界会乱掉。我刚开始学的时候,就犯过这个错——对着IDA看了半天,发现跳转地址全都不对,后来才意识到基地址设错了。

定位基地址的方法:

方法 原理 适用场景
查看中断向量表 ARM/MIPS的复位向量通常在0x0或0x80000000 裸机固件、RTOS
分析重定位表 ELF文件有明确的加载地址 Linux内核、uboot
搜索字符串引用 代码中引用的字符串地址可以反推基地址 所有固件
对比已知固件 同系列设备的基地址通常相同 有参考样本时

我个人最常用的方法是搜索字符串引用。具体来说:

  1. 在二进制里找一个明显的字符串,比如"Hello World"。
  2. 在代码段里搜索这个字符串的地址引用。
  3. 如果代码里引用的地址是0x80123456,而字符串在文件中的偏移是0x123456,那么基地址就是0x80000000。

举个例子:

# 假设字符串 "login:" 在文件中的偏移是 0x5678
# 代码中引用它的指令是: ldr r0, =0x80005678
# 那么基地址 = 0x80005678 - 0x5678 = 0x80000000

关键点:基地址定位一定要交叉验证。用中断向量表算一个,用字符串引用再算一个,两个结果一致才能放心。

1.5 本章知识体系总览

下面这张图,把这一章的核心流程串起来了。你可以把它当作固件逆向的「操作手册」。

固件逆向分析基础流程 固件提取 编程器 / JTAG / OTA 固件解包 binwalk / dd / fmk 文件系统识别 SquashFS / JFFS2 基地址定位 向量表 / 字符串引用 直接分析(无文件系统) 反汇编与交叉引用 IDA Pro / Ghidra 关键原则: 1. 每一步都要交叉验证,不要轻信单一工具的结果。 2. 基地址定位错误会导致后续所有分析失效,务必反复确认。

这张图展示了从原始固件到可分析代码的完整链路。每一步都有对应的工具和方法,而且每一步之间是串行依赖的——前一步没做好,后面就全白费。

1.6 本章小结

这一章我们讲了四个核心步骤:

  • 固件提取:从硬件或网络拿到原始二进制。
  • 固件解包:用binwalk等工具拆开打包格式。
  • 文件系统识别:找到真正的代码和配置文件。
  • 基地址定位:确定代码在内存中的加载地址。

这四个步骤,我做了这么多年,每次都会走一遍。哪怕是一个看起来很简单的固件,我也会用两种方法交叉验证基地址。为什么?因为吃过亏。有一次我偷懒,只用了向量表定位,结果那个固件是XIP(原地执行)的,基地址根本不是向量表里的值——浪费了整整两天。

嗯,这一章的内容就到这里。记住:基础不牢,地动山摇。后面的章节,我们会基于这一章的知识,深入字符串交叉引用和状态机恢复。到时候你会发现,前期准备做得越扎实,后面的分析就越顺畅。