内核漏洞挖掘技术系列(4)——syzkaller(2)
字数 2409 2025-08-05 08:16:32
Syzkaller 内核漏洞挖掘技术深入解析
一、系统调用模板语法
Syzkaller 使用一种特定的系统调用模板语言来描述系统调用接口,基本语法结构如下:
syscallname "(" [arg ["," arg]*] ")"
arg = argname type
argname = identifier
type = typename [ "[" type-options "]" ]
typename = "const" | "intN" | "intptr" | "flags" | "array" | "ptr" |
"buffer" | "string" | "strconst" | "filename" | "len" |
"bytesize" | "bytesizeN" | "bitsize" | "vma" | "proc"
type-options = [type-opt ["," type-opt]*]
二、编译流程概述
系统调用模板的编译过程分为两个主要阶段:
- syz-extract:从模板中提取常量并生成
.const文件 - syz-sysgen:将模板和常量编译为 Syzkaller 使用的代码
syz-sysgen 的四个关键步骤:
assignSyscallNumbers:分配系统调用号,过滤不支持的调用patchConsts:将 AST 中的常量替换为实际值check:对 AST 进行语义检查genSyscalls:从 AST 生成 prog 对象
三、syz-extract 详细解析
1. 主要处理流程
- 调用
archFileList获取系统调用模板文件和架构配置 - 调用对应 extractor 的
prepare函数 - 分别对架构和文件调用
processArch和processFile
2. 关键函数分析
processArch 函数:
- 调用
ParseGlob→Parse→parseTopRecover→parseTop - 解析模板文件为 AST
- 调用
ExtractConsts→Compile提取常量标识符
Compile 函数:
- 调用
createCompiler初始化内置别名和模板 - 调用
typecheck进行多项检查:checkDirectivescheckNamescheckFieldscheckTypedefscheckTypes
- 调用
extractConsts提取常量信息
extractConsts 函数:
提取以下内容:
- 常量 (consts)
- 定义 (defines)
- 包含头文件数组 (includeArray)
- 包含目录数组 (incdirArray)
具体提取逻辑:
- 提取包含目录和头文件
- 将定义名称作为常量
- 为系统调用名添加前缀(Linux 是
__NR_) - 提取 call、struct、resource 等类型参数中的常量
- 提取 struct 类型 size 中的常量
- 提取 flag 和 resource 中的常量
processFile 函数:
- 输入文件如
sys/linux/binder.txt - 输出文件如
sys/linux/binder_amd64.const - 调用
extract→compile生成常量值
compile 函数:
- 使用
srcTemplate模板将数据解析为源代码 - 编译为二进制文件
- 处理可能的错误(某些架构未定义的常量)
特殊处理:
- i386/arm 架构上的
mmap转换为old_mmap,修复为mmap2 - 最终调用
SerializeConsts序列化为常量=值格式写入文件
四、syz-sysgen 详细解析
1. 主要处理流程
- 创建
gen文件夹存放生成的 go 文件 - 从
.txt文件提取 AST - 从
.const文件提取常量 - 调用
Compile函数(与 syz-extract 中的调用不同)
2. 关键函数分析
Compile 函数(完整流程):
createCompiler:初始化编译器typecheck:类型检查assignSyscallNumbers:分配系统调用号patchConsts:替换常量实际值check:语义检查genSyscalls:生成 prog 对象
genSyscalls 函数:
- 调用
genSyscall生成单个系统调用 - 按系统调用名排序
genSyscall调用genType生成返回值,genFieldArray生成参数- 调用
genResources生成资源 - 调用
genStructDescs生成结构体描述
输出处理:
generate函数将结果输出到io.Writer- 写入
gen文件夹下对应架构的.go文件 - 自动执行
init函数中的RegisterTarget writeExecutorSyscalls写入配置:executor/defs.h:配置信息executor/syscalls.h:系统调用名和编号
五、关键实现细节
1. 模板解析过程
解析流程:ParseGlob → Parse → parseTopRecover → parseTop
parseTop 函数根据标识符类型调用不同处理函数:
%开头的指令include包含指令define定义指令resource资源定义call系统调用定义struct结构体定义flags标志定义
2. 常量提取策略
提取的常量类型包括:
- 系统调用编号(自动添加
__NR_前缀) - 结构体大小和字段偏移
- 标志位值
- 资源定义值
- 类型定义中的常量
3. 架构适配处理
特殊架构处理:
- 多架构编译时的常量过滤
- i386/arm 的
mmap特殊处理 - 不同架构的系统调用号差异
六、总结与后续
本文详细分析了 Syzkaller 系统调用模板的编译过程,包括:
- 模板语法结构
- syz-extract 的常量提取机制
- syz-sysgen 的代码生成流程
- 关键函数实现细节
后续研究方向:
- Fuzz 过程分析
- Crash 复现机制
- Generate 和 Mutate 策略
- 执行监控和漏洞检测
通过理解这些底层机制,可以更有效地扩展 Syzkaller 的功能,提高内核漏洞挖掘的效率。