2、固件基础与结构:固件在嵌入式系统中的角色、常见固件格式(BIN/HEX/ELF)、固件启动流程(Bootloader->Kernel->Rootfs)

大家好,我是老周。今天咱们聊聊固件的基础结构。说实话,这章是整个课程的基石。你想想看,连固件长什么样都不清楚,后面怎么逆向分析?怎么挖漏洞?

我在做安全评估的时候,经常遇到一些朋友,上来就问「怎么破解这个设备」。我一般会反问一句:你先告诉我,它的固件是什么格式?启动流程是怎样的?嗯,大部分人就卡住了。

所以,咱们先把地基打牢。

2.1 固件在嵌入式系统中的角色

固件是什么?说白了,就是嵌入在硬件设备里的软件。它不像Windows、Linux那样装在硬盘上,而是直接烧录在Flash、ROM这类非易失性存储器里。

它的角色,我总结为三点:

  • 硬件初始化:上电后,CPU、内存、外设这些硬件,都得靠固件来「唤醒」和配置。
  • 系统引导:把操作系统内核加载到内存,然后跳转执行。
  • 应用功能:很多小型设备(比如智能插座、传感器),固件本身就包含了全部业务逻辑,没有独立的操作系统。

重要提醒:在安全分析中,固件就是攻击者的「第一战场」。你拿到了固件,就等于拿到了设备的「灵魂」。我曾经在一个路由器的固件里,直接找到了硬编码的后门账号——嗯,那家厂商后来被通报了。

2.2 常见固件格式:BIN / HEX / ELF

做逆向分析,你得先认识这三种最常见的格式。我刚开始接触时也搞混过,后来踩了几个坑才彻底搞明白。

2.2.1 BIN 格式(原始二进制)

BIN 文件,就是最纯粹的二进制数据。没有头,没有尾,没有元信息。它是什么?就是内存或Flash的「裸拷贝」。

  • 特点:简单、直接、体积小。
  • 缺点:没有地址信息,你不知道这段数据该加载到哪个地址。
  • 分析工具:binwalk、hexdump、010 Editor。

我记得有一次,客户给了一个BIN文件,说「帮我看看里面有什么」。我直接用binwalk跑了一下,发现里面嵌了一个完整的Linux内核和文件系统。嗯,这种「裸奔」的固件,在IoT设备里非常常见。

2.2.2 HEX 格式(Intel HEX)

HEX 格式,是文本形式的二进制数据。每一行都包含地址、数据类型和校验和。它比BIN多了地址信息。

举个例子,一个典型的HEX行是这样的:

:10000000FF00400000000000000000000000000000E0

拆开来看:

  • : 起始标记
  • 10 本行数据长度(16字节)
  • 0000 起始地址
  • 00 数据类型(00=数据,01=文件结束)
  • FF004000... 实际数据
  • E0 校验和

个人技巧:我习惯用 objcopy 把HEX转成BIN,然后再用binwalk分析。因为binwalk对BIN的支持最好,对HEX反而容易出问题。

2.2.3 ELF 格式(Executable and Linkable Format)

ELF 是Linux世界里最常用的可执行文件格式。它比BIN和HEX都复杂,包含了:

  • ELF头:魔数、架构、入口点等。
  • 程序头:描述如何加载到内存。
  • 节头:描述代码段、数据段、符号表等。

在嵌入式领域,很多Bootloader和内核镜像都是ELF格式。比如U-Boot,编译出来就是ELF文件。

我建议你用 readelf -hreadelf -l 来查看ELF的结构。举个例子:

$ readelf -h u-boot.elf
ELF Header:
  Magic:   7f 45 4c 46
  Class:   ELF32
  Machine: ARM
  Entry point address: 0x00000000

看到那个 7f 45 4c 46 了吗?这就是ELF的魔数,也就是 .ELF 的ASCII码。嗯,这个细节在逆向分析时很有用——你可以在二进制文件里搜索这个魔数,快速定位ELF镜像。

2.3 固件启动流程:Bootloader → Kernel → Rootfs

这是嵌入式系统启动的「三步曲」。我画了一张图,帮你直观理解:

嵌入式系统启动流程 Bootloader 初始化硬件 加载内核到内存 跳转到内核入口 跳转 Kernel 解压自身 初始化驱动 挂载根文件系统 挂载 Rootfs 启动init进程 加载应用程序 系统就绪 三个阶段,环环相扣。任何一个环节被攻破,整个系统就沦陷了。 ⚠ 安全风险点:Bootloader未锁定 | 内核未签名 | Rootfs可写

这张图,我建议你保存下来。每次分析固件时,对照着看,思路会清晰很多。

2.3.1 第一阶段:Bootloader

Bootloader 是系统上电后第一个执行的程序。它的任务很简单:初始化硬件,然后加载内核。

常见的Bootloader有:

名称 适用架构 特点
U-Boot ARM、MIPS、x86等 功能强大,支持网络启动、Flash烧写
Barebox ARM、MIPS 类似Linux内核,模块化设计
RedBoot ARM、MIPS 轻量级,常用于网络设备
Coreboot x86、ARM 极速启动,常用于PC/服务器

避坑指南:我曾经遇到一个设备,Bootloader没有锁定。攻击者通过串口中断了启动过程,直接进入了U-Boot命令行。然后他修改了启动参数,加载了自己的恶意内核。嗯,这就是典型的「Bootloader攻击」。所以,生产环境中一定要锁定Bootloader,禁用不必要的调试接口。

2.3.2 第二阶段:Kernel

Bootloader 把内核加载到内存后,就跳转到内核入口点。内核开始执行后,会做以下几件事:

  1. 解压自身:很多嵌入式内核是压缩的(比如zImage、uImage),需要先解压。
  2. 初始化硬件抽象层:设置MMU、中断控制器、定时器等。
  3. 加载驱动:根据设备树(Device Tree)或ACPI,加载对应的硬件驱动。
  4. 挂载根文件系统:这是最后一步,也是最关键的一步。

在安全分析中,内核镜像往往是重点。我习惯用 binwalk -Me 先解包,然后用 strings 搜索敏感信息。比如:

$ strings vmlinux.bin | grep -i "password\|key\|secret"

你猜怎么着?我经常能在内核里找到硬编码的密钥或者调试密码。嗯,厂商们总是「忘记」清理这些调试信息。

2.3.3 第三阶段:Rootfs

根文件系统(Rootfs)是系统启动后的「家」。它包含了所有用户空间的程序、库、配置文件。

常见的根文件系统格式:

  • SquashFS:只读、高压缩比。常用于路由器、摄像头。
  • JFFS2 / UBIFS:可读写、支持闪存。常用于高端设备。
  • initramfs:内存中的文件系统,常用于临时启动。
  • YAFFS2:专为NAND Flash设计。

我个人习惯,拿到固件后第一件事就是解包Rootfs。因为这里藏着最多的「宝藏」:

  • 硬编码的密码
  • 私钥和证书
  • 调试后门
  • 过时的漏洞库

实用技巧:解包SquashFS时,我常用 unsquashfs 命令。如果遇到未知格式,试试 binwalk -Me,它能自动识别并解包大部分文件系统。

2.4 小结

好了,这一章的内容就到这里。我们讲了固件的角色、三种常见格式(BIN/HEX/ELF),以及启动流程的三部曲。这些都是基本功,但也是最容易被忽视的。

我记得刚入行时,总觉得这些「太简单」,直接跳过去学高级技巧。结果呢?分析一个固件时,连它是什么格式都没搞清楚,折腾了半天。嗯,从那以后,我每分析一个固件,都会先做这三步:

  1. 识别固件格式
  2. 提取文件系统
  3. 分析启动脚本

你试试看,按照这个流程走一遍,很多问题会迎刃而解。


专注资料整理