静态分析基础:字符串分析、二进制文件格式识别、控制流图生成
各位同学好,我是老周。今天咱们聊聊固件漏洞分析的第一步——静态分析。说白了,就是不动手运行程序,光靠看代码和二进制文件来摸清它的底细。我做了这么多年漏洞挖掘,发现很多新手上来就开调试器,结果被复杂的执行流绕晕了。其实,静态分析做扎实了,后面能省一大半力气。
1. 字符串分析——最直接的线索
你想想看,一个固件里藏着多少字符串?错误提示、版本号、路径信息、调试日志……这些都是宝贝。我习惯拿到一个二进制文件后,第一件事就是跑 strings 命令。
# 基本用法
strings firmware.bin
# 指定最小长度,过滤掉太短的噪音
strings -n 8 firmware.bin
# 输出偏移地址,方便定位
strings -t x firmware.bin
嗯,这里要注意:strings 默认只扫描 ASCII 字符串。遇到 UTF-16 编码的固件(比如某些 Windows 驱动),你得加个 -e l 参数。
2. 二进制文件格式识别——ELF 还是 PE?
拿到一个文件,你得先搞清楚它是什么格式。Linux 下用 ELF,Windows 下用 PE,嵌入式设备还有各种魔改格式。我见过有人把 raw binary 当成 ELF 去分析,折腾半天才发现文件头都不对。
怎么识别?看文件头的前几个字节:
| 格式 | 魔数(Magic Number) | 常见后缀 |
|---|---|---|
| ELF | 0x7f 'E' 'L' 'F' |
.elf, .so, .o |
| PE | 'M' 'Z' |
.exe, .dll, .sys |
| Mach-O | 0xfe 0xed 0xfa 0xce |
.dylib, .o |
| Raw Binary | 无固定魔数 | .bin, .rom |
用 file 命令最省事:
file firmware.bin
# 输出示例:ELF 32-bit LSB executable, ARM, version 1 (SYSV)
3. 交叉引用分析——谁调用了谁?
静态分析的核心之一,就是搞清楚函数之间的调用关系。你看到一个危险函数(比如 strcpy、sprintf),得知道是谁调了它,参数从哪里来。
在 IDA Pro 或 Ghidra 里,交叉引用(Xref)是最常用的功能。我个人的习惯是:
- 先看 代码交叉引用:哪个函数调用了当前函数?
- 再看 数据交叉引用:哪些代码引用了这个全局变量或字符串?
举个例子,你在固件里发现一个字符串 "admin:admin",右键查看交叉引用,发现它被 check_login() 函数引用。再往上追,check_login() 又被 http_handler() 调用。你看,一条攻击链路就出来了。
4. 控制流图生成——看清程序的骨架
控制流图(CFG)把函数内部的执行路径画出来。每个基本块(Basic Block)是一段顺序执行的代码,箭头表示跳转关系。我刚开始学逆向时,对着反汇编代码一头雾水,直到学会看 CFG,才豁然开朗。
下面是我用 SVG 画的一个简化版控制流图示例,展示了一个典型函数的结构:
你看,有了 CFG,你一眼就能看出:函数入口后执行基本块 A,然后根据条件分支到 B 或 C,最后在汇合点收尾。漏洞往往就藏在那些复杂的分支路径里——比如某个分支忘记检查缓冲区长度。
5. 实战思路——把这些串起来
好了,上面四个技术点讲完了。在实际项目中,我是这么用的:
- 第一步: 用
file和strings快速扫描,确认文件格式,收集敏感字符串。 - 第二步: 加载到反汇编工具,识别入口点和 main 函数。
- 第三步: 从危险函数(如
memcpy、system)出发,做交叉引用分析,反向追踪调用链。 - 第四步: 对关键函数生成 CFG,分析每个分支的输入验证和边界检查。
举个例子,我之前分析一个 IoT 固件,先用 strings 发现了一个 /etc/shadow 的路径。交叉引用一查,有个函数直接把这个路径拼接到 fopen() 的参数里。再生成 CFG 一看,好家伙,路径拼接前根本没做过滤。这就是一个典型的路径遍历漏洞。
嗯,今天就先聊到这儿。静态分析是基本功,练好了后面学动态分析、补丁对比都会轻松很多。记住,拿到固件别急着跑,先静下心来看一看、搜一搜、画一画。