第二讲:固件基础与逆向工程入门

大家好,欢迎来到第二讲。今天咱们聊聊固件结构,这是后续所有分析工作的地基。我刚开始接触固件安全时,也踩过不少坑——比如解包到一半发现文件系统不对,或者提取出来的内核根本跑不起来。嗯,今天就把这些经验一并分享给你。

固件到底长什么样?

说白了,固件就是嵌入式设备的操作系统镜像。它通常包含三个核心部分:Bootloader、Kernel 和 Rootfs。我习惯把它们比作「启动三部曲」——先引导、再加载内核、最后挂载文件系统。

核心三要素:

  • Bootloader:负责硬件初始化,加载内核到内存。常见的有 U-Boot、RedBoot、Barebox。
  • Kernel:操作系统的核心,管理硬件资源。通常是 Linux 内核,也可能是 VxWorks、RTOS。
  • Rootfs:根文件系统,包含用户程序、库文件、配置文件。常见格式有 SquashFS、JFFS2、YAFFS2、CramFS。

你想想看,如果连固件结构都搞不清楚,后面做漏洞挖掘就像蒙着眼睛找针。我在一次路由器固件分析中,就遇到过 Bootloader 被加密的情况——解包出来全是乱码,后来才发现是厂商加了自定义头部。

固件结构示意图

下面这张图是我自己画的,帮你直观理解固件在 Flash 中的布局:

典型固件 Flash 布局结构 Flash 芯片 (SPI NOR / NAND) Bootloader U-Boot / RedBoot 偏移: 0x0 ~ 0x2FFFF Kernel Linux / VxWorks 偏移: 0x30000 ~ 0x9FFFF Rootfs SquashFS / JFFS2 偏移: 0xA0000 ~ 结束 ① 上电 → Bootloader 启动 ② 加载内核到内存 ③ 挂载根文件系统 额外区域 配置参数 / 校准数据 / 日志 / 升级备份区 Flash 起始地址: 0x00000000 Flash 结束地址: 0x01000000 (16MB)

这张图展示了一个典型的 16MB SPI Flash 布局。Bootloader 在最前面,接着是内核,最后是文件系统。实际项目中,厂商可能会在头部加自定义签名或加密,这时候就需要手动分析偏移量了。

常用固件提取工具

拿到固件文件后,第一步就是提取里面的内容。我常用的工具主要有两个:binwalkdd。它们各有各的脾气,配合使用效果最好。

binwalk —— 固件分析的瑞士军刀

binwalk 是我最常用的工具。它能自动识别固件中的文件系统、压缩包、内核镜像等。说白了,它就是靠「魔数签名」来猜文件类型的。

我的习惯:拿到固件后,先跑一遍 binwalk -Me firmware.bin,让它自动解包。如果解不出来,再手动分析。

常用命令一览:

命令 作用 个人备注
binwalk firmware.bin 扫描固件中的文件签名 先看输出,确认有哪些组件
binwalk -e firmware.bin 自动提取所有可识别文件 最常用,但有时会漏
binwalk -Me firmware.bin 递归提取 + 数学模式扫描 对付加密或混淆固件有效
binwalk -D 'squashfs:rootfs' firmware.bin 指定提取特定类型文件 当自动提取失败时手动指定

注意:binwalk 不是万能的。我曾经遇到一个固件,厂商把 SquashFS 的魔数改成了 0xDEADBEEF,binwalk 直接认不出来。这时候就得靠 dd 手动切了。

dd —— 最原始的切分工具

dd 是 Linux 下的数据复制工具,但用来切固件特别顺手。当你知道了某个组件的偏移量和大小,用 dd 就能精确提取。

基本用法:

# 从固件中提取 Bootloader(偏移 0x0,大小 0x30000)
dd if=firmware.bin of=bootloader.bin bs=1 skip=0 count=196608

# 提取内核(偏移 0x30000,大小 0x70000)
dd if=firmware.bin of=kernel.bin bs=1 skip=196608 count=458752

# 提取文件系统(偏移 0xA0000)
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=655360

你可能会问:「偏移量怎么知道?」嗯,这就是 binwalk 的用武之地了。先用 binwalk 扫描,它会告诉你每个组件的起始地址和大小,然后你用 dd 精确切分。

固件解包与打包实战

理论说完了,咱们来点实际的。我拿一个常见的路由器固件做演示,手把手带你走一遍流程。

第一步:扫描固件结构

$ binwalk DIR-859_A1_FW122b02.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             TRX firmware header, little endian, image size: 8388608
196           0xC4            LZMA compressed data, properties: 0x5D
1310720       0x140000        SquashFS filesystem, little endian, version 4.0
...

看到没?这个固件包含 TRX 头部、LZMA 压缩数据,以及 SquashFS 文件系统。偏移量 0x140000 就是文件系统的起始位置。

第二步:提取文件系统

# 用 binwalk 自动提取
binwalk -e DIR-859_A1_FW122b02.bin

# 或者用 dd 手动提取
dd if=DIR-859_A1_FW122b02.bin of=rootfs.squashfs bs=1 skip=1310720

我个人更推荐先用 binwalk -e,如果它搞不定,再手动来。有一次 binwalk 提取出来的 SquashFS 是坏的,我手动用 dd 切出来反而能用——原因可能是 binwalk 的自动提取逻辑在某些边界情况下会出错。

第三步:解包文件系统

SquashFS 需要用 unsquashfs 工具解包:

# 解包 SquashFS
unsquashfs rootfs.squashfs

# 解包后会在当前目录生成 squashfs-root 文件夹
ls squashfs-root/
bin/  dev/  etc/  lib/  mnt/  proc/  sbin/  tmp/  usr/  var/

看到熟悉的 Linux 目录结构了吗?到这里,你就可以开始分析里面的二进制文件、配置文件、Web 界面代码了。

第四步:修改后重新打包

分析完了,如果想修改固件再刷回去,就需要重新打包:

# 重新打包为 SquashFS
mksquashfs squashfs-root/ new_rootfs.squashfs -comp xz -b 131072

# 用 binwalk 重新组装固件(需要保留原头部和内核)
cat original_header.bin kernel.bin new_rootfs.squashfs > new_firmware.bin

避坑指南:我曾经在重新打包时忘了保持原来的压缩算法和块大小,结果刷进去设备直接变砖。所以,打包前一定要用 unsquashfs -s rootfs.squashfs 查看原文件系统的参数。

常见文件系统格式对比

不同厂商喜欢用不同的文件系统,我整理了一个表格,方便你快速对照:

文件系统 特点 常见设备 解包工具
SquashFS 只读、高压缩比 路由器、摄像头 unsquashfs
JFFS2 可读写、磨损均衡 工业设备、旧款路由器 jefferson / jffs2dump
YAFFS2 专为 NAND Flash 设计 手机、嵌入式设备 unyaffs
CramFS 古老、简单 老旧设备 cramfsck
UBIFS 新一代、支持大容量 新式路由器、IoT 设备 ubireader / ubidump

嗯,看到这里,你应该对固件结构有了整体认识。记住:拿到固件先扫结构,再提取组件,最后解包分析。这个流程我用了好几年,基本没出过大问题。

下一讲我们会深入 Bootloader 的逆向分析,到时候再聊 U-Boot 的那些坑。今天就到这里,动手试试吧。


专注资料整理