本文架构
- 1.动机
- 2.honggfuzz的基本概念
- 官网描述
- 解读
- 3. honggfuzz的反馈驱动(Feedback-Driven)
- 软件驱动反馈(software-based coverage-guided fuzzing)
- 代码覆盖率
- 代码覆盖率的计量单位
- 代码覆盖率的统计方式
- 硬件驱动反馈( hardware-based coverage-feedback fuzzing)
- 4.持久型Fuzzing(Persistent Fuzzing)
- 5. 日志信息
1.动机
最近的学习需要使用honggfuzz,但苦于一直没有高度相关的、系统性总结的博客,官方文档写的也十分有限。于是收集各方资料形成此文。笔者还在初步学习阶段,若有错误还请大家不吝指出。
【Github】honggfuzz
首先提供几篇参考的博客,若看完本文还有不懂的地方希望能给大家解惑。
【安全客Blog】honggfuzz漏洞挖掘技术原理分析这篇文章讲的比较细致,我的大部分知识基本是这里看懂的。
【看雪Blog】honggfuzz漏洞挖掘技术深究系列分析的比较深入,且博主好几篇类似文章,值得参考。
2.honggfuzz的基本概念
官网描述
Description:A security oriented, feedback-driven, evolutionary, easy-to-use fuzzer with interesting analysis options.
解读
从官网描述中我们可以提取到honggfuzz的几个特点:
面向安全(security oriented): 这点十分容易理解,fuzzing工具用以测试程序代码中可能存在的安全隐患(i.e. 漏洞)。
反馈驱动(feedback-driven): 通过监控样本触发的代码覆盖率,改进输入样本以提高代码覆盖率,增加发现漏洞的概率。本文将在第3章介绍。
持久型Fuzzing(Persistent Fuzzing): 所谓持久型fuzzing(Persistent Fuzzing),就是长生命周期进程重复调用被fuzz的API。
多种反馈驱动方式(Feedback-Driven):
* 支持基于软件和基于硬件(eg.分支计数(branch counting)、指令计数(instruction counting)等)
* Intel BTS(Branch Trace Store),
* Intel PT(Processor Tracing)
更接近底层的异常监视: 使用底层接口监视进程(linux和NetBSD下使用ptrace),与其它fuzzer相比更有可能从crash中发现并报告被劫持/忽略的信号(被fuzz的程序可能截获并隐藏)。
3. honggfuzz的反馈驱动(Feedback-Driven)
【官方文档】FeedbackDrivenFuzzing.md
Feedback-driven fuzzing :
Honggfuzz is capable of performing feedback-guided (code coverage driven) fuzzing. It can utilize the following sources of data:
* (Linux) Hardware-based counters (instructions, branches)
* (Linux) Intel BTS code coverage (kernel >= 4.2)
* (Linux) Intel PT code coverage (kernel >= 4.2)
* Sanitizer-coverage instrumentation (-fsanitize-coverage=bb
)
* Compile-time instrumentation (-finstrument-functions
or-fsanitize-coverage=trace-pc[-guard],indirect-calls,trace-cmp
or both)
Developers may provide the initial file corpus which will be gradually improved upon, but it’s not necessary with feedback-driven modes.
软件驱动反馈(software-based coverage-guided fuzzing)
Requirements for software-based coverage-guided fuzzing
-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp
- Clang >= 5.0-fsanitize-coverage=trace-pc
- GCC >= 9.0-fsanitize-coverage=bb
- Clang >= 3.7-finstrument-functions
- GCC or Clang- [older, slower variant]
-fsanitize-coverage=trace-pc,indirect-calls
- Clang >= 3.9
Note: The -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp set of flags will be automatically added to clang’s command-line switches when using hfuzz-clang binary.
命令示例:
$ <honggfuzz_dir>/honggfuzz/hfuzz_cc/hfuzz-clang terminal-test.c -o terminal-test
代码覆盖率
代码覆盖率的计量单位
honggfuzz的代码覆盖计算有三种方式:
- 函数级(Function-Level)
- 基本块级(Basic Bolck-Level)
- 边界级(Edge-Level)
函数级别(Function-Level) 最为粗糙,只记录函数的调用情况。如下代码,其仅会记录function1
,function2
是否执行,而不关心内部if
语句的执行情况,使用这种覆盖率统计方式可能会使我们的程序分析丢失一定的分析精度。
因此,常用的是基本块级(Basic Bolck-Level),能满足大部分程序分析的精度要求,基本块以跳转指令做为划分依据。如function1
代码,此时,会分别记录Block A
和Block B
的是否执行。当用例a确小于b时仅能触发Block A
,而Block B
无法覆盖。通过用例变异后续可能产生能访问Block B
的测试用例。
void function1(int a,int b){
... if(a<b){//Block A}else{//Block B}
...
}
边界级(Edge-Level) 是基本块级更为精细的一种情况。如下代码,if(a)
及其上面的语句被划归为Block A
,当a==0时才会执行Block A
,否则执行Block C
。前面基本块的方式就无法确切地知道是否曾从Block A
执行到Block C
,因为不论是A-B-C
还是A直达C
,基本块A和C都会被标记为已覆盖。
为了解决这一问题,edge方式会在A-C之间创建一个虚拟块D。以判断是否曾经执行过A直达C
路径。
void function2(int a){
... //Block Aif(a){a=0;//Block B}else{//Block C}
}
代码覆盖率的统计方式
有源码时:
SanitizerCoverage:SanitizerCoverage内置在LLVM中,可以在函数、基本块和边界这些级别上插入对用户定义函数的回调,实现了简单的覆盖率报告和可视化。
- 添加编译选项(基本块统计方式)
CFLAG=“-fsanitize=address -fsanitize-coverage=bb”
无源码时:
使用Pin、DynamoRIO等二进制插桩工具去hook统计,或者pediy改指令的方式去监控。
硬件驱动反馈( hardware-based coverage-feedback fuzzing)
【区分为LINUX系和Intel(x86)系】
Linux系:
Requirements for hardware-based counter-based fuzzing
- GNU/Linux OS
- A relatively modern Linux kernel (4.2 should be ok)
- CPU which is supported by the perf subsystem for hardware-assisted instruction and branch counting
Intel系
Requirements for hardware-based coverage-feedback fuzzing (Intel)
- CPU supporting BTS (Branch Trace Store) for hardware assisted unique pc and edges (branch pairs) counting. Currently it’s available only in some newer Intel CPUs (unfortunately no AMD support for now)
- CPU supporting Intel PT (Processor Tracing) for hardware assisted unique edge (branch pairs) counting. Currently it’s available only in some newer Intel CPUs (since Broadwell architecture)
- GNU/Linux OS with a supported CPU; Intel Core 2 for BTS, Intel Broadwell for Intel PT
- Intel’s ibipt library for Intel PT
- Linux kernel >= v4.2 for perf AUXTRACE
对于闭源程序的反馈驱动Fuzzing,通常有3种方式:
-
二进制插桩:使用Pin或DynamoRIO动态插桩监控代码覆盖率,比如winafl
-
虚拟化技术:使用Qemu或Boch等虚拟化技术实现应用层和内核层的代码覆盖率监控,比如afl、bochpwn
-
硬件级技术:使用Intel Processor Trace(PT)技术,比如honggfuzz
apt install linux-tools-common
# 安装好后输入
perf -v
#然后会弹出提示你安装哪些包,根据提示安装对应的包即可。
perf
按照固定的频率去“采样”,然后再统计函数的调用次数,算出百分比。只要采样的频率足够大,把这些“瞬时截面”组合在一起,就可以得到进程运行时的可信数据,比较全面地描述出 CPU 使用情况。
输入 top 查看进程的 PID 号
$ sudo perf top -K -p 3213 (PID)
4.持久型Fuzzing(Persistent Fuzzing)
如前所述,持久型Fuzzing就是具有长生命周期进程,可重复调用被用以fuzz的API
-
trace-pc
: 追踪执行过的基本块BB,在每个edge中插入__saitizer_cov_trace_pc
函数,可定义该函数作为相应的回调处理。 -
indirect-calls
:在每个间接调用中添加PC追踪,与前面的trace-pc
或trace-pc-guard
联合使用,回调函数:__sanitizer_cov_trace_pc_indir
-
trace-cmp
:追踪每个比较指令和swith语句
5. 日志信息
测试时会输出日志信息。
下面是日志信息的源码。
LOG_I("Size:%zu (i,b,hw,edge,ip,cmp): %" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64"/%" PRIu64 "/%" PRIu64 ", Tot:%" PRIu64 "/%" PRIu64 "/%" PRIu64 "/%" PRIu64"/%" PRIu64 "/%" PRIu64,run->dynamicFileSz, run->linux.hwCnts.cpuInstrCnt, run->linux.hwCnts.cpuBranchCnt,run->linux.hwCnts.newBBCnt, softCntEdge, softCntPc, softCntCmp,run->global->linux.hwCnts.cpuInstrCnt, run->global->linux.hwCnts.cpuBranchCnt,run->global->linux.hwCnts.bbCnt, run->global->linux.hwCnts.softCntEdge,run->global->linux.hwCnts.softCntPc, run->global->linux.hwCnts.softCntCmp);
可以看到,输出的信息分别代表单次测试设计的指令数量(cpuInstrCnt
)、分支计数(cpuBranchCnt
)、新基本块数量newBBCnt
)、软件边(softCntEdge
)、软件路径(softCntPc
)、软件比较次数(softCntCmp
)。以Tot:
开头的则是整个程序分析的次数。
先写到这,后期继续更。任重道远啊…哎~