第四章 固件文件系统分析

文件系统分析,说白了就是固件逆向的「拆弹环节」。你拿到一个固件包,binwalk扫出一堆偏移地址,但怎么把里面的东西完整倒出来?这就要看你对文件系统的理解有多深了。

我个人习惯把文件系统分成两类:一类是压缩只读型,比如Squashfs、Cramfs;另一类是日志型闪存文件系统,比如JFFS2、UBIFS、YAFFS2。处理方式完全不同,踩坑的点也不一样。

核心认知:固件文件系统分析 = 识别类型 + 正确提取 + 挂载验证 + 异常修复。四步缺一不可。

4.1 常见文件系统一览

先快速过一遍市面上最常见的几种。我按使用频率排了个序,你心里有个数:

文件系统 特点 常见设备 提取难度
Squashfs 高压缩只读,支持压缩算法多 路由器、摄像头、IoT设备 ★★☆
JFFS2 日志型,支持NAND/NOR Flash 老款嵌入式设备、工业控制器 ★★★
UBIFS UBI层之上,针对NAND优化 新款路由器、手机基带 ★★★★
Cramfs 极简只读,Linux内核原生支持 老旧设备、极小固件 ★☆☆
YAFFS2 专为NAND Flash设计 部分安卓设备、相机 ★★★

嗯,这里要注意:Squashfs虽然提取难度标了两颗星,但遇到非标准压缩算法时,难度直接飙升到四星。我在项目中遇到过一台国产路由器,它用的Squashfs魔改版,lzma压缩参数被改了,binwalk直接报错——那次折腾了我整整一个下午。

4.2 binwalk工具使用

binwalk是固件分析的瑞士军刀,但很多人只会用binwalk -e。我建议你掌握这几个核心用法:

# 基础扫描,识别文件类型和偏移
binwalk firmware.bin

# 递归提取所有文件
binwalk -Me firmware.bin

# 只提取特定偏移范围的数据
binwalk -D 'squashfs:0:0x100000' firmware.bin

# 手动指定文件系统类型提取
binwalk -y 'squashfs' -e firmware.bin

# 查看文件系统详细信息
binwalk -T firmware.bin

为什么会这样?因为很多固件会故意混淆文件系统头。比如把Squashfs的魔数hsqs改成HSQS,或者把偏移地址藏到固件中间。binwalk的-y参数就是用来做模糊匹配的,能绕过一些简单的混淆。

我的小技巧:遇到binwalk扫不出来但明显有文件系统特征的固件,试试binwalk -W firmware.bin做熵分析。高熵区域通常就是压缩数据块,手动切出来再分析。

4.3 手动提取与挂载

binwalk自动提取失败时,就得手动上了。我一般按这个流程走:

  1. 定位偏移:用hexdump -C firmware.bin | grep "hsqs"找到魔数位置
  2. 切割数据dd if=firmware.bin of=rootfs.squashfs bs=1 skip=0x123456
  3. 尝试挂载mount -t squashfs -o loop rootfs.squashfs /mnt
  4. 如果挂载失败:用unsquashfssasquatch解包

举个例子,我曾经处理过一个JFFS2固件,binwalk死活提取不出来。我手动用dd切出疑似JFFS2的区域,然后用jefferson工具解包——结果发现厂商在JFFS2的cleanmarker里塞了自定义数据,导致标准工具无法识别。最后我手动修改了cleanmarker的校验值才搞定。

# JFFS2手动提取示例
dd if=firmware.bin of=jffs2_part.bin bs=1 skip=0x80000 count=0x400000
modprobe mtdblock
modprobe jffs2
# 创建MTD设备并挂载
mknod /tmp/mtdblock0 b 31 0
flash_erase /dev/mtd0 0 0
nandwrite -p /dev/mtd0 jffs2_part.bin
mount -t jffs2 /dev/mtdblock0 /mnt

警告:手动挂载JFFS2和UBIFS时,一定要确认MTD设备的大小和擦除块大小。搞错了轻则挂载失败,重则损坏Flash芯片。我见过有人把128KB擦除块设成64KB,结果整个分区数据全乱了。

4.4 文件系统修复与解包失败处理

解包失败是家常便饭。我总结了最常见的三种情况:

4.4.1 魔数被修改

厂商把文件系统头部的魔数改了,比如Squashfs的hsqs改成hsqS。解决办法:用十六进制编辑器手动改回来,或者用sed批量替换。

# 修复魔数示例
printf '\x68\x73\x71\x73' | dd of=firmware.bin bs=1 seek=0x123456 conv=notrunc

4.4.2 压缩算法不匹配

有些厂商用非标准压缩参数。比如Squashfs默认用gzip,但有人改成lzma或lzo。这时候需要sasquatch工具,它支持更多压缩算法。

# 使用sasquatch尝试多种压缩算法
sasquatch -d output_dir -ll rootfs.squashfs

4.4.3 文件系统损坏或截断

固件可能被截断,或者文件系统内部有坏块。我遇到过UBIFS的LEB(逻辑擦除块)顺序被打乱的情况。这时候需要用ubi_reader工具手动重建UBI卷。

# UBIFS修复示例
ubireader_extract_files -k -o output_dir ubifs_part.bin
# -k参数保留损坏的文件,尽量多提取数据

避坑指南:我曾经处理过一个YAFFS2固件,解包后文件全是乱码。后来发现厂商在YAFFS2的object header里加了自定义字段,导致标准工具解析偏移出错。解决办法是用yaffshiv工具配合自定义偏移表重新解析。记住:遇到非标准实现,别死磕通用工具,自己写脚本解析才是正道。

4.5 知识体系总览

下面这张图是我自己梳理的文件系统分析流程,你照着这个思路走,基本不会漏掉关键步骤:

固件文件系统分析流程 第一步:识别类型 binwalk / 熵分析 / 魔数 第二步:提取数据 dd / binwalk -e / 手动切割 第三步:挂载/解包 mount / unsquashfs / jefferson 成功? 分析文件 修复处理 修复处理子流程 魔数修复 sed / hexedit 压缩算法匹配 sasquatch / 自定义 坏块/截断修复 ubi_reader / yaffshiv 自定义偏移表 Python脚本解析 最终输出:完整文件系统 常用工具:binwalk | unsquashfs | jefferson | ubi_reader | sasquatch | yaffshiv

这张图把整个流程串起来了。你从识别开始,走提取、挂载,遇到失败就进修复子流程,修完再回来继续。循环几次,大部分固件都能搞定。

最后说一句:文件系统分析没有银弹。每个厂商都有自己的「小九九」,魔改参数、自定义头、混淆偏移……这些我都见过。保持耐心,多试几种工具组合,实在不行就自己写Python脚本逐字节解析。记住:固件逆向的本质是跟厂商斗智斗勇,不是跟工具较劲。


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