4. 固件二进制分析基础
各位好,欢迎来到固件逆向分析的第一道关卡。说实话,很多新手拿到一个陌生的固件文件,第一反应就是直接扔进反汇编器——这其实是个大坑。我见过太多人因为没搞清楚文件格式,浪费了好几天时间在错误的数据上。
这一章,我们就从最基础的二进制文件格式讲起。你想想看,连文件是什么格式都不清楚,怎么谈得上分析呢?
4.1 三种常见的固件文件格式
嵌入式世界里,最常见的固件格式就三种:BIN、HEX 和 ELF。它们各有各的脾气,咱们一个一个说。
| 格式 | 全称 | 特点 | 典型用途 |
|---|---|---|---|
| BIN | Binary | 纯二进制数据,无元信息 | 烧录到 Flash 的最终镜像 |
| HEX | Intel HEX | ASCII 文本表示,含地址和校验 | 编程器、烧录工具中间格式 |
| ELF | Executable and Linkable Format | 含符号表、段信息、调试信息 | 开发调试阶段的可执行文件 |
4.1.1 BIN 文件——最纯粹的二进制
BIN 文件说白了就是内存或 Flash 的直接映像。没有文件头,没有元数据,从第一个字节开始就是代码或数据。我早期做路由器固件分析时,经常遇到的就是这种格式。
个人经验: 拿到 BIN 文件后,我习惯先用 hexdump -C 看一眼开头几个字节。如果是 ARM 芯片,开头往往是中断向量表;如果是 RISC-V,可能是跳转指令。这个习惯帮我快速判断过很多次架构类型。
4.1.2 HEX 文件——带地址的文本格式
Intel HEX 格式很有意思,它把二进制数据编码成了 ASCII 文本。每一行以冒号开头,后面跟着长度、地址、记录类型、数据和校验和。
举个例子:
:10000000 00000000 00000000 00000000 00000000 F0
:10001000 00000000 00000000 00000000 00000000 E0
:00000001 FF
看懂了吗?第一行表示从地址 0x0000 开始,有 16 个字节的数据。最后一行是文件结束标记。
注意: HEX 文件里的地址不一定是连续的!有些固件会把不同段的代码放在不同地址区域。我曾经遇到过一个案例,HEX 文件里跳了好几个地址段,结果有人直接按顺序拼接成 BIN,烧进去就变砖了。
4.1.3 ELF 文件——开发者的最爱
ELF 格式是 Linux 和大多数嵌入式 RTOS 的标准可执行格式。它包含多个段(Section),比如 .text(代码段)、.data(数据段)、.bss(未初始化数据段),还有符号表。
我个人觉得,ELF 是最好分析的格式,因为符号表能直接告诉你函数名和变量名。但问题是,很多量产固件会剥离符号表,甚至用 strip 命令把调试信息全删掉。
4.2 文件头识别——看一眼就知道是什么
每个文件格式都有独特的魔数(Magic Number)。你只要看一眼开头几个字节,就能判断出文件类型。
| 格式 | 魔数(十六进制) | ASCII 表示 |
|---|---|---|
| ELF (32-bit) | 7F 45 4C 46 |
.ELF |
| ELF (64-bit) | 7F 45 4C 46 |
.ELF |
| Intel HEX | 3A |
: |
| BIN | 无固定魔数 | 取决于具体内容 |
关键点: BIN 文件没有魔数,所以识别起来最麻烦。我通常的做法是:先看文件大小是否对齐到 Flash 的页大小(比如 256 字节或 4KB),再看开头是不是中断向量表或启动代码。
4.3 使用 binwalk 进行固件解包
binwalk 是我工具箱里最常用的工具之一。它能自动识别固件里嵌入的文件系统、压缩数据、甚至隐藏的固件。
基本用法很简单:
# 扫描固件,识别嵌入的文件
binwalk firmware.bin
# 递归提取所有识别到的文件
binwalk -Me firmware.bin
# 只提取特定类型的文件(比如只提取 SquashFS)
binwalk -D 'squashfs:rootfs:squashfs' firmware.bin
我记得有一次分析一个 IoT 摄像头固件,binwalk 直接帮我找到了藏在固件中间的 JFFS2 文件系统。如果没有它,我可能得手动搜索文件系统魔数,那工作量可就大了。
避坑指南: 我曾经遇到过 binwalk 误报的情况——把随机数据识别成了某种文件系统。所以我的习惯是:永远不要完全相信 binwalk 的输出。提取出来的文件一定要手动验证一下,比如用 file 命令确认类型。
4.4 使用 strings 提取关键信息
strings 命令看起来简单,但用好了能挖出不少线索。它能从二进制文件中提取出可打印的字符串。
# 提取所有长度 >= 4 的字符串
strings firmware.bin
# 提取并显示偏移地址
strings -t x firmware.bin
# 限制最小字符串长度(减少噪音)
strings -n 8 firmware.bin
你可能会问:这有什么用?我告诉你,很多固件里藏着:
- 文件系统路径(比如
/etc/config、/var/run) - 调试日志信息(比如
init complete、error: timeout) - 硬编码的 IP 地址或域名
- 甚至明文密码或密钥
我在分析一个路由器固件时,就是用 strings 找到了一个隐藏的后门账号。那个字符串看起来像乱码,但仔细一看是 Base64 编码的密码。
4.5 熵分析——判断加密与压缩区域
熵(Entropy)这个概念,说白了就是衡量数据混乱程度的指标。纯文本的熵值较低,而加密或压缩后的数据熵值接近最大值(8.0)。
为什么要做熵分析?因为固件里经常有:
- 未加密的代码/数据:熵值较低(约 4.0-6.5)
- 压缩的数据:熵值较高(约 7.0-7.9)
- 加密的数据:熵值接近 8.0
binwalk 自带熵分析功能:
# 生成熵图
binwalk -E firmware.bin
# 保存熵图为 PNG
binwalk --entropy --save firmware.bin
实战技巧: 熵图里如果出现突然的跳变,往往意味着不同区域的边界。比如从低熵突然跳到高熵,很可能就是代码段结束、压缩数据开始的标志。我靠这个技巧找到过好几个隐藏的加密分区。
4.6 本章知识体系总览
下面这张图,是我梳理的固件二进制分析的核心流程。你可以把它当作一个检查清单:
这张图把整个流程串起来了。你拿到固件后,先判断格式,然后根据格式选择不同的分析路径。BIN 和 HEX 需要先用 binwalk 解包,再用 strings 和熵分析辅助判断;ELF 则可以直接利用符号信息。
最后提醒一句: 熵分析不是万能的。有些固件会把加密数据和普通数据混在一起,熵图看起来可能很平滑。我遇到过一种情况,固件用了分段加密,每段只有 1KB,熵图上根本看不出明显跳变。这时候就得结合其他方法综合判断了。