2. 固件逆向工程基础:二进制分析工具链
说实话,很多刚入行的朋友一拿到固件文件就懵了——一堆十六进制,完全不知道从哪下手。我当年第一次分析路由器固件时也是这样,对着二进制文件发呆。后来慢慢摸索出一套工具链,才算真正入了门。
这一章,我就带你走一遍固件逆向的起步阶段。说白了,就是三件事:看结构、找线索、认文件系统。工具嘛,就是 binwalk、strings、hexdump 这几把“瑞士军刀”。
核心思路:固件逆向不是一上来就反汇编。先做“表面分析”——识别文件类型、提取文件系统、定位关键字符串。这一步做扎实了,后续的深度逆向才能事半功倍。
2.1 固件文件结构初探
一个典型的嵌入式固件,通常包含这些部分:
- 引导头(Header)——包含魔数、版本、校验和、大小等信息
- 引导加载程序(Bootloader)——如 U-Boot、RedBoot
- 内核(Kernel)——通常是压缩过的 Linux 内核
- 文件系统(Filesystem)——SquashFS、JFFS2、CramFS 等
- 附加数据——配置分区、校准数据、签名等
我遇到过一台工业网关的固件,厂商把配置分区藏在文件系统后面,用 binwalk 默认扫描根本看不到。后来我手动调整了偏移量才挖出来。嗯,这里要注意:不要完全信任自动化工具的输出。
2.2 binwalk:固件分析的“第一把刀”
binwalk 是我每次分析固件时第一个跑的工具。它能做的事情很多:
- 扫描固件中的文件签名(魔数)
- 识别嵌入的文件系统、内核、压缩数据
- 提取文件系统内容
- 分析熵值分布
基本用法很简单:
# 扫描固件中的文件签名
binwalk firmware.bin
# 提取所有识别到的文件
binwalk -e firmware.bin
# 只提取特定偏移的文件系统
binwalk -D 'squashfs:rootfs:squashfs' firmware.bin
# 生成熵值图,判断数据是否加密或压缩
binwalk -E firmware.bin
你想想看,为什么 binwalk 能识别这么多东西?因为它内置了一个签名数据库。每个文件格式都有独特的“魔数”——比如 SquashFS 以 hsqs 开头,JFFS2 以 0x1985 开头。binwalk 就是靠这些特征来定位的。
我的习惯:拿到固件后,先跑 binwalk -E 看熵值图。如果整个固件熵值接近 1.0(高熵),说明大概率是加密的。这时候别急着提取文件系统,先找解密密钥或算法。我曾经在一个 IP 摄像头的固件上浪费了两天,后来才发现厂商用了简单的 XOR 加密——熵值图早就告诉我了,只是我没仔细看。
2.3 strings:快速定位关键信息
strings 这个工具,说简单也简单,说强大也强大。它的作用就是从二进制文件中提取可打印的字符串。
# 提取所有长度 >= 4 的字符串
strings firmware.bin
# 指定最小长度,减少噪音
strings -n 8 firmware.bin
# 同时显示偏移地址
strings -t x firmware.bin
# 结合 grep 快速定位
strings firmware.bin | grep -i "admin\|password\|debug\|secret"
我个人习惯在 binwalk 扫描之后,马上跑 strings 看看有没有敏感信息。你猜怎么着?很多厂商会把默认密码、调试接口、甚至私钥直接硬编码在固件里。
注意:strings 只能提取 ASCII 和 Unicode 字符串。如果固件用了 UTF-16 编码的中文,记得加 -e l 参数。另外,压缩或加密过的数据段里,strings 基本是无效的——全是乱码。
2.4 hexdump:手动“摸”固件
hexdump(或 xxd、od)是最后一道防线。当自动化工具不给力时,就得靠它手动分析了。
# 查看文件头部 512 字节
hexdump -C firmware.bin | head -n 32
# 跳过前 0x1000 字节,查看后续内容
hexdump -C -s 0x1000 firmware.bin | head -n 32
# 只查看指定偏移范围
hexdump -C -s 0x20000 -n 256 firmware.bin
为什么要手动看 hexdump?举个例子:有一次我分析一个智能插座固件,binwalk 什么都没扫出来。我手动看头部,发现前 4 个字节是 AB CD 12 34——这不是任何已知文件系统的魔数。后来我查了厂商的 SDK 文档,才知道这是他们自定义的头部格式,魔数后面 4 字节就是实际固件大小。去掉头部后,binwalk 就能正常识别了。
说白了,自动化工具是帮你省时间的,不是替你思考的。
2.5 固件文件系统识别
嵌入式设备常用的文件系统就那么几种。我整理了一个对照表:
| 文件系统类型 | 魔数(十六进制) | 魔数(ASCII) | 特点 |
|---|---|---|---|
| SquashFS | 68 73 71 73 |
hsqs |
只读压缩,最常见 |
| JFFS2 | 19 85 |
不可打印 | 日志结构,支持磨损均衡 |
| CramFS | 45 3D CD 28 |
不可打印 | 老式压缩文件系统 |
| UBIFS | 31 18 10 06 |
不可打印 | UBI 层之上的文件系统 |
| YAFFS2 | 03 00 00 00 |
不可打印 | NAND Flash 专用 |
| ROMFS | 2D 72 6F 6D |
-rom |
极简只读文件系统 |
识别文件系统后,下一步就是挂载或解压它。以最常见的 SquashFS 为例:
# 用 binwalk 提取
binwalk -e firmware.bin
# 或者手动解压(需要知道偏移)
unsquashfs -d ./rootfs -o 0x123456 rootfs.img
# 挂载只读查看
mount -t squashfs -o loop,ro rootfs.img /mnt/rootfs
避坑指南:我曾经遇到一个固件,binwalk 识别出 SquashFS 但提取失败。后来发现厂商修改了 SquashFS 的超级块结构,在标准头部前加了 4 字节的自定义字段。解决办法是用 dd 跳过这 4 字节,再喂给 unsquashfs。命令如下:
dd if=firmware.bin of=clean_squashfs.bin bs=1 skip=4
unsquashfs -d ./rootfs clean_squashfs.bin
2.6 综合实战:一个典型固件分析流程
把上面的工具串起来,一个标准的固件初步分析流程是这样的:
- 文件信息收集:
file firmware.bin看文件类型,ls -lh看大小 - 熵值分析:
binwalk -E firmware.bin判断是否加密 - 签名扫描:
binwalk firmware.bin识别文件系统和内核 - 字符串提取:
strings -n 8 firmware.bin | grep -i "pass\|admin\|key" - 手动验证:用 hexdump 查看头部和关键偏移
- 文件系统提取:
binwalk -e firmware.bin或手动解压 - 内容审查:查看提取出的文件系统,寻找配置文件、脚本、二进制程序
这个流程我用了快十年,基本没翻过车。当然,遇到加密固件或自定义格式时,需要额外步骤——那些我们后面章节再聊。
这张图就是我平时做固件初步分析的完整路径。你可能会问:为什么步骤②③④是并行的?因为这三个工具互不依赖,可以同时跑,节省时间。我一般开三个终端窗口,binwalk 扫着,strings 跑着,hexdump 看着——效率最高。
总结一下:binwalk 是“侦察兵”,strings 是“情报员”,hexdump 是“侦察兵”。三者配合,基本能搞定 80% 的固件初步分析。剩下的 20%,要么是加密固件,要么是自定义格式——那些就需要更深入的技术了,我们后面的章节会逐一攻克。