静态分析基础:字符串分析、二进制文件格式识别、控制流图生成

各位同学好,我是老周。今天咱们聊聊固件漏洞分析的第一步——静态分析。说白了,就是不动手运行程序,光靠看代码和二进制文件来摸清它的底细。我做了这么多年漏洞挖掘,发现很多新手上来就开调试器,结果被复杂的执行流绕晕了。其实,静态分析做扎实了,后面能省一大半力气。

1. 字符串分析——最直接的线索

你想想看,一个固件里藏着多少字符串?错误提示、版本号、路径信息、调试日志……这些都是宝贝。我习惯拿到一个二进制文件后,第一件事就是跑 strings 命令。

# 基本用法
strings firmware.bin

# 指定最小长度,过滤掉太短的噪音
strings -n 8 firmware.bin

# 输出偏移地址,方便定位
strings -t x firmware.bin

嗯,这里要注意:strings 默认只扫描 ASCII 字符串。遇到 UTF-16 编码的固件(比如某些 Windows 驱动),你得加个 -e l 参数。

我的小技巧: 把 strings 输出重定向到文件,然后用 grep 过滤关键词。比如搜 "password"、"key"、"debug"、"error"。我曾经在一个路由器固件里搜到硬编码的后门账号,就是靠这招。

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)
避坑指南: 我曾经遇到一个固件,file 命令显示是 "data",但用十六进制编辑器一看,其实是 ELF 文件被截断了头部。这时候别放弃,手动补上 ELF 头的前 16 字节,往往就能正常解析了。

3. 交叉引用分析——谁调用了谁?

静态分析的核心之一,就是搞清楚函数之间的调用关系。你看到一个危险函数(比如 strcpysprintf),得知道是谁调了它,参数从哪里来。

在 IDA Pro 或 Ghidra 里,交叉引用(Xref)是最常用的功能。我个人的习惯是:

  • 先看 代码交叉引用:哪个函数调用了当前函数?
  • 再看 数据交叉引用:哪些代码引用了这个全局变量或字符串?

举个例子,你在固件里发现一个字符串 "admin:admin",右键查看交叉引用,发现它被 check_login() 函数引用。再往上追,check_login() 又被 http_handler() 调用。你看,一条攻击链路就出来了。

核心思路: 交叉引用分析的本质是构建调用图。你不需要一开始就看完所有函数,先关注那些处理用户输入、操作内存、执行系统命令的关键函数。

4. 控制流图生成——看清程序的骨架

控制流图(CFG)把函数内部的执行路径画出来。每个基本块(Basic Block)是一段顺序执行的代码,箭头表示跳转关系。我刚开始学逆向时,对着反汇编代码一头雾水,直到学会看 CFG,才豁然开朗。

下面是我用 SVG 画的一个简化版控制流图示例,展示了一个典型函数的结构:

函数入口 基本块 A if (x) 基本块 B 基本块 C 汇合点

你看,有了 CFG,你一眼就能看出:函数入口后执行基本块 A,然后根据条件分支到 B 或 C,最后在汇合点收尾。漏洞往往就藏在那些复杂的分支路径里——比如某个分支忘记检查缓冲区长度。

工具推荐: IDA Pro 的 CFG 视图按空格键切换;Ghidra 的 Function Graph 也很直观。我习惯先用 Ghidra 生成 CFG,然后导出为 SVG 做笔记。

5. 实战思路——把这些串起来

好了,上面四个技术点讲完了。在实际项目中,我是这么用的:

  1. 第一步:filestrings 快速扫描,确认文件格式,收集敏感字符串。
  2. 第二步: 加载到反汇编工具,识别入口点和 main 函数。
  3. 第三步: 从危险函数(如 memcpysystem)出发,做交叉引用分析,反向追踪调用链。
  4. 第四步: 对关键函数生成 CFG,分析每个分支的输入验证和边界检查。

举个例子,我之前分析一个 IoT 固件,先用 strings 发现了一个 /etc/shadow 的路径。交叉引用一查,有个函数直接把这个路径拼接到 fopen() 的参数里。再生成 CFG 一看,好家伙,路径拼接前根本没做过滤。这就是一个典型的路径遍历漏洞。

注意: 静态分析不是万能的。遇到混淆代码、动态加载、自修改代码,静态分析可能会漏掉关键路径。这时候就得结合动态调试了。但话说回来,80% 的漏洞在静态分析阶段就能发现。

嗯,今天就先聊到这儿。静态分析是基本功,练好了后面学动态分析、补丁对比都会轻松很多。记住,拿到固件别急着跑,先静下心来看一看、搜一搜、画一画。

专注资料整理