第二章:固件逆向工程基础——二进制分析工具与文件结构解析
各位同学,欢迎来到固件安全审计的第二课。今天咱们聊聊逆向工程最基础的东西——怎么跟一堆二进制数据打交道。
说实话,我刚入行那会儿,面对一个几十兆的.bin文件,真有点无从下手。后来慢慢摸索,发现其实就三板斧:binwalk、strings、hexdump。这三样工具用熟了,大部分固件在你面前就跟透明的一样。
2.1 二进制分析三剑客
2.1.1 binwalk——固件分析的瑞士军刀
binwalk 是我个人最常用的工具,没有之一。它的核心能力就两个:扫描文件签名和提取文件系统。
为什么会这么强?因为大多数固件其实就是把多个文件打包在一起——比如 bootloader、内核、文件系统、配置数据。binwalk 能根据文件头签名,把这些东西一个个识别出来。
常用命令速查:
# 扫描固件中的文件签名
binwalk firmware.bin
# 提取所有识别到的文件
binwalk -e firmware.bin
# 递归提取(遇到压缩包自动解压)
binwalk -Me firmware.bin
# 只显示签名信息,不提取
binwalk -I firmware.bin
我在项目中遇到过一台路由器固件,用 binwalk 扫描后发现有 3 个 squashfs 文件系统。当时我就知道——这设备大概率是双分区备份设计。后来验证确实如此。
小技巧:binwalk 的签名库可以自定义。如果你遇到私有格式的固件,可以自己写签名规则放到 ~/.config/binwalk/magic/ 下。
2.1.2 strings——从二进制里捞字符串
strings 这个工具,说白了就是帮你从一堆乱码里找出人类能读懂的文本。别小看它,很多时候关键信息就藏在这些字符串里。
# 提取所有可打印字符串
strings firmware.bin
# 设置最小长度(过滤太短的字符串)
strings -n 8 firmware.bin
# 显示字符串在文件中的偏移地址
strings -t x firmware.bin
# 结合 grep 快速定位
strings firmware.bin | grep -i "password\|admin\|debug"
嗯,这里要注意:strings 默认只处理 ASCII 编码。如果固件用的是 UTF-16 或别的编码,记得加参数 -e l 或 -e b。
我曾经在分析一个工控设备的固件时,用 strings 捞出了一段硬编码的 SSH 私钥。你想想看,这要是被攻击者拿到,整个设备就成后门了。所以做固件审计时,strings 绝对是第一道防线。
2.1.3 hexdump——二进制世界的显微镜
当 binwalk 和 strings 搞不定的时候,就该 hexdump 上场了。它能让你看到最原始的字节数据。
# 标准十六进制查看
hexdump -C firmware.bin | head -50
# 只显示指定偏移范围
hexdump -C -s 0x1000 -n 512 firmware.bin
# 显示 ASCII 和十六进制对照
xxd firmware.bin | head -30
# od 命令也可以(老派但好用)
od -A x -t x1z -v firmware.bin | head -20
避坑指南:我曾经在分析一个加密固件时,直接用 hexdump 看了半天,发现全是随机数据。后来才意识到——这固件是加密的,必须先解密才能分析。所以看到全乱码时,先想想是不是加密了。
2.2 固件文件结构解析
搞清楚了工具,咱们来看看固件到底长什么样。一个典型的嵌入式固件,结构大致是这样的:
这个结构图是我根据上百个固件总结出来的。当然,实际中会有变种——有的固件没有 Bootloader,有的把配置数据放在最后。但核心骨架就是这四块。
2.2.1 固件头部(Header)
头部是固件的身份证。不同厂商的头部格式千差万别,但通常包含这些信息:
| 字段 | 说明 | 常见值示例 |
|---|---|---|
| 魔数(Magic) | 文件类型标识 | 0x27051956(U-Boot)、0x1F8B(gzip) |
| 版本号 | 固件版本信息 | 1.0.2、R3.1.0 |
| 校验和 | 完整性校验 | CRC32、MD5、SHA256 |
| 固件大小 | 总长度或各分区长度 | 0x400000(4MB) |
| 入口地址 | CPU 开始执行的位置 | 0x80001000 |
实战技巧:用 hexdump 看头部前 64 字节,基本就能判断固件格式。比如看到 HDR0 开头的是 Broadcom 的固件,27051956 是 U-Boot 镜像。
2.2.2 Bootloader 与内核
Bootloader 负责初始化硬件并加载内核。常见的有 U-Boot、RedBoot、CoreBoot。内核则通常是压缩过的,需要先解压才能分析。
怎么区分它们?用 binwalk 扫描时,Bootloader 通常出现在最前面,内核会显示为 Linux kernel 或 ARM zImage。我个人习惯先看 Bootloader 的版本——老版本往往有已知漏洞。
2.2.3 文件系统——真正的宝藏
文件系统是固件分析的重头戏。大部分应用层代码、配置文件、Web 页面都在这。常见的嵌入式文件系统有:
- squashfs:只读压缩文件系统,最常见。用
unsquashfs解压。 - jffs2/ubifs:可读写,用于 Flash 存储。用
jefferson或ubi_reader提取。 - cramfs:老式压缩文件系统。用
cramfsck解压。 - ext4:少见,但高端设备会用。
# 提取 squashfs 文件系统
binwalk -e firmware.bin
cd _firmware.bin.extracted/
unsquashfs *.squashfs
# 或者手动指定偏移(如果 binwalk 没识别出来)
dd if=firmware.bin bs=1 skip=0x100000 of=rootfs.squashfs
unsquashfs rootfs.squashfs
注意:有些厂商会对文件系统进行加密或混淆。我曾经遇到一个设备,文件系统被 XOR 加密了,密钥就藏在 Bootloader 的字符串里。所以提取不出来时,别急着放弃,回头看看 Bootloader 区域。
2.3 实战:分析一个真实固件
咱们来走一遍完整流程。假设你拿到了一个 router_fw.bin:
# 第一步:查看文件基本信息
file router_fw.bin
# 输出:data(说明是裸二进制)
# 第二步:binwalk 扫描
binwalk router_fw.bin
# 输出示例:
# DECIMAL HEXADECIMAL DESCRIPTION
# 0 0x0 TRX firmware header, little endian
# 28 0x1C LZMA compressed data
# 131072 0x20000 Squashfs filesystem, little endian
# 第三步:提取文件系统
binwalk -e router_fw.bin
# 第四步:进入提取目录,查看内容
cd _router_fw.bin.extracted/
ls -la
# 第五步:解压 squashfs
unsquashfs *.squashfs
# 第六步:分析关键文件
cd squashfs-root/
strings bin/httpd | grep -i "password\|admin\|root"
cat etc/shadow
ls -la etc/init.d/
你看,就这么几步,固件的内部结构就暴露无遗了。我在实际项目中,80% 的漏洞都是通过这个流程发现的——硬编码密码、后门账号、不安全的配置,全藏在文件系统里。
核心要点总结:
- binwalk 负责拆包,strings 负责捞信息,hexdump 负责看细节
- 固件结构 = 头部 + Bootloader + 内核 + 文件系统
- 文件系统是分析重点,squashfs 最常见
- 遇到加密/混淆,先找密钥,通常在 Bootloader 或头部
好了,这一章的内容就到这。工具和方法都给了,剩下的就是多练。下次你拿到一个固件,别急着跑动态分析,先用这三板斧把静态结构摸清楚——你会发现,很多问题在静态阶段就已经暴露了。
公众号:蓝海资料掘金营,微信deep3321