3. 固件静态分析基础
固件静态分析是在不运行目标程序的前提下,从固件镜像中提取结构、语义与安全线索的过程。它之所以是固件安全扫描的第一道工序,是因为嵌入式设备往往无法方便地运行调试器,而静态分析可以先把“这是什么、由什么组成、哪里可疑”回答清楚,再决定是否进入动态验证。本节围绕五个相互衔接的分析动作展开:二进制文件结构解析、CPU 架构识别、字符串提取、熵分析、已知漏洞模式匹配。
一、二进制文件结构:先看清容器与段布局
固件镜像通常不是单一格式,而是“容器套容器”。常见形态包括:厂商打包的完整升级镜像(含头部、校验、多个分区)、U-Boot 的 uImage/FIT、SquashFS/JFFS2/CramFS 文件系统、以及内部的 ELF 可执行文件与共享库。静态分析第一步是用 file、binwalk、hexdump 观察魔数与偏移,把嵌套结构拆开。
$ file firmware.bin
firmware.bin: data
$ binwalk firmware.bin
DECIMAL HEXADECIMAL DESCRIPTION
0 0x0 uImage header, size: 3145728 bytes
64 0x40 LZMA compressed data
1048576 0x100000 SquashFS filesystem, little endian, version 4.0
3145728 0x300000 JFFS2 filesystem, little endian
对 ELF 文件,重点看 ELF Header 中的 e_machine、e_type、e_entry,以及 Program Header 的加载地址与 Section Header 的节区分布。加载地址(如 0x400000 或 0x8000)决定了后续反汇编时的基址,若填错,交叉引用与字符串地址都会错位。段权限(R/RX/RW)则直接暴露是否存在可写可执行段,这是缓冲区溢出利用链的关键前提。
二、架构识别:决定反汇编与仿真工具链
架构识别错误会让后续所有分析失效。识别依据按可靠性排序为:ELF 头 e_machine 字段(最可靠)→ 指令字节模式 → 字符串中的工具链线索 → 内核/引导日志。常见取值:ARM 为 0x28,AArch64 为 0xB7,MIPS 为 0x08,x86-64 为 0x3E。同时要判断端序(EI_DATA:1 为小端,2 为大端),MIPS 设备中大小端都常见。
识别结果直接映射到工具:ARM 用 arm-linux-gnueabi-objdump,MIPS 用 mipsel-linux-gnu-objdump,AArch64 用 aarch64-linux-gnu-objdump;仿真时 QEMU 需选择 qemu-arm、qemu-mipsel 等对应版本。若架构识别为 ARM 但实际是 Thumb 混合指令,反汇编会出现大量无意义指令,此时需在函数入口处切换指令集模式。
三、字符串提取:低成本高收益的线索来源
字符串提取用 strings -a -n 6 扫描可打印序列,是发现硬编码凭据、URL、命令、路径、版本号最快的手段。关键不是“提取”,而是“筛选与定位”。建议按类别过滤:
- 凭据类:
password、passwd、secret、token、api_key; - 网络类:
http://、https://、telnet、ftp、IP 与端口; - 命令类:
/bin/sh、system(、popen、wget、curl; - 路径与配置:
/etc/、/tmp/、.conf、nvram; - 版本与构建:
BusyBox v、OpenSSL、gcc、buildroot。
提取后必须回填地址:用 strings -t x 得到文件偏移,再结合段映射换算为虚拟地址,才能在反汇编中定位引用该字符串的代码,判断它是被 strcmp 比较、被 system 执行,还是仅作日志输出。这一步把“字符串列表”变成“可利用线索”。
四、熵分析:定位压缩、加密与隐藏数据
熵衡量数据的随机程度,取值 0~8 bit/byte。普通代码与文本熵约 4~6,压缩或加密数据接近 7.9~8.0。用滑动窗口计算熵并绘制曲线,可以快速识别固件中哪些区域是压缩文件系统、哪些是加密固件、哪些是填充或未使用区域。典型判断:
| 熵区间 | 可能内容 | 处理建议 |
|---|---|---|
| 0.0~1.0 | 全 0x00/0xFF 填充 | 跳过,记录偏移 |
| 3.5~6.0 | 代码、文本、配置 | 反汇编、字符串分析 |
| 6.0~7.5 | 压缩数据、部分加密 | 尝试解压、识别算法 |
| 7.5~8.0 | 高熵加密或已压缩 | 寻找密钥、判断是否可解 |
若整段固件熵持续接近 8.0 且无已知文件系统魔数,通常意味着厂商做了全镜像加密,此时静态分析会受阻,需要转向密钥提取或动态侧信道。反之,若某段高熵区域前后出现 LZMA/zlib 魔数,则可直接解压还原。
五、已知漏洞模式匹配:把线索变成结论
模式匹配是把前述结构、字符串、熵结果与已知漏洞特征对齐。常见模式包括:危险函数调用(strcpy、sprintf、system)、命令注入拼接、硬编码密钥、弱口令哈希、过期组件版本(如 OpenSSL 1.0.1、BusyBox 1.19)、调试接口残留(telnetd、gdbserver)。匹配可用 YARA 规则、Ghidra/IDA 脚本或自建正则库。
rule hardcoded_credential {
strings:
$a = "admin" nocase
$b = "password" nocase
condition:
$a and $b
}
匹配结果必须结合上下文判定:字符串 password 出现在配置模板中可能只是字段名,出现在 strcmp 参数中才是真实校验逻辑。因此模式匹配的输出应包含文件、偏移、虚拟地址、引用函数与置信度,供人工复核。
把五步串起来看,静态分析的价值在于“用最低成本建立可验证的假设”。结构解析给出地图,架构识别给出语言,字符串与熵给出可疑区域,模式匹配给出候选漏洞。最终输出应是一份带偏移、地址、证据与置信度的清单,而不是零散命令结果。只有这些线索被明确记录,后续的动态验证才能有针对性地复现与确认。