3. 文件系统提取与探索:手动提取SquashFS文件系统

好,咱们继续。上一节我们把固件解包了,拿到了一个 bin 文件。但说实话,那只是第一步。真正的宝藏,都藏在文件系统里。对于思科这类设备,SquashFS 几乎是标配。为什么?因为它压缩率高,适合嵌入式设备的有限存储空间。

我个人习惯,拿到固件后第一件事就是确认文件系统的类型。别急着解压,先看看它到底是什么。

3.1 识别 SquashFS 魔数

每个文件系统都有自己独特的“身份证”,也就是魔数。SquashFS 的魔数通常是 hsqs 或者 sqsh。我们可以用 hexdump 或者 binwalk 来确认。

# 用 hexdump 查看文件头部
hexdump -C firmware.bin | head -20

# 或者直接用 binwalk 扫描
binwalk firmware.bin

如果看到类似这样的输出:

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             uImage header, ...
...
1048576       0x100000        SquashFS filesystem, little endian, version 4.0, ...

嗯,那就对了。注意看偏移量,比如这里是 0x100000。这个地址就是 SquashFS 的起始位置。

我的经验: 有时候固件里会包含多个 SquashFS 镜像。比如一个用于恢复模式,一个用于正常启动。别漏了。我曾经在一个 AP 固件里挖出过三个文件系统,每个都藏着不同的秘密。

3.2 手动提取 SquashFS

知道了偏移量,提取就简单了。我们可以用 dd 命令来切分。

# 从偏移量 0x100000 开始,提取 SquashFS 部分
dd if=firmware.bin of=squashfs.img bs=1 skip=$((0x100000))

但这里有个坑:dd 默认会一直读到文件末尾。如果固件后面还有别的数据(比如 JFFS2 分区),你会把垃圾也一起读进来。更好的做法是先确定 SquashFS 的大小。

怎么确定?用 binwalk -e 可以自动提取,但咱们是手动派,得自己来。你可以用 unsquashfs -s 来查看镜像信息:

# 先试一下,看能不能识别
unsquashfs -s squashfs.img

如果报错说“文件太小”,说明你多切了或者少切了。调整一下偏移量再试。我一般会先用 binwalk-Me 选项自动提取一次,然后对照它的输出,手动验证一遍。这样既能保证效率,又能加深理解。

注意: 有些固件厂商会对 SquashFS 进行魔改,比如修改压缩算法或者添加自定义头部。遇到这种情况,标准工具可能解不开。你需要逆向分析它的解压逻辑。这个话题我们后面会专门讲。

3.3 解压与探索目录结构

拿到干净的 squashfs.img 后,解压就简单了:

# 解压到当前目录下的 squashfs-root 文件夹
unsquashfs squashfs.img

如果一切顺利,你会看到一个完整的 Linux 文件系统。来看看典型的思科设备目录结构:

目录 说明
/bin 核心二进制文件,比如 busybox
/sbin 系统管理工具,httpd、ntpd 通常在这里
/usr/sbin 另一个常见位置,dhcpd 可能在这里
/etc 配置文件,启动脚本
/lib 共享库,依赖分析的关键
/www Web 管理界面文件

我个人习惯先看 /etc/init.d 或者 /etc/rc.d,那里有启动脚本。从启动脚本里,你能看到哪些服务是默认开启的,它们的启动参数是什么。这比直接翻二进制文件要快得多。

3.4 定位关键二进制文件

好,现在我们来定位那三个关键目标:httpdntpddhcpd

最直接的方法是用 find 命令:

# 在解压后的目录里搜索
find squashfs-root -type f -name "httpd"
find squashfs-root -type f -name "ntpd"
find squashfs-root -type f -name "dhcpd"

但有时候,厂商会给二进制文件改名。比如 httpd 可能叫 mini_httpd 或者 thttpd。这时候,你需要用 file 命令来识别:

# 查看文件类型
file squashfs-root/sbin/* | grep ELF

把所有 ELF 文件列出来,然后根据大小和名字猜测。我遇到过最坑的一次,httpd 被编译进了 busybox 里,根本找不到单独的二进制文件。那怎么办?只能从启动脚本里找线索,看它调用了哪个符号链接。

实战技巧: 找到二进制文件后,别急着分析。先看看它的依赖库是否完整:

# 查看动态链接库依赖
readelf -d squashfs-root/sbin/httpd | grep NEEDED

如果缺少某个 .so 文件,你的动态分析可能会失败。提前准备好对应的库文件,或者考虑静态编译的版本。

3.5 我的避坑指南

做文件系统提取这么多年,我踩过不少坑。分享几个典型的:

  • 偏移量算错: 有时候固件头部有多个 uImage 嵌套,偏移量要一层层剥。我曾经因为少算了一个头部偏移,浪费了半天时间。
  • 压缩算法不匹配: 老版本 SquashFS 用 gzip,新版本用 lz4 或 zstd。如果 unsquashfs 报错,试试指定算法:unsquashfs -comp xz squashfs.img
  • 文件权限丢失: 解压后有些文件可能是只读的,记得用 chmod -R +w 改一下权限,不然后面修改配置时会报错。
  • 符号链接断裂: 嵌入式系统里大量使用相对路径的符号链接。如果你把文件系统移到别的目录下,链接可能失效。用 find -type l -xtype l 可以找出所有断链。

嗯,说到这,我想起一个项目。当时分析一台思科交换机,固件里明明有 dhcpd,但启动脚本里却找不到对应的服务。后来发现,它被一个叫 dnsmasq 的程序替代了,而 dnsmasq 同时提供了 DHCP 和 DNS 功能。所以,别死盯着名字,要看功能。

3.6 本章知识体系

为了让你更直观地理解整个流程,我画了一张图:

SquashFS 文件系统提取与探索流程 步骤1:固件分析 步骤2:识别魔数 hexdump / binwalk 步骤3:手动提取 dd + unsquashfs 步骤4:解压文件系统 unsquashfs squashfs.img 步骤5:探索目录结构 /bin /sbin /etc /lib /www 步骤6:定位关键二进制 find / file / readelf 步骤7:依赖分析 ldd / readelf -d 目标:httpd、ntpd、dhcpd

这张图把整个流程串起来了。从固件分析开始,到识别魔数、手动提取、解压、探索目录,最后定位到关键二进制文件并做依赖分析。每一步都有对应的工具和方法。

好了,这一章的内容就到这里。记住,文件系统提取是固件逆向的基础,基础打牢了,后面的分析才能事半功倍。下一章我们会深入分析 httpd 的二进制代码,看看它的 Web 服务是怎么处理请求的。