内核漏洞挖掘技术系列(4)——syzkaller(3)
字数 2308 2025-08-05 08:17:20

Syzkaller内核漏洞挖掘技术深入解析(3):Crash复现与最小化

一、Syzkaller配置参数详解

1.1 基本配置参数

syz-manager的配置文件中,以下参数是核心配置项:

  • http: 显示运行中syz-manager进程信息的URL
  • email_addrs: 首次出现bug时接收通知的电子邮件地址(仅支持Mailx)
  • workdir: 工作目录位置,包含以下子内容:
    • crashes/*: crash输出文件
    • corpus.db: 程序语料库
    • instance-x: 每个VM实例的临时文件
  • syzkaller: syzkaller二进制文件位置
  • kernel_obj: 包含目标文件(如vmlinux)的目录
  • procs: 每个VM中的并行测试进程数(通常4或8)
  • image: QEMU实例的磁盘镜像文件位置
  • sshkey: 用于与虚拟机通信的SSH密钥位置

1.2 沙盒模式配置

sandbox参数支持以下模式:

  • none: 默认设置,不做特殊处理
  • setuid: 冒充用户nobody(65534)
  • namespace: 使用命名空间删除权限(需内核支持相关配置)

1.3 系统调用控制

  • enable_syscalls: 指定要测试的系统调用列表
  • disable_syscalls: 指定禁用的系统调用列表
  • suppressions: 已知错误的正则表达式列表

1.4 虚拟机配置

  • type: 虚拟机类型(如qemu)
  • vm: VM类型相关参数:
    • count: 并行运行的VM数量
    • kernel: 要测试的内核bzImage文件位置
    • cmdline: 启动内核的额外命令行选项
    • cpu: VM中模拟的CPU数量
    • mem: VM内存大小(MB)

二、Syzkaller启动流程

2.1 初始化阶段

  1. 日志缓存:启用日志缓存功能,限制为1000行或1^29字节
  2. 配置加载:加载config文件,获取OS和架构信息
  3. 目标获取:通过GetTarget函数获取参数对应的target
  4. 系统调用解析:通过ParseEnabledSyscalls解析enable/disable_syscalls参数

2.2 虚拟机池创建

if config.Type != "none" {
    vmPool = vmimpl.Create(config.Type, config.VM)
}
  • type=none用于调试/开发,需手动启动VM
  • 虚拟机池(vmPool)用于创建多个独立VM
  • 通过qemu.go的Ctor函数检查参数有效性

三、Crash复现机制

3.1 复现队列处理

  • crash保存在reproQueue
  • 通过len(reproQueue) != 0判断是否有待复现crash
  • 实例分割:
    vmIndexes := append([]int{}, instances[len(instances)-instancesPerRepro:]...)
    instances = instances[:len(instances)-instancesPerRepro]
    
    • vmIndexes: 用于crash复现
    • instances: 运行新实例

3.2 复现流程(repro函数)

  1. 程序提取:调用extractProg提取触发crash的程序
  2. 时间控制:Timeouts有三个级别:
    • 10s: 简单crash复现
    • 1min: 中等复杂度crash
    • 5min: 复杂条件竞争crash
  3. 二分查找:当单个程序无法复现时,使用二分法找出触发crash的程序
  4. 复现策略:按时间从短到长、从后向前、从单个到多个的顺序尝试复现

四、程序最小化技术

4.1 最小化流程(minimizeProg函数)

  1. 调用SanitizeCall:对特殊系统调用进行处理
  2. 移除系统调用:尝试逐个移除非必要系统调用
    • 示例:移除open系统调用
  3. 参数精简:去除系统调用的无关参数

4.2 参数最小化(do函数)

根据参数类型调用不同的minimize函数:

  • 指针类型:将指针或指针指向内容置空
  • 数组类型:逐个移除数组元素

五、C程序提取与简化

5.1 C程序提取(extractC函数)

  1. 调用testCProg
    • 使用csource.Write生成C代码
    • 使用csource.Build编译可执行文件
  2. 生成独立复现程序:创建可独立运行的测试用例

5.2 程序简化(simplifyProg函数)

  1. 再次提取C程序:调用extractC
  2. 简化C程序:调用simplifyC
  3. 简化选项:优化复现crash时的设置:
    • 线程配置
    • 并发设置
    • 沙盒配置

六、关键数据结构与函数

6.1 主要数据结构

  • reproQueue: crash复现队列
  • vmPool: 虚拟机池
  • instances: VM实例集合

6.2 核心函数

函数名 功能描述
extractProg 提取触发crash的程序
minimizeProg 程序最小化主函数
SanitizeCall 特殊系统调用处理
Minimize 最小化算法实现
extractC 生成C语言测试用例
simplifyC 简化C程序配置

七、最佳实践建议

  1. 配置优化

    • 根据硬件资源合理设置procsvm.count
    • 复杂漏洞设置更长timeout
  2. 调试技巧

    • 使用debug参数输出VM日志
    • 使用bench参数记录统计信息
  3. 复现策略

    • 优先尝试最后触发的程序
    • 从简单配置开始逐步增加复杂度
  4. 最小化原则

    • 先移除系统调用,再精简参数
    • 不同类型参数采用不同最小化策略

通过本文分析的crash复现与最小化技术,Syzkaller能够高效地验证和简化发现的漏洞,为后续分析提供清晰的测试用例。下一部分将深入分析vmLoop函数的fuzz机制。

Syzkaller内核漏洞挖掘技术深入解析(3):Crash复现与最小化 一、Syzkaller配置参数详解 1.1 基本配置参数 在 syz-manager 的配置文件中,以下参数是核心配置项: http : 显示运行中syz-manager进程信息的URL email_ addrs : 首次出现bug时接收通知的电子邮件地址(仅支持Mailx) workdir : 工作目录位置,包含以下子内容: crashes/* : crash输出文件 corpus.db : 程序语料库 instance-x : 每个VM实例的临时文件 syzkaller : syzkaller二进制文件位置 kernel_ obj : 包含目标文件(如vmlinux)的目录 procs : 每个VM中的并行测试进程数(通常4或8) image : QEMU实例的磁盘镜像文件位置 sshkey : 用于与虚拟机通信的SSH密钥位置 1.2 沙盒模式配置 sandbox 参数支持以下模式: none : 默认设置,不做特殊处理 setuid : 冒充用户nobody(65534) namespace : 使用命名空间删除权限(需内核支持相关配置) 1.3 系统调用控制 enable_ syscalls : 指定要测试的系统调用列表 disable_ syscalls : 指定禁用的系统调用列表 suppressions : 已知错误的正则表达式列表 1.4 虚拟机配置 type : 虚拟机类型(如qemu) vm : VM类型相关参数: count : 并行运行的VM数量 kernel : 要测试的内核bzImage文件位置 cmdline : 启动内核的额外命令行选项 cpu : VM中模拟的CPU数量 mem : VM内存大小(MB) 二、Syzkaller启动流程 2.1 初始化阶段 日志缓存 :启用日志缓存功能,限制为1000行或1^29字节 配置加载 :加载config文件,获取OS和架构信息 目标获取 :通过 GetTarget 函数获取参数对应的target 系统调用解析 :通过 ParseEnabledSyscalls 解析enable/disable_ syscalls参数 2.2 虚拟机池创建 type=none 用于调试/开发,需手动启动VM 虚拟机池(vmPool)用于创建多个独立VM 通过qemu.go的Ctor函数检查参数有效性 三、Crash复现机制 3.1 复现队列处理 crash保存在 reproQueue 中 通过 len(reproQueue) != 0 判断是否有待复现crash 实例分割: vmIndexes : 用于crash复现 instances : 运行新实例 3.2 复现流程(repro函数) 程序提取 :调用 extractProg 提取触发crash的程序 时间控制 :Timeouts有三个级别: 10s: 简单crash复现 1min: 中等复杂度crash 5min: 复杂条件竞争crash 二分查找 :当单个程序无法复现时,使用二分法找出触发crash的程序 复现策略 :按时间从短到长、从后向前、从单个到多个的顺序尝试复现 四、程序最小化技术 4.1 最小化流程(minimizeProg函数) 调用SanitizeCall :对特殊系统调用进行处理 移除系统调用 :尝试逐个移除非必要系统调用 示例:移除open系统调用 参数精简 :去除系统调用的无关参数 4.2 参数最小化(do函数) 根据参数类型调用不同的minimize函数: 指针类型 :将指针或指针指向内容置空 数组类型 :逐个移除数组元素 五、C程序提取与简化 5.1 C程序提取(extractC函数) 调用testCProg : 使用 csource.Write 生成C代码 使用 csource.Build 编译可执行文件 生成独立复现程序 :创建可独立运行的测试用例 5.2 程序简化(simplifyProg函数) 再次提取C程序 :调用extractC 简化C程序 :调用simplifyC 简化选项 :优化复现crash时的设置: 线程配置 并发设置 沙盒配置 六、关键数据结构与函数 6.1 主要数据结构 reproQueue : crash复现队列 vmPool : 虚拟机池 instances : VM实例集合 6.2 核心函数 | 函数名 | 功能描述 | |--------|----------| | extractProg | 提取触发crash的程序 | | minimizeProg | 程序最小化主函数 | | SanitizeCall | 特殊系统调用处理 | | Minimize | 最小化算法实现 | | extractC | 生成C语言测试用例 | | simplifyC | 简化C程序配置 | 七、最佳实践建议 配置优化 : 根据硬件资源合理设置 procs 和 vm.count 复杂漏洞设置更长timeout 调试技巧 : 使用 debug 参数输出VM日志 使用 bench 参数记录统计信息 复现策略 : 优先尝试最后触发的程序 从简单配置开始逐步增加复杂度 最小化原则 : 先移除系统调用,再精简参数 不同类型参数采用不同最小化策略 通过本文分析的crash复现与最小化技术,Syzkaller能够高效地验证和简化发现的漏洞,为后续分析提供清晰的测试用例。下一部分将深入分析vmLoop函数的fuzz机制。