3、固件基础知识:固件定义、固件类型(Bootloader、RTOS、应用固件)、固件文件格式(BIN、HEX、ELF、UBoot镜像)
固件(Firmware)是嵌入式系统中介于硬件与上层软件之间的关键层,它直接驱动硬件寄存器、管理中断向量、调度任务并承载业务逻辑。与通用计算机的BIOS/UEFI不同,嵌入式固件往往以“裸机镜像”或“RTOS镜像”的形式整体烧录到NOR Flash、NAND Flash、eMMC或SPI Flash中。理解固件的定义、类型与文件格式,是进行逆向分析、二进制提取、漏洞挖掘和固件更新的前提。本章将从固件的本质出发,梳理Bootloader、RTOS、应用固件的分层关系,并深入解析BIN、HEX、ELF、UBoot镜像四种主流格式的存储结构、加载地址与转换方法。
3.1 固件定义与分层架构
核心原理
固件是存储在非易失性存储器中的软件集合,它直接操作硬件寄存器,不依赖操作系统或中间件。在嵌入式系统中,固件通常由三部分构成:Bootloader(引导程序)、RTOS内核(实时操作系统)和应用固件(业务逻辑)。三者并非总是同时存在——小型MCU项目可能只有裸机应用固件,而复杂的SoC(如高通、海思平台)则包含多级Bootloader(如XBL、ABL、U-Boot)和多个RTOS/HLOS镜像。
从逆向工程视角看,固件是一个“二进制大对象”(Binary Large Object, BLOB),其内部结构取决于编译链接脚本(Linker Script)和启动流程。分析固件的第一步是确定其加载地址(Load Address)和入口点(Entry Point),这决定了反汇编时使用的基址。例如,ARM Cortex-M系列通常从0x08000000(Flash起始)启动,而Cortex-A系列则可能从0x40000000或0x80000000启动。
判断方法
- 查看启动向量表:ARM Cortex-M的向量表位于Flash起始地址,前4字节为初始SP值,第5字节为Reset_Handler地址。通过分析向量表可快速定位固件类型。
- 识别RTOS特征:FreeRTOS的TCB(任务控制块)包含固定偏移的字段,uC/OS-II的OS_TCB有特定签名;RT-Thread的线程控制块也有独特结构。
- 分析Bootloader字符串:U-Boot镜像通常包含"U-Boot 2020.04"等版本字符串,可通过binwalk或strings工具提取。
案例拆解:从路由器固件中分离三部分
某家用路由器固件(16MB SPI Flash)提取后,使用binwalk -e扫描,发现偏移0x0处为U-Boot(约256KB),0x40000处为Linux内核(LZMA压缩),0x200000处为SquashFS根文件系统。进一步分析U-Boot环境变量,发现bootcmd指定了内核加载地址0x82000000,设备树地址0x81000000。通过dd命令分别提取三个分区,再使用jefferson解包SquashFS,最终定位到Web服务程序(应用固件)的二进制文件。
binwalk、firmware-mod-kit或ubi_reader等工具,先识别文件系统边界,再针对每个分区单独逆向。切勿直接对整个固件进行线性反汇编,否则会因数据与代码混杂导致大量无效指令。
3.2 固件类型详解:Bootloader、RTOS、应用固件
3.2.1 Bootloader
Bootloader是上电后最先执行的代码,负责初始化时钟、DDR、Flash控制器,并加载后续镜像。常见Bootloader包括:U-Boot(广泛用于ARM/PPC/MIPS)、Coreboot(x86/ARM服务器)、Little Kernel (LK)(Android设备)、Barebox(嵌入式Linux)。Bootloader通常包含网络协议栈(TFTP/NFS)、Flash编程命令和交互式Shell,这些功能为逆向分析提供了丰富的字符串和函数特征。
| Bootloader类型 | 典型平台 | 存储介质 | 逆向特征 |
|---|---|---|---|
| U-Boot | ARM64 / ARM32 / RISC-V | SPI NOR / eMMC | 包含"U-Boot"字符串、md/cp/mw命令表 |
| LK (Little Kernel) | Qualcomm / MediaTek | eMMC / UFS | ABL(Android Bootloader)分区,包含"LK"版本号 |
| Barebox | i.MX / AM335x | NAND / NOR | 类似Linux的Shell,支持devicetree |
3.2.2 RTOS内核
RTOS提供任务调度、中断管理、时间片轮转和同步原语。常见RTOS包括:FreeRTOS(开源,MCU主流)、RT-Thread(国产,支持POSIX)、VxWorks(商业,航空航天)、ThreadX(微软,IoT)。RTOS固件的逆向重点在于识别任务控制块(TCB)结构、调度器入口和任务栈布局。例如,FreeRTOS的pxCurrentTCB指向当前任务,通过分析任务切换函数(如vTaskSwitchContext)可定位所有任务入口。
3.2.3 应用固件
应用固件是用户业务逻辑的载体,可能包含网络协议栈(lwIP)、文件系统(LittleFS)、加密算法(mbedTLS)等。在物联网设备中,应用固件往往通过OTA(Over-The-Air)更新,因此逆向时需关注固件签名验证逻辑和版本回滚保护机制。
pxCurrentTCB和任务栈内容,动态确认任务优先级和状态。静态分析容易将任务函数误判为普通函数,导致调用关系混乱。
3.3 固件文件格式:BIN、HEX、ELF、UBoot镜像
3.3.1 BIN(原始二进制)
BIN文件是纯二进制数据,无任何元数据,必须知道加载地址才能正确反汇编。它通常由objcopy -O binary生成,或直接从Flash中dd提取。BIN文件的优点是体积最小,缺点是缺乏符号信息和段边界。逆向BIN时,需通过向量表、字符串引用或已知外设寄存器地址来推断基址。
3.3.2 Intel HEX(HEX)
HEX文件是ASCII文本格式,每行以冒号开头,包含长度、地址、类型、数据和校验和。类型包括:00(数据)、01(文件结束)、02(扩展段地址)、04(扩展线性地址)。HEX文件适合烧录器,但逆向时需先转换为BIN。使用objcopy -I ihex -O binary firmware.hex firmware.bin即可转换。
3.3.3 ELF(可执行与链接格式)
ELF是Linux/Unix系统及嵌入式交叉编译的标准格式,包含ELF头、程序头表(段)和节头表(节)。关键段包括:.text(代码)、.rodata(只读数据)、.data(已初始化数据)、.bss(零初始化数据)。ELF保留了符号表和重定位信息,是逆向分析的最佳格式。使用readelf -a可查看所有节,objdump -d可反汇编。
3.3.4 UBoot镜像(uImage / FIT)
U-Boot镜像在BIN/ELF基础上增加了64字节的头(对于uImage)或FIT(Flattened Image Tree)结构。uImage头包含:镜像类型(内核/ramdisk)、加载地址、入口点、CRC32校验值。FIT是设备树源(DTS)描述的镜像集合,支持多配置和数字签名。解析uImage使用mkimage -l uImage,解析FIT使用dumpimage -T flat_dt -l fit.itb。
| 格式 | 元数据 | 加载地址 | 符号信息 | 逆向工具 |
|---|---|---|---|---|
| BIN | 无 | 需手动指定 | 无 | IDA / Ghidra(手动加载) |
| HEX | 地址记录 | 内嵌 | 无 | objcopy / 十六进制编辑器 |
| ELF | 节/段表 | 程序头指定 | 有(.symtab) | readelf / objdump / Ghidra |
| uImage | 64字节头 | 头内指定 | 无 | mkimage / dumpimage |
案例拆解:从ELF到BIN的完整转换与逆向
某智能门锁固件(基于STM32F407)提供firmware.elf,使用readelf -h查看入口点0x08000135,readelf -S发现.text起始0x08000000,.data加载地址0x20000000。使用objcopy -O binary -R .bss firmware.elf firmware.bin生成纯代码段BIN。在Ghidra中加载BIN,设置基址0x08000000,自动分析后定位到main函数(通过向量表Reset_Handler)。进一步交叉引用,发现OTA升级函数ota_update,其中包含对固件签名的RSA验证逻辑。
arm-none-eabi-objcopy -I ihex -O binary input.hex output.bin。
3.4 固件格式识别与转换实战
操作流程
- 文件类型探测:使用
file firmware.bin查看文件类型;使用binwalk firmware.bin扫描嵌入的文件系统。 - 头部特征匹配:检查前64字节是否包含uImage头(
27 05 19 56魔数)或FIT结构(d0 0d fe ed)。 - 地址推断:若为BIN,搜索ARM/Thumb指令特征(如
00 00 00 00后的跳转指令),或查找中断向量表(Cortex-M的SP和PC初始值)。 - 格式转换:使用
objcopy在ELF/HEX/BIN之间互转;使用mkimage生成或解包uImage。 - 加载到反汇编器:在IDA/Ghidra中手动指定处理器类型(ARM/Thumb/MIPS)和基址,开始分析。
常见误区
- 忽略字节序:ARM Cortex-M默认小端,但部分MIPS/PPC平台使用大端。错误设置字节序会导致所有指令解码失败。
- 混淆加载地址与链接地址:加载地址是镜像在Flash中的位置,链接地址是运行时地址。对于位置无关代码(PIC),两者可能不同。
- 未处理压缩段:部分固件使用LZMA/gzip压缩内核或文件系统,需先解压再分析。使用
binwalk -e自动解压。