4、固件文件系统分析:常见文件系统的挂载与提取

固件逆向工程中,文件系统是承载操作系统内核、应用程序、配置参数与设备驱动的核心载体。当我们从固件镜像中识别出文件系统类型并成功提取其内容时,就相当于拿到了设备内部世界的“地图”。本章将围绕嵌入式设备中最常见的四种文件系统——SquashFS、JFFS2、CramFS、UBIFS——展开深入分析,讲解其结构原理、识别方法、挂载提取流程以及实战中的常见陷阱。同时,我们会系统梳理 binwalk 这一核心工具的高级用法,帮助你在面对未知固件时能够快速定位文件系统偏移并完成内容抽取。

固件文件系统分析决策链路 固件镜像 .bin / .img / .trx binwalk 扫描 文件签名 / 熵分析 类型判断 SquashFS / JFFS2 / CramFS / UBIFS 提取方法 unsquashfs / jefferson / cramfsck / ubireader SquashFS 只读压缩文件系统 魔数: hsqs / sqsh 工具: unsquashfs 压缩: gzip/lzma/lzo/xz JFFS2 日志闪存文件系统 魔数: 0x1985 工具: jefferson NOR/NAND 闪存 CramFS 压缩只读文件系统 魔数: 0x28cd3d45 工具: cramfsck zlib 压缩 UBIFS UBI 卷上的文件系统 无固定魔数 工具: ubireader NAND 闪存 步骤1: 识别偏移 binwalk -A 或 -E 确认 步骤2: 切割镜像 dd if=fw.bin of=fs.img bs=1 skip=偏移 步骤3: 挂载/解包 mount -o loop / 专用工具 ⚠ 注意:UBIFS 需要 nandsim 模拟器或 ubireader 提取,不能直接 loop 挂载

上图展示了固件文件系统分析的核心决策链路。在实际操作中,我们通常从固件镜像出发,通过 binwalk 扫描识别文件系统类型与偏移,再选择合适的工具进行提取。下面我们将逐一深入每个环节。

4.1 核心原理:四种文件系统的结构与适用场景

嵌入式设备中的文件系统设计往往围绕“只读、压缩、闪存友好”三个核心诉求展开。理解每种文件系统的底层设计逻辑,是正确选择提取工具和判断异常偏移的前提。

文件系统 设计目标 存储介质 压缩方式 关键魔数/特征 典型工具
SquashFS 高压缩比只读 NOR/NAND/块设备 gzip/lzma/lzo/xz/zstd "hsqs" (小端) / "sqsh" (大端) unsquashfs, 7z
JFFS2 闪存日志型读写 NOR/NAND zlib/rtime/lzma 节点魔数 0x1985 (小端 0x8519) jefferson, jffs2dump
CramFS 简单压缩只读 块设备/内存 zlib 0x28cd3d45 (小端) cramfsck, cramfsswap
UBIFS UBI卷上的日志型 NAND (需UBI层) lzo/zlib 无固定魔数,依赖UBI卷布局 ubireader, nandsim+mount

SquashFS 是目前路由器、IoT 网关和工业设备中最常见的只读文件系统。它将整个文件系统打包成一个压缩的块设备镜像,支持随机读取,且压缩率极高。其镜像头部包含超级块结构,偏移 0 处存放魔数 "hsqs"(小端)或 "sqsh"(大端),通过该魔数可以快速定位文件系统在固件中的起始偏移。

JFFS2 是专为闪存设计的日志型文件系统,它直接在原始 NAND/NOR 闪存上运行,具备磨损均衡和掉电恢复能力。JFFS2 没有传统意义上的超级块,而是由一系列节点(node)组成,每个节点头部包含魔数 0x1985。在固件分析中,JFFS2 镜像通常以原始闪存转储的形式存在,提取时需要遍历所有节点并重组文件。

CramFS 是一种较老的压缩只读文件系统,结构简单,但压缩率不如 SquashFS。其超级块魔数为 0x28cd3d45,且文件数据按页压缩存储。CramFS 在老旧的路由器(如 Broadcom 芯片组)中仍有出现,分析时需注意字节序问题。

UBIFS 运行在 UBI(Unsorted Block Images)层之上,专为现代大容量 NAND 闪存设计。UBIFS 没有固定魔数,其识别依赖于 UBI 卷描述符和擦除块头(EC header)。提取 UBIFS 镜像通常需要先解析 UBI 层,再使用 ubireader 或通过 nandsim 模拟 NAND 设备后挂载。

经验要点:在固件分析中,SquashFS 和 JFFS2 占据了约 80% 的案例。SquashFS 多用于只读根文件系统,JFFS2 多用于可写分区(如 /overlay、/data)。CramFS 多见于 2010 年以前的设备,UBIFS 则常见于较新的 NAND 设备。

4.2 判断方法与操作流程:从 binwalk 扫描到文件提取

面对一个未知固件,系统化的分析流程可以大幅提升效率。以下是经过实战验证的标准操作路径。

4.2.1 初步扫描:binwalk 基础与进阶用法

binwalk 是固件分析中最核心的工具,它通过扫描文件签名(magic bytes)来识别嵌入的文件系统、内核、引导加载程序等。基础用法如下:

# 基本扫描
binwalk firmware.bin

# 详细扫描(显示所有签名,包括非文件系统)
binwalk -A firmware.bin

# 熵分析(识别加密或压缩区域)
binwalk -E firmware.bin

# 提取所有识别的文件
binwalk -e firmware.bin

# 指定提取目录
binwalk -e -C ./output firmware.bin

当 binwalk 输出中出现类似 SquashFS filesystem, little endian, version 4.0, compression: xz 的信息时,说明已识别到文件系统及其偏移地址。例如:

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             uImage header, header size: 64 bytes, header CRC: 0x...
64            0x40            Linux kernel ARM boot executable zImage
1048576       0x100000        SquashFS filesystem, little endian, version 4.0, compression: xz

上述输出表明 SquashFS 文件系统起始于偏移 0x100000(1MB 处)。此时可以使用 dd 命令切割出独立的文件系统镜像:

dd if=firmware.bin of=squashfs.img bs=1 skip=1048576

4.2.2 各文件系统的提取与挂载

SquashFS 提取:使用 unsquashfs 工具直接解包:

# 查看镜像信息
unsquashfs -s squashfs.img

# 解包到指定目录
unsquashfs -d ./squashfs-root squashfs.img

如果 unsquashfs 报错,可能是版本不匹配(如 v3 vs v4),可尝试 7z x squashfs.img 或使用 sasquatch(binwalk 的补丁版 unsquashfs,支持非标准块大小)。

JFFS2 提取:JFFS2 没有标准的挂载工具,推荐使用 jefferson(Python 脚本):

# 安装 jefferson
pip install jefferson

# 提取 JFFS2 镜像
jefferson jffs2.img -d ./jffs2-root

如果 JFFS2 镜像带有擦除块头(cleanmarker),可能需要先去除 0x20000 字节的块头。此时可以尝试:

dd if=jffs2.img of=jffs2-clean.img bs=1 skip=131072
jefferson jffs2-clean.img -d ./jffs2-root

CramFS 提取:使用 cramfsck 工具:

# 检查并提取
cramfsck -x ./cramfs-root cramfs.img

如果遇到字节序问题(大端 CramFS),需要先使用 cramfsswap 转换:

cramfsswap cramfs.img cramfs-le.img
cramfsck -x ./cramfs-root cramfs-le.img

UBIFS 提取:UBIFS 提取相对复杂,推荐使用 ubireader:

# 安装 ubireader
pip install ubi_reader

# 提取 UBI 镜像中的文件
ubireader_extract_files ubi.img -o ./ubi-root

# 如果镜像包含多个 UBI 卷,可先列出卷信息
ubireader_extract_info ubi.img

另一种方法是使用 nandsim 模拟 NAND 设备后挂载:

# 加载 nandsim 模块(需要 root)
modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15

# 将 UBI 镜像写入模拟设备
ubiformat /dev/mtd0 -f ubi.img
modprobe ubi
ubiattach /dev/ubi_ctrl -m 0

# 挂载 UBIFS 卷
mount -t ubifs ubi0:rootfs /mnt/ubifs

风险提示:使用 nandsim 需要内核支持 NAND 模拟和 UBI 模块,且需要 root 权限。在虚拟机或容器中可能无法加载内核模块。优先推荐 ubireader 进行无内核提取。

4.3 案例拆解:某品牌路由器固件的完整提取过程

下面通过一个真实案例,展示从固件下载到文件系统内容获取的完整闭环。

背景与约束:某品牌家用路由器固件 router_fw.bin,大小 16MB。目标是提取 Web 管理界面的源码和默认配置文件,以分析潜在的后门账号。设备使用 NAND 闪存,固件包含 U-Boot、Linux 内核和根文件系统。

初始现象:直接运行 binwalk router_fw.bin 只识别出 uImage 头,未发现文件系统签名。这通常意味着文件系统被嵌套或使用了非标准偏移。

关键操作步骤:

  1. 熵分析:运行 binwalk -E router_fw.bin,发现偏移 0x500000 之后熵值接近 1.0(高熵),表明存在压缩数据。
  2. 手动扫描:使用 binwalk -A 强制扫描所有已知签名,发现偏移 0x500000 处有 SquashFS 魔数 "hsqs"。
  3. 切割镜像:dd if=router_fw.bin of=rootfs.squashfs bs=1 skip=5242880 count=11534336(切割 11MB)。
  4. 解包:unsquashfs -d ./rootfs rootfs.squashfs,成功解出完整根文件系统。
  5. 深入分析:在 ./rootfs/etc/ 下发现 shadow 文件,其中包含 root 账号的哈希值;在 ./rootfs/www/ 下找到 CGI 脚本源码。

结果对比:通过熵分析定位到隐藏偏移,比盲目扫描节省了约 30 分钟。提取出的 shadow 文件经过 hashcat 破解,发现设备使用弱密码 "admin123",证实了设备存在默认凭据风险。

风险与代价:切割镜像时如果偏移计算错误,会导致解包失败。建议在 dd 前使用 binwalk -A 确认偏移,并保留原始固件备份。

复盘经验:当 binwalk 默认扫描无法识别文件系统时,不要立即放弃。熵分析是定位压缩文件系统的有效手段,配合 -A 强制扫描往往能发现被嵌套的签名。

4.4 常见误区与总结复盘

在固件文件系统分析中,初学者常犯以下错误,导致提取失败或分析结果偏差。

  • 误区一:忽略字节序。SquashFS 和 CramFS 都有大端/小端变体。在 MIPS 或 PowerPC 设备中,大端镜像非常常见。使用 unsquashfs 报错时,先检查字节序。
  • 误区二:直接挂载原始固件。很多初学者尝试 mount -o loop firmware.bin /mnt,但固件中通常包含多个分区,直接挂载会失败。必须先切割出文件系统部分。
  • 误区三:忽略 JFFS2 的擦除块头。JFFS2 镜像可能包含 0x20000 字节的擦除块头(cleanmarker),导致 jefferson 无法识别。需要先跳过这些头部。
  • 误区四:UBIFS 尝试 loop 挂载。UBIFS 不是块设备文件系统,无法通过 loop 设备挂载。必须使用 UBI 层或 ubireader。
  • 误区五:binwalk 提取不完整。binwalk -e 有时会漏掉文件系统内部的小文件,尤其是当文件系统被压缩时。建议使用专用工具二次提取。

总结建议:固件文件系统分析的核心是“识别-切割-提取-验证”四步法。识别阶段优先使用 binwalk 默认扫描 + 熵分析;切割阶段务必确认偏移和大小;提取阶段根据文件系统类型选择专用工具;验证阶段检查提取出的关键文件(如 /etc/passwd、/etc/shadow、/www/index.html)是否完整。掌握这四种文件系统的特性,能够覆盖绝大多数嵌入式设备的固件分析需求。