4、固件解包与识别:使用binwalk分析固件结构、识别文件系统、手动提取与修复

拿到一个固件,第一件事是什么?

不是急着去逆向,而是先搞清楚它里面到底装了啥。说白了,就是给固件做个“CT扫描”。

我个人的习惯是,先跑一遍 binwalk,看看固件里藏了多少东西。这工具就像一把瑞士军刀,能帮你快速定位文件系统、内核镜像、甚至是一些加密的签名块。

4.1 binwalk 基础用法

先来个最简单的命令:

binwalk firmware.bin

它会扫描固件,然后列出所有它认识的“签名”。比如你可能会看到:

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             TRX firmware header, little endian, image size: 4194304 bytes, CRC32: 0xABCDEF01, flags: 0x0, version: 1, header size: 28 bytes
28            0x1C            LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 2097152 bytes
1052672       0x101000        SquashFS filesystem, little endian, version 4.0, compression:lzma, size: 3145728 bytes,  inodes: 1024, blocks: 512

嗯,这里要注意。binwalk 不是万能的。它靠的是“特征签名”来识别。如果固件被加密了,或者用了非标准的魔数,它可能就认不出来。

小技巧: 如果 binwalk 没扫出东西,试试加个 -T 参数,提高扫描的灵敏度。或者用 -E 看熵值图,加密的固件熵值通常很高,接近1.0。

4.2 识别文件系统:SquashFS、JFFS2、CramFS

固件里最常见的文件系统,我接触最多的是这三种:

文件系统 特点 常见场景
SquashFS 只读、压缩率高、支持大端小端 OpenWrt、大部分路由器固件
JFFS2 可读写、支持闪存磨损均衡 老款嵌入式设备、工业控制器
CramFS 老牌只读文件系统、压缩率一般 早期Linux内核、老旧设备

binwalk 识别出文件系统后,通常会告诉你偏移量和大小。比如上面那个例子,SquashFS 从 0x101000 开始,大小是 3145728 字节。

我曾经在一个工控设备的固件里,binwalk 识别出了三个 SquashFS 分区。一开始我以为是重复的,后来才发现,一个是主系统,一个是恢复分区,还有一个是配置分区。如果不仔细看偏移量,很容易搞混。

4.3 手动提取与修复

binwalk 自带的 -e 参数可以自动提取,但有时候会失败。为什么?

因为固件可能被“魔改”过。比如厂商把文件系统的魔数改了,或者中间插了一些自定义的头部。这时候,就得手动来。

手动提取的步骤,我一般这么干:

  1. 确认偏移量:用 binwalk 或者 hexdump 找到文件系统的起始地址。
  2. 切割文件:用 dd 命令把文件系统部分单独切出来。
dd if=firmware.bin of=squashfs_part.bin bs=1 skip=1052672 count=3145728

这里 skip 是跳过的字节数,count 是要提取的长度。这两个值都来自 binwalk 的输出。

  1. 尝试挂载或解压
# 尝试挂载 SquashFS
mount -t squashfs -o loop,ro squashfs_part.bin /mnt

# 或者用 unsquashfs 解压
unsquashfs -d ./extracted squashfs_part.bin

如果挂载失败,提示“无法识别文件系统”,那大概率是魔数被改了。

避坑指南: 我曾经遇到一个固件,binwalk 识别出 SquashFS,但 unsquashfs 死活解不开。后来用 hexdump 一看,文件系统的魔数 hsqs 被改成了 hsqS。手动改回来,就能正常解压了。

修复魔数的方法很简单:

# 用 printf 写入正确的魔数
printf '\x68\x73\x71\x73' | dd of=squashfs_part.bin bs=1 seek=0 conv=notrunc

对于 JFFS2 文件系统,情况更复杂一些。它通常需要知道擦除块大小。如果 binwalk 没识别出来,可以试试 jefferson 工具:

jefferson -d ./jffs2_root jffs2_part.bin

如果还是不行,可能是文件系统有损坏,或者固件里包含了多个 JFFS2 分区,需要手动拼接。

4.4 核心逻辑:固件解包流程

下面这张图,是我自己总结的固件解包流程。每次拿到新固件,我都会按这个思路走一遍。

固件解包核心流程 1. 获取固件 2. binwalk 扫描 识别成功? 自动提取 (-e) 手动分析/修复 3. 挂载/解压文件系统 4. 分析文件系统内容

这个流程看起来简单,但实际工作中,每一步都可能遇到坑。比如第二步,binwalk 识别成功了,但自动提取出来的文件系统是坏的。这时候,就得回到手动分析的路子上来。

核心要点:
  • binwalk 是起点,不是终点。它帮你定位,但不保证提取成功。
  • 手动提取时,dd 命令的偏移量和大小必须精确。差一个字节,文件系统就可能挂不上。
  • 魔数修复是常见操作。SquashFS 的魔数是 hsqs,JFFS2 的魔数是 1985(小端序)。
  • 遇到加密固件,先找密钥。密钥可能在 bootloader 里,也可能在固件的某个固定偏移处。

我记得有一次,一个客户拿来的工控固件,binwalk 扫出来全是乱码。我一看熵值,0.99,明显是加密了。后来在 bootloader 里找到了 AES 密钥,写了个脚本解密,才看到里面的 SquashFS。嗯,那一次折腾了整整两天。

所以,别指望一个工具解决所有问题。binwalk 是你的好帮手,但真正的功夫,在于你手动分析和修复的能力。

个人习惯: 我每次解包后,都会先看看 /etc/init.d//sbin/init 这两个文件。它们能告诉你固件的启动流程和关键服务。有时候,漏洞就藏在这些启动脚本里。

好了,这一章的内容就到这里。记住,固件解包是逆向的第一步,也是最关键的一步。基础打牢了,后面的分析才能事半功倍。


公众号:蓝海资料掘金营,微信deep3321