4、固件二进制分析:Bin/Hex/ELF格式解析、固件加载地址识别、入口点定位
拿到一个固件文件,第一件事是什么?
别急着上反汇编器。你得先搞清楚手里拿的是什么格式。Bin、Hex、ELF,这三种格式我几乎天天打交道。它们长得不一样,解析方式也完全不同。搞错了,后面全是白费功夫。
4.1 三种固件格式的本质差异
先看一张对比表,心里有个底:
| 格式 | 是否包含地址信息 | 是否分段 | 典型场景 | 解析难度 |
|---|---|---|---|---|
| Bin | 否 | 否 | 量产烧录、OTA升级包 | 低 |
| Hex (Intel HEX) | 是(每行记录地址) | 是(按记录类型分段) | 调试器下载、IDE输出 | 中 |
| ELF | 是(完整符号表) | 是(段表+节表) | 调试、逆向分析 | 高 |
我个人习惯,拿到固件先看文件头。Bin文件最简单,就是裸二进制流,没有头、没有校验、没有地址。Hex文件开头是冒号,每行都有地址偏移。ELF文件开头是7f 45 4c 46(即.ELF),一看便知。
hexdump -C firmware.bin | head -20看一眼前几行,基本就能判断格式。如果是纯机器码,没有ASCII字符,大概率是Bin。如果看到大量冒号开头的行,那就是Hex。
4.2 Bin文件:裸数据,地址全靠猜
Bin文件最让人头疼。它没有地址信息,你得自己猜加载地址。
我在项目中遇到过好几次,客户只给了个Bin文件,连芯片型号都没说。怎么办?
- 看中断向量表:STM32的启动代码,前4字节是栈顶指针,接着4字节是复位向量。复位向量的值,往往能反推出加载基址。
- 找常见模式:比如
0x08000000开头的地址,基本就是Flash基址。 - 试错法:用IDA或Ghidra,分别尝试
0x08000000、0x20000000、0x00000000,看哪个能正确解析出代码。
4.3 Hex文件:逐行解析,地址分段
Intel HEX格式,每行格式固定:
:10000000 00 08002000 080001F1 080001F5 080001F9 6A
| | | | | | | |
| 长度 地址 记录类型 数据... 校验和
冒号
记录类型有6种,但常用的就两种:
- 00(数据记录):真正的固件内容,地址+数据。
- 01(文件结束记录):表示文件结束。
- 04(扩展线性地址):用于指定高16位地址,配合数据记录的低16位,组成32位地址。
解析时,我建议按以下步骤:
- 逐行读取,跳过空行。
- 校验每行的校验和。如果校验失败,说明文件损坏。
- 遇到04记录,更新当前高地址段。
- 遇到00记录,将数据写入对应地址。
- 遇到01记录,停止解析。
0x08000000,但Hex文件里可能从0x00000000开始。你需要根据芯片手册,做地址偏移。
4.4 ELF文件:段与节,信息最丰富
ELF文件是逆向分析的最爱。它包含了符号表、段信息、重定位表,甚至调试信息。
ELF的结构分两层:
- 链接视图(节表):用于编译链接阶段,包含
.text、.data、.bss等节。 - 执行视图(段表):用于加载运行阶段,包含
LOAD、DYNAMIC等段。
我一般用readelf工具快速查看:
# 查看ELF文件头
readelf -h firmware.elf
# 查看段表(执行视图)
readelf -l firmware.elf
# 查看节表(链接视图)
readelf -S firmware.elf
# 查看符号表
readelf -s firmware.elf
对于STM32固件,重点关注LOAD段。它告诉固件应该加载到哪个地址。比如:
LOAD 0x08000000 0x08000000 0x00010000
LOAD 0x20000000 0x20000000 0x00002000
第一行:Flash区,加载地址0x08000000,大小0x10000。第二行:RAM区,加载地址0x20000000,大小0x2000。
4.5 固件加载地址识别
加载地址错了,反汇编出来的代码全是错的。怎么确认?
我总结了三个方法:
- 向量表法:STM32的复位向量,通常指向
Reset_Handler。如果这个地址在0x08000000附近,那基址就是0x08000000。 - 字符串搜索法:在固件中搜索常见字符串,比如
STM32、HAL、Error。这些字符串的地址,能帮你反推基址。 - 交叉引用法:用IDA加载后,看函数调用关系。如果大部分函数地址都在某个范围内,那这个范围就是代码段。
4.6 入口点定位
入口点,就是CPU上电后执行的第一条指令。对于STM32,入口点就是复位向量指向的地址。
定位步骤:
- 找到向量表基址(通常是
0x08000000)。 - 读取偏移
0x04处的4字节,这就是复位向量。 - 复位向量的值,就是入口点地址。
举个例子:
地址: 0x08000000 数据: 0x20001000 (栈顶指针)
地址: 0x08000004 数据: 0x080001F1 (复位向量 → 入口点)
入口点就是0x080001F1。注意,这个地址通常是奇数(Thumb模式),实际执行地址是0x080001F0。
4.7 知识体系总览
下面这张图,概括了本章的核心逻辑:
说白了,固件分析的第一步就是搞清楚格式、地址、入口。这三件事搞定了,后面反汇编、逻辑还原才有基础。你想想看,如果连代码从哪开始执行都不知道,那分析还有什么意义?
嗯,这一章的内容就到这里。记住,拿到固件先看格式,再看地址,最后找入口。三步走,稳得很。