内核漏洞挖掘技术系列(4)——syzkaller(4)
字数 2376 2025-08-05 08:18:36
Syzkaller 内核漏洞挖掘技术详解
1. Syzkaller 概述
Syzkaller 是一个开源的、无监督的、覆盖引导的内核模糊测试工具,主要用于发现操作系统内核中的漏洞。它通过系统调用序列来测试内核,能够自动生成、变异和执行测试用例,并监控内核的运行状态以发现异常。
2. Syzkaller 工作流程
2.1 初始设置
-
启动命令:
/syz-fuzzer -executor=/syz-executor -name=vm-0 -arch=amd64 -manager=10.0.2.10:33185 -procs=1 -leak=false -cover=true -sandbox=none -debug=true -v=100 -
监控机制:
MonitorExecution函数监控输出中的内核 oops 信息、连接丢失、挂起等情况
2.2 初始化阶段
-
加载语料库:
- 通过 RPC 调用
Check函数 loadCorpus函数将数据库中的语料加载到mgr.candidates中
- 通过 RPC 调用
-
轮询机制:
pollLoop→poll→ RPC 调用Poll函数candidateBatch函数获取mgr.candidates并加入fuzzer的workQueue
-
优先级计算:
CalculatePriorities函数计算系统调用间的优先级BuildChoiceTable函数基于优先级构建选择表
3. 优先级计算机制
3.1 静态优先级 (calcStaticPriorities)
-
资源使用分析:
- 创建哈希表记录每种资源的使用情况
- 资源类型包括:Vma、Ptr、Buffer 等
- 权重分配:
- Vma: 0.5
- Ptr: 1.0
-
计算规则:
- 对于同一种资源,将系统调用 A 的权重 × 系统调用 B 的权重
- 加到系统调用 A 和 B 的优先级中
- 当 A = B 时,prios 设置为 A 与其他所有系统调用 prios 的最大值
- 最后调用
normalizePrio规范化
3.2 动态优先级 (calcDynamicPrio)
- 计算规则:
- 如果语料库中一对系统调用经常一起出现,则 prios 加 1
- 同样调用
normalizePrio规范化
3.3 选择表构建 (BuildChoiceTable)
-
构建规则:
- 对于系统调用 i 和 j:
run[i][j]= 之前run[i][x](x < j) 的和 +prios[i][j]× 1000
- 结果是一个按权重排序的表
- 对于系统调用 i 和 j:
-
选择函数 (
Choose):- 根据 run 表随机选择系统调用
- 表仅提供有限权重指导
4. Fuzz 循环 (loop 函数)
4.1 工作队列 (WorkQueue)
包含三类对象:
WorkTriage- 可能有新覆盖率的待确认程序WorkSmash- 新添加到语料库的程序WorkCandidate- 来自 hub 的待评估程序
4.2 处理函数
-
triageInput函数:- 确认程序是否提供新覆盖率
- 如果是,则最小化并添加到语料库
-
smashInput函数:- 如果启用
comparisonTracingEnabled,调用executeHintSeed - 使用 hint 机制优化变异
- 如果启用
-
execute函数:- 处理
WorkCandidate类型的程序 - 与本地生成的程序处理方式相同
- 处理
5. Hint 机制详解
5.1 基本原理
-
Hint:
- 由指向程序系统调用参数的指针和应赋给该参数的值组成
- 称为 "replacer"
-
工作流程:
- 启动程序 (hint seed)
- 为每个系统调用收集所有比较数据
- 尝试匹配比较操作数值和输入参数值
- 用匹配值替换指定参数
- 检查新程序是否获得新覆盖率
-
数据收集:
- 使用 kcov 的
KCOV_MODE_TRACE_CMP模式
- 使用 kcov 的
5.2 关键函数
-
executeHintSeed:- 先执行原始程序
- 在
MutateWithHints中用比较操作数匹配变异初始程序 - 检查每个变异是否提供新覆盖率
-
generateHints:- 调用
checkConstArg和checkDataArg - 使用
shrinkExpand得到 replacer 替换原值
- 调用
5.3 示例分析
-
示例 1:
void f(u64 qw) { u8 b = (u8) qw; u16 w = (u16) qw; u32 dw = (u32) qw; if (b == 0xab) {...} if (w == 0xcdcd) {...} if (dw == 0xefefefef) {...} if (qw == 0x0101010101010101) {...} }; f(0x1234567890abcdef);CompMap:
CompMap { 0xef: uint64Set{0xab: true}, 0xcdef: uint64Set{0xcdcd: true}, 0x90abcdef: uint64Set{0xefefefef: true}, 0x1234567890abcdef: uint64Set{0x0101010101010101: true}, }结果:
uint64Set{ 0x1234567890abcdab: true, 0x1234567890abcdcd: true, 0x12345678efefefef: true, 0x0101010101010101: true, } -
示例 2:
void f(i32 dw) { i64 qw = (i32) dw; if (qw == -2) }; f(-1);CompMap:
CompMap{0xffffffffffffffff: uint64Set{0xfffffffffffffffe: true}}结果:
uint64Set{0xfffffffe: true}
6. Executor 执行流程
-
调用链:
proc.execute → proc.executeRaw → proc.env.Exec → env.cmd.exec -
执行前准备:
makeCommand设置 pipe 通信- 通过
osutil.Command运行 executor
-
Executor 主流程 (
executor.cc):- 重新映射输入/输出
- 根据 sandbox 设置调用相应函数
- 执行系统调用并收集覆盖率信息
-
执行链:
loop → execute_one → schedule_call → thread_create → thread_start → worker_thread → execute_call → execute_syscall
7. 改进方向 - MoonShine
7.1 基本原理
-
Distillation (蒸馏) 算法:
- 分析真实世界程序的 system-call 依赖关系
- "蒸馏"出种子程序给 syzkaller
-
优势:
- 发现更多内核漏洞
- 实际应用中发现了 17 个新的 Linux 内核漏洞
7.2 资源
-
源代码:
https://github.com/shankarapailoor/moonshine -
论文:
https://www.usenix.org/conference/usenixsecurity18/presentation/pailoor -
解读:
https://zhuanlan.zhihu.com/p/60798641
8. 关键总结
-
优先级计算:
- 结合静态资源分析和动态共现频率
- 指导系统调用的选择组合
-
Hint 机制:
- 利用比较操作数指导变异
- 有效提高路径覆盖率
-
执行流程:
- 多级调用确保安全执行
- 完善的监控机制捕获异常
-
改进空间:
- 种子选择算法优化
- 真实程序行为模拟