← Paper Digest
阅读笔记

Driller:Fuzzing 与符号执行如何互补

Fuzzing 负责快速探索,选择性符号执行帮助跨过限制性条件,再将新种子交回 fuzzing。Driller 给我的核心启发,是如何让两种方法通过执行反馈形成有效协作。

FuzzingSymbolic ExecutionSoftware Testing
阅读原论文 ↗

在准备 ENGG5505 的 Driller 论文报告时,我最想保留的启发是:fuzzing 与 symbolic method 可以围绕各自擅长的问题分工,通过具体输入和执行反馈反复协作。

Driller 中使用的符号方法,具体是选择性的 concolic execution(具体执行与符号执行结合)。Fuzzing 承担大部分探索;当探索停滞时,符号执行尝试生成能跨过关键检查的输入;这些输入又成为 fuzzing 的新起点。

论文信息

Driller: Augmenting Fuzzing Through Selective Symbolic Execution

Nick Stephens、John Grosen、Christopher Salls、Andrew Dutcher、Ruoyu Wang、Jacopo Corbetta、Yan Shoshitaishvili、Christopher Kruegel、Giovanni Vigna

NDSS 2016 · DOI: 10.14722/ndss.2016.23368

这篇笔记基于我的课程报告材料整理。下文将论文的方法与实验结果,以及我从中得到的研究启发分别展开。

为什么需要把两种方法结合起来

Coverage-guided fuzzing 的基本循环是:选择种子、变异输入、执行程序,再根据覆盖反馈保留有价值的输入。AFL 可以快速进行大量测试,但某些检查要求输入满足非常精确的条件。大量不同的错误输入可能都走向同一条拒绝分支,覆盖反馈无法告诉它应该往哪个方向修改。

报告中使用了论文的配置解析器示例。程序先解析配置,再检查 magic 是否等于一个常量;通过之后,才会进入初始化和命令分派。即使已经生成了语法合法的配置,仍可能一直卡在 magic 检查之前。

假设一个示例字段有 32 位,而且每次猜测都独立、均匀,那么命中指定值的概率是 1 / 2^32,平均需要约 43 亿次尝试。这只是解释精确匹配困难的概率示例,并非 AFL 的实测命中率,也不是论文解析器字段宽度的断言。

符号执行可以把输入表示为变量,沿程序执行记录约束,再求解满足目标条件的输入。例如,进入初始化需要满足 m == MAGIC_NUMBER。但对整个程序持续展开符号路径,又会遇到路径爆炸和求解开销。

两种方法因此具有互补性:大量快速执行适合探索宽广的输入空间,约束求解适合处理某些难以靠变异命中的条件。

Driller 如何交接探索任务

Driller 协作流程:AFL 变异与执行;进展停滞后选择性调用 concolic execution;求解成功的新输入返回 AFL

课程报告中的方法概览图,根据 Driller 第 IV–V 节整理。点击图片可查看大图。

Driller 用 AFL 进行覆盖引导的 fuzzing,并用 angr 执行选择性的 concolic 分析。一次交接可以理解为以下过程:

  1. 先让 AFL 探索。 运行并变异已有输入,保存能够增加覆盖的种子。
  2. 根据进展决定是否请求帮助。 在与输入长度相关的变异预算内没有出现新的状态转移时,触发符号分析。这是一种启发式信号,并不能证明程序中一定存在难解条件。
  3. 沿已有种子的轨迹分析。 从 AFL 队列中选择有价值的输入,回放具体路径,同时记录符号约束,并考虑 fuzzing 尚未覆盖的转移。
  4. 生成新的具体输入。 若替代分支的约束可满足,就把解转成种子,交回 AFL,在新区域继续变异和执行。

以 magic 检查为例,假设已有输入通过了解析,但 magic = 0 导致拒绝。令 P 表示到达这条检查前的路径条件,目标查询可以写成:

目标约束: P ∧ (m == MAGIC_NUMBER)

这里有一个重要细节:回放时用于固定原输入字节的临时等式约束,需要在求替代输入时解除;到达目标分支所需的前序路径条件则要保留。否则,输入会被原来的具体值锁住,无法跨到另一条分支。求解器也可能修改其他字节,只要新的输入仍满足所保留的路径前缀。

通过 magic 检查后,还可能遇到命令字符串比较等新条件。交接会继续发生:求解打开新的可达区域,fuzzing 再探索其中的更多行为。论文用 compartments 描述被限制性检查分隔的行为区域;它是理解分工的概念模型,实际决策依赖覆盖信息和执行轨迹。

“Selective” 对成本的控制

组合方法的价值很大程度上取决于何时、对哪里使用符号执行。Driller 对这些选择施加了限制:

决策 做法
何时调用 在变异预算内没有发现新的转移时触发
分析哪些输入 使用 AFL 队列中的有价值种子
尝试哪些分支 关注 fuzzer 尚未覆盖的转移
向外探索多远 对新转移附近的符号探索设置基本块数量限制

这些策略减少了不必要的符号工作,但不会消除困难约束、状态空间膨胀或分析失败。对一个混合测试系统来说,触发频率、目标选择和预算分配本身就值得研究。

实验说明了什么

论文在 DARPA Cyber Grand Challenge 的 126 个单二进制应用上进行评估。每个目标最多运行 24 小时,发现崩溃则提前停止,并使用 CGC 工具回放验证。下表的单位是发现并验证了崩溃的应用数,不能直接理解为不同漏洞根因的数量。

方法或集合 应用数
AFL 基线 68
符号执行基线 16
两个基线的交集 13
两个基线结果的并集 71
Driller 77

并集是 68 + 16 − 13 = 71。Driller 比 AFL 多覆盖了 9 个有崩溃的应用,比两个独立基线结果的并集多了 6 个。这个结果支持了“交换种子、交替探索能带来额外收益”的判断。数据见原论文第 VI 节与 Figure 5。

同时,Driller 为每个目标配置 4 个 AFL 核心,并另外使用一个共享的 64 核 concolic 执行池;单条 trace 有 1 小时和 4 GB 的限制。因此,相同的运行时间上限不等于相同的总 CPU 预算。这些结果也不能直接推导出它在所有生产软件上的表现。

我的主要启发:让快速探索与约束推理形成闭环

1. 以具体输入作为两种方法的协作接口

Fuzzing 能提供已经到达某个位置的输入,以及这次执行留下的轨迹和覆盖信息。符号分析据此构造一个更有针对性的求解任务,再返回可以直接执行的新输入。

这种接口很有价值:输入本身可以运行、复现和继续变异,帮助两种方法持续交换探索成果。对后续测试系统的设计,我会优先考虑如何保存和复用这些中间成果。

2. 把昂贵分析用在可能改变可达性的地方

当目标程序包含配置约束、格式检查或参数依赖时,可以先用低成本测试收集反馈,再考虑对持续阻塞探索的条件做局部建模与求解。

这是我希望进一步验证的方向:用 fuzzing 找到值得分析的位置,用 symbolic method 生成跨过条件的新输入,再让 fuzzing 放大这次求解带来的收益。 这里仍需判断哪些条件适合建模、如何选择路径,以及何时应当放弃求解。它是一种研究思路,尚不是我已经完成的实验结果。

3. 评价一次求解,应当看后续探索收益

求解成功率只能描述其中一个环节。更值得观察的是:新种子是否带来新的覆盖、后续变异是否继续发现行为,以及最终是否触发可复现的问题。

如果继续做实验,我会比较纯 fuzzing、独立运行两种方法、以及交替反馈的混合方案。在相同总 CPU 预算下进行多次重复运行,同时记录覆盖随时间的变化、经验证的问题数量、求解耗时和新种子的后续贡献。这些是待开展的验证设计。

4. 保留具体执行的验证环节

求解器回答的是所建模约束是否可满足。生成输入之后,仍需要在目标程序中执行,检查它实际到达了哪里。发现崩溃以后,还需要回放、去重和定位,才能理解问题本身。

这也是我从 Driller 中最想沿用的工作方式:快速探索提供线索,约束推理产生候选输入,实际执行验证收益,然后继续探索。

资料来源

  • Stephens et al. Driller: Augmenting Fuzzing Through Selective Symbolic Execution. NDSS 2016. 论文 PDF。方法对应第 III–V 节,实验对应第 VI 节。
  • 我的 ENGG5505 Driller 英文课程报告与讲稿,整理于 2026 年 10 月。文中流程图来自报告材料;“我的主要启发”部分为个人阅读思考与待验证方向。
← 返回 Paper Digest