一. AST-Like格式详解
Syzkaller定义的AST(由Prog 结构体表示)定义了一个程序的整体结构,包含三大部分内容:
Prog结构体,Call结构体,Arg结构体。
1. Prog.go&AST-like包含的信息
prog.go 文件属于 syzkaller 项目,负责描述程序(program)的结构及其相关操作。程序由系统调用(syscalls)和它们的参数组成,这些程序用于在内核或虚拟化环境中进行模糊测试,寻找潜在的漏洞。该文件定义了 Prog(程序)的结构、系统调用的定义以及程序的各种操作方法。 1. Prog 结构体 该结构体表示一个程序,包含系统调用、注释等信息:
2. Call 结构体
3. Arg 接口及其实现
prog.go 文件是 syzkaller 项目中用于描述系统调用程序的核心部分。它提供了丰富的结构和方法来创建、操作和管理系统调用及其参数。通过这些结构和方法,syzkaller 可以构建、修改和执行复杂的系统调用序列,从而在操作系统中发现潜在的漏洞。 |
2. AST-like格式
相关代码:https://github.com/google/syzkaller/blob/master/prog/prog.go
Syzkaller文档中提到的AST-like格式指的是 prog/prog.go 中定义的 prog 结构。这种格式用于Syzkaller内部,代表程序的抽象语法树,其中每个系统调用都被表示为一个 Call 结构。此 AST-like 表示结构化了系统调用序列,便于 Syzkaller 进行进一步的分析、变异和生成 fuzzing 测试用例。 |
|
Prog 的表示形式虽然不是传统意义上的 AST,但它确实具有类似 AST 的树形结构和递归特性,可以看作是专门为系统调用序列设计的领域特定 AST(Domain-Specific AST)。这种设计非常适合 syzkaller 的需求,使得它能够高效地生成、操作和分析系统调用序列。 |
|
Syzkaller 的 "AST-like" 表示是系统调用序列的中间形式,它类似于抽象语法树 (AST),但它的结构化方式更接近编程语言中对函数调用的表示。它由系统调用和参数构成的树形结构表示,存储在内存中,主要用于描述系统调用序列、执行流程和数据结构。
3. AST-like的转换
Syzkaller程序在不同阶段经历了多个格式转换:
- 生成阶段: syz-manager 生成或从 corpus.db 读取可逆二进制格式并反序列为AST-like格式的程序。
- 转换阶段: 在 syz-manager 内部,调用 prog.SerializeForExec(),将AST-like格式转换为exec二进制格式。
- 发送阶段: syz-manager 通过RPC发送exec格式给 syz-executor。
- 执行阶段: syz-executor 直接执行收到的exec格式系统调用序列。
3.1 AST-like格式程序生成:
syzkaller首先将syzkaller/sys/linux/目录下装有描述(description)的txt声明文件生成amd.go文件,编译syzkaller后,能够读取系统调用的相关描述。
syz-manager 在运行过程中会根据生成策略和参考这些描述,生成"程序"的AST-like格式(即 prog.Program 结构)。
SQL |
AST-like格式 是 prog.Program 的内存表示形式,它定义了系统调用序列的结构,包括系统调用名称、参数、资源之间的关系等。
3.2 AST-LIKE转换文本格式
AST-like格式转换为文本格式: 当需要保存或展示程序时,Syzkaller会将程序从AST-like格式转换为可读的文本格式。这在程序的可视化、调试和存储(如corpus.db中)时非常有用。
1.syzkaller实现从AST-LIKE到文本格式的转化时,输入是AST-LIKE格式而不是corpus.db中的二进制形式,相关的代码文件:https://github.com/google/syzkaller/blob/master/prog/encoding.go
encoding.go 文件的主要功能是序列化和反序列化程序(Prog),它涉及到如何将 Prog 对象转换为文本数据并从文本数据中解析出程序对象。该文件还处理程序中的系统调用(Syscall)及其参数(Args)的编码与解码,支持多种不同的参数类型和复杂的数据结构。此外,还包括对自动生成参数(AUTO 类型)和条件性参数的处理。 1. 序列化与反序列化的核心逻辑 1.1 序列化:
1.2 反序列化:
2. 参数的处理 反序列化过程中,参数(Arg)是程序中最为重要的部分之一,程序通过解析这些参数来描述与操作系统交互的细节。不同的参数类型有不同的解析逻辑:
3. 数据处理
4. 地址和内存处理
5. 数据与十六进制转换:文件中的 hexToByte、toHexChar、fromHexChar 等函数处理十六进制与字节流之间的转换,确保系统调用中的数据能以正确的格式表示和存储。 encoding.go 是 Syzkaller 项目中用于处理系统调用程序的序列化与反序列化的核心文件。它不仅支持多种复杂的参数类型,还通过灵活的错误处理和恢复机制,确保程序在系统调用描述变化时能够正确解析。此外,文件中的自动参数处理和条件性参数处理逻辑使得 Syzkaller 能够生成高度灵活且动态的系统调用程序,极大地增强了模糊测试的能力。 |
二. Trace2syz解析
解析的完整流程如下:
1. 系统调用转换
将strace跟踪的系统调用(parser.Syscall)解析为Syzkaller所需的系统调用(prog.call)。
2. 参数处理
解析和转换系统调用的参数,包括指针、数组、结构体、联合体、资源、缓冲区等。
3.内存分配
根据程序需求生成内存分配调用(如mmap),并将其添加到Syzkaller程序的开头。
4.返回值缓存
缓存系统调用返回的资源,用于后续系统调用中引用。
5.上下文管理
使用Context对象管理转换过程中所需的元信息,例如当前调用、目标平台、返回值缓存等。
1. Trace格式转换需求
一个转换示例如下:
输入trace格式:
Bash |
转换后的Syzkaller程序:
Bash |
这个转换过程涉及:
- 从文本格式的原始trace解析系统调用信息成中间表示(Tracetree)
- 提取调用名称、参数和返回值
- 类型推断和参数重建
- 生成符合Syzkaller格式的程序
主要代码文件:parser/parser.go: trace解析成中间表示
2. 转换流程
注意代码块中都只是该代码文件的简略表述:
// parser/parser.go
func Parse(data []byte) (*TraceTree, error) {
// 1. 词法分析,将文本分割为token
lexer := newLexer(data)
// 2. 语法分析,构建语法树
parser := newParser(lexer)
// 3. 构建TraceTree结构
tree := &TraceTree{
Processes: make(map[int]*Process),
}
}
3. 代码分析
parser.go 的主要功能是解析系统调用跟踪文件(trace 文件),将其转换为一个中间表示(TraceTree),以便后续进行分析。它的核心任务是逐行读取跟踪文件,解析每一行的系统调用信息、覆盖率信息,并将这些信息组织到一个层次化的数据结构中。
TraceTree 和 Syscall在intermediateTypes.go文件中给出定义
转换流程如下:
- 逐行读取文件内容
- 解析系统调用:通过 parseSyscall 函数提取系统调用名称、参数、返回值等信息并构建 Syscall 对象。
- 解析覆盖率信息:通过 parseCoverage 函数提取覆盖率信息(如指令地址)并关联到对应的系统调用。
- 构建进程树:通过 TraceTree 和 Process 数据结构,将系统调用与其所属的进程关联起来。
- 过滤无关行:跳过无关的行,例如包含 ERESTART 错误码或信号标记(+++ 和 ---)。
TraceTree结构:
- TraceTree是最顶层的结构,包含了所有进程信息
- 每个Process包含了该进程下的所有系统调用
- 每个Syscall包含了我们之前看到的系统调用信息(名称、参数、返回值、覆盖率)
所以实际上,之前显示的Syscall结构是完整TraceTree结构的一个子集,它被包含在Process结构的Calls数组中,而Process又被存储在TraceTree的Ptree映射中。
Bash |
Bash |
4. trace2syz.go
将 strace 跟踪的系统调用日志转换为Syzkaller 可执行的输入格式的程序(prog.Prog),
trace2syz 的输入是结构化的中间表示(并不是parser/parser.go中表示多进程的Tracetree,而是parser/intermediateTypes.go中定义的Trace格式),而不是原始的 strace 文件。
1. Trace的定义
在parser.g0中,Trace被定义为一个系统调用的列表:
//Trace is just a list of system calls
type Trace struct{
Calls []*Syscall
}
Trace是一个简单的结构,包含一个Ca11s字段,表示系统调用的列表。其中的每个系统调用用Syscal1类型表示。
2. Syscall的定义
Syscal1是Trace中每个调用的表示形式:
//Syscallstruct is the IR type for any system call
typeSyscallstruct{
CallName string //系统调用名称
Args []IrType //系统调用的参数
Pid int64 //进程ID
Ret int64 //返回值
Cover []uint64 //覆盖信息
Paused bool //是否被信号中断
Resumed bool //是否恢复
}
关键点如下:
1.CallName:系统调用的名称,例如open或read
2.Args:系统调用的参数,使用IrType表示。
3.Pid:发起系统调用的进程D。
4.Ret:系统调用的返回值。
5.Cover:覆盖信息(如果有)。
6.Paused和Resumed:表示系统调用是否被中断以及是否恢复。
当 parser.Parse 解析一个 strace 文件时,它会生成一个 TraceTree。TraceTree 包含多个 Trace,每个 Trace 对应一个进程的系统调用。
trace2syz 需要的是单进程的系统调用序列,而不是整个 TraceTree,从 TraceTree 中提取单个进程的 Trace,可以通过 TraceTree的TraceMap 字段实现。
三. AST-Like格式提取
Bash |
- crashes文件夹中的文件名:在将运行后的corpus.db通过syz-db进行unpack时,程序的文件名都是这个程序对应的唯一哈希值。在将这些文本格式的POC重新进行pack打包成corpus.db时,syzkaller也会校验文件名对应的哈希值是否正常。
- syz-db只有在进行pack操作且发现POC文件名和其哈希值不符时才会进行中间格式AST的转换(反序列化),unpack操作时则是直接从corpus.db中读取二进制内容写入到文件中形成我们人类阅读的文本格式。因此需要额外补充代码进行AST格式的保存。本工作针对syz-db.go的unpack方法进行修改,增加了AST结构的读取和保存步骤。
1. 修改syz-db.go后
Bash |
2. 重新编译syz-db工具
make db
3. 使用unpack方法对某个corpus.db进行解包
./bin/syz-db unpack test.db UNPACK/
./bin/syz-db unpack corpus.db UNPACK/
./bin/syz-db pack waitPack test.db
- 解包后得到五种结果(依据syz-db.go的不同版本有一定区别,但txt和JSON文件必然存在)
- txt 文件 提供了调用的文本格式,由corpus.db的二进制内容直接写入,适合人类快速查看。
- AST_文件夹内的JSON 文件 提供了详细的参数类型和结构信息,保存的具体内容由人工定义的结构体控制,适合程序分析。
- AST_文件夹内的TXT文件(与txt进行区别,本文中采用大写进行表述) 提供了更接近程序内部表示的信息,包含内存地址,适合深入调试。
- target.txt:每个程序分析的统计信息,如调用次数、是否有注释等。
- 对prog的go格式保存(输出打印),保留了完整的prog的内容。
Bash |
1. AST-Like格式分析
1. txt 文件
- 内容:包含系统调用的原始文本表示。
- 信息:展示了调用的名称和参数的基本结构,但缺乏参数的详细类型和结构信息。
- 用途:用于快速查看调用序列,适合人类阅读。
在指定的UNPACK文件夹内,存放了corpus.db中二进制文件直接转换为的文本格式txt文件:
Bash |
2. JSON 文件
- 内容:以 JSON 格式详细描述了每个调用,包括调用名称、属性、参数类型、参数值、返回值等。
- 信息:提供了参数的类型信息(如 PointerArg, DataArg 等),以及每个参数的详细结构(如子参数)。
- 用途:适合程序解析和进一步分析,因为它结构化且详细。
注意:JSON格式中所有获取的字段/结构,都是仿造syzkaller中的Prog, Call,Args结构体进行构建和获取,所以会导致初版代码不能获取ANY,AUTO等关键字,因为没有进行单独的说明。为了获取这些关键字,需要进行单独的处理。
在指定的UNPACK文件夹内的AST_子文件夹内,存放了提取的AST-like格式json文件:
Bash |
3. TXT 文件
- 内容:以文本格式描述了程序及其调用的详细信息。
- 信息:类似 JSON 文件,但以更接近程序内部表示的方式展示,包含指针和参数的引用信息。
- 用途:适合技术人员理解程序的内部表示,可用于调试。
在指定的UNPACK文件夹内的AST_子文件夹内,存放了接近程序内部表示的信息,包含内存地址和对应系统调用在syzkaller本地文件中的引用序号:
Bash |
字段分析:
Bash |
如下图所示,Name为这段内容具体的方法名,CallName是Syzkaller内系统调用名,一个CallName可以声明多个Name。同时下命的fs,dir,flags等,都是这个Name对应的方法所需的参数,Ref后的数字是标识这个Name对应参数的唯一标识。
|
|
TXT中Arg的数量和txt中对应系统调用的参数数量相同,值对应。例如Call1的
Bash |
对应的是0x0和0x0这两个参数
Bash |
4. target.txt文件
在运行unpack(选定的解压文件夹)路径下的target.txt包含了每个程序分析的统计信息,如调用数量、是否有注释等
Bash |