-
-
[原创]syzkaller源码分析(一) syz-manager.go
-
2021-6-20 17:19 8699
-
关于syzkaller的资料以安装居多,代码分析比较少,把自己的关于代码的理解稍微写一下,有理解错误的函数也请指正。下面内容主要就是syz-manager.go的主要流程,去掉了对核心流程不重要的部分以及具体实现,要是了解具体的实现还要去看源码。源码地址如下:https://github.com/google/syzkaller
md文件会附在最后
对syzkaller的简介可以去看 https://xz.aliyun.com/t/5079
syzkaller正常情况下都以syz-manager作为入口,syz-manager.go主要负责各种工作的启动(HTTP、RPC、dashboard等等)、调用fuzz以及repro的生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | manager.go func main() { RunManager(cfg) } func RunManager(cfg * mgrconfig.Config) { 初始化: 文件 corpus.db &Manager HTTP RPC prometheus参数 dash go func() { log() } if * flagBench ! = "" / / bench: write execution statistics into this file periodically go func(){ mgr.minimizeCorpus() 写入文件 } if mgr.dash ! = nil { go mgr.dashboardReporter() } mgr.vmLoop() } func (mgr * Manager) vmLoop() { 启动instance 初始化pendingRepro reproducing reproInstances / / 可以复现并且有剩余的instances for canRepro() && len (instances) > = instancesPerRepro { go func() { repro.Run() } } / / 没有可以复现的但是有剩余的instances for !canRepro() && len (instances) ! = 0 { go func(){ / / 调用mgr.runInstanceInner; / / mgr.runInstanceInner()调用了FuzzerCmd()启动fuzz; 有调用MonitorExecution()监控信息并返回Report对象 / / 返回crash信息 mgr.runInstance(idx) } } / / 下面进行结果处理 ... } } |
重点函数 minimizeCorpus()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | func (mgr * Manager) minimizeCorpus() { ... / / 返回的signal有elemType相同的,取其中prio最高的 for _, ctx : = range signal.Minimize(inputs) { ... } ... / / 为了防止出现corpus explosion,需要统计call的cov和count情况 for call, info : = range mgr.collectSyscallInfoUnlocked() { if 出现corpus explosion情况 mgr.saturatedCalls[call] = true } 写回db.filename } |
重点函数repro.Run()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | func Run(){ ParseLog(crashLog) / / 返回[] * LogEntry LogEntry describes one program in execution log. 设置各种参数 go func(){ 启动vmIndex个Instance } / / 最主要的函数 / / 见下面 res, err : = ctx.repro(entries, crashStart) / / Try to rerun the repro if the report is corrupted. / / Corrupted indicates whether the report is truncated of corrupted in some other way. for attempts : = 0 ; ctx.report.Corrupted && attempts < 3 ; attempts + + { if res.CRepro { _, err = ctx.testCProg(res.Prog, res.Duration, res.Opts) } else { _, err = ctx.testProg(res.Prog, res.Duration, res.Opts) } if err ! = nil { return nil, nil, err } } } |
重点函数 ctx.repro
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | func (ctx * context) repro(){ / / Cut programs that were executed after crash. / / 发生crash后面的prog就不需要了 for i, ent : = range entries { if ent.Start > crashStart { entries = entries[:i] break } } / / 返回值是能触发crash的单个program或者能触发crash的programs的组合 / / 首先,在所有program(用entries数组表示)提取出每个proc所执行的最后一个program, / / 并按执行先后倒序排列,存放到lastEntries数组中。 / / 其次,调用extractProgSingle()倒序执行每一个program,如果res不为空,返回 / / 最后(即单个程序不会引起crash),调用extractProgBisect()测试哪几个program一起触发了crash / / extractProgBisect()的实现:先调用bisectProgs()进行分组,看哪一组可以触发crash。 ctx.extractProg(entries) / / 主要调用prog.Minimize(),Minimize calls and arguments / / prog.Minimize()的原始注释如下: / / Minimize minimizes program p into an equivalent program using the equivalence / / predicate pred. It iteratively generates simpler programs and asks pred / / whether it is equal to the original program or not . If it is equivalent then / / the simplification attempt is committed and the process continues. ctx.minimizeProg(res) / / Try extracting C repro without simplifying options first. / / Try triggering crash with a C reproducer. / / 调用ctx.testCProg()看能不能返回crashed = true,再进行赋值res.CRepro = crashed ctx.extractC(res) / / Simplify options and try extracting C repro. / / 在repro.go中定义了progSimplifies数组作为简化规则,依次使用每一条规则后,如果有效(crash还能被触发), / / 再调用extractC(res)尝试提取C repro if !res.CRepro { ctx.simplifyProg(res) } / / Simplify C related options. / / 跟上面的ctx.simplifyProg(res)差不多,就是规则使用了cSimplifies数组 if res.CRepro { ctx.simplifyC(res) } } |
赞赏
他的文章
看原图