winafl

标签(空格分隔): fuzz


构成

afl-fuzz.c 主模块

读取文件

维护testcase queue

进行mutate fuzz_one

评估代码覆盖率 执行遗传算法

更新界面 show_stats

winafl.c 注入dll

循环调用fuzz目标

更新覆盖率位图

注册事件回调函数

在模块加载回调中对模块进行插桩

使用pre_fuzz_handler和post_fuzz_handler进行循环fuzz

基本块\边界 覆盖率模式


(一)变异部分

queue_entry代表每一个测试用例的struct

//测试用例队列
struct queue_entry { u8* fname; /* File name for the test case */
u32 len; /* Input length */ u8 cal_failed, /* Calibration failed? */
trim_done, /* Trimmed? */
was_fuzzed, /* Had any fuzzing done yet? */
passed_det, /* Deterministic stages passed? */
has_new_cov, /* Triggers new coverage? */
var_behavior, /* Variable behavior? */
favored, /* Currently favored? */
fs_redundant; /* Marked as redundant in the fs? */ u32 bitmap_size, /* Number of bits set in bitmap */
exec_cksum; /* Checksum of the execution trace */ u64 exec_us, /* Execution time (us) */
handicap, /* Number of queue cycles behind */
depth; /* Path depth */ u8* trace_mini; /* Trace bytes, if kept */
u32 tc_ref; /* Trace bytes ref count */ struct queue_entry *next, /* Next element, if any */
*next_100; /* 100 elements ahead */ };

fuzz_one函数

afl-fuzz.c的主要函数是fuzz_one(char** argv),对测试文件的变形操作也通过这个函数完成,在fuzz_one函数中会调用trim_case(char** argv, struct queue_entry* q, u8* in_buf)函数,先对测试用例进行裁剪,测试用例太大会影响测试速度,这是肯定的,所以理论上说测试用例越小那么全部fuzzing完成的时间就越短,那么怎么裁剪才不会影响fuzzing的结果呢,afl-fuzz.c靠如下手段完成。

首先,Winafl会建立一个64KB的共享内存,用于存储被fuzzing的应用程序的执行路径,如:A -> B -> C -> D -> E (tuples: AB, BC, CD, DE) ,当程序路径发生改变时,如变成A -> B -> D -> C -> E (tuples: AB, BD, DC, CE),Winafl会更新共享内存,发现新的执行路径更有助于发现代码的漏洞,因为大多数安全漏洞经常是一些没有预料到的状态转移,而不是因为没有覆盖那一块代码。

trim_case函数会用测试用例的总长度除以16,并删除这个长度的数据交给被fuzzing的应用程序打开并记录执行路径到共享内存中,执行完成后trim_case函数对这块共享内存进行hash计算,如果这块共享内存没有发生变化,那么说明没有发现新的执行路径,也同时说明被裁剪的这段数据对整个测试结果影响不大,于是对以后的测试用例都删除掉这段内容,这样,测试用例就会减小到一个合理的大小而并不会影响fuzzing结果。

在fuzz_one函数里,接着就会对测试用例文件做大量的变形操作,包括以下几种类型的变形:bit flips、byte flips、arithmetics、known ints、dictionary、havoc。

1.bit flips

2.byte flips

3.arithmetics

4.known ints

5.dictionary

6.havoc

bit flips

#define FLIP_BIT(_ar, _b) do { \
u8* _arf = (u8*)(_ar); \
u32 _bf = (_b); \
_arf[(_bf) >> 3] ^= (128 >> ((_bf) & 7)); \
} while (0)

byte flips

在byte flips模式下,对测试用例文件的内容进行按每8、16、32位的长度进行顺序,变形时使用语句:out_buf[stage_cur] ^= 0xFF;、(u16)(out_buf + i) ^= 0xFFFF;、(u32)(out_buf + i) ^= 0xFFFFFFFF;来进行变形,并把变形后的测试用例交给common_fuzz_stuff函数进行后续处理。

arithmetics

在arithmetics模式下,

8位的运算运用变形语句:

out_buf[i] = orig + j;

16位的运算运用变形语句:

(u16)(out_buf + i) = orig – j;

32位的运算运用变形语句:

(u32)(out_buf + i) = orig + j;

并把变形后的测试用例交给common_fuzz_stuff函数进行后续处理。

known

在known ints模式下

8位的运算运用变形语句:

out_buf[i] = interesting_8[j];

16位的运算运用变形语句:

(u16)(out_buf + i) = interesting_16[j];

32位的运算运用变形语句:

(u32)(out_buf + i) = interesting_32[j];

并把变形后的测试用例交给common_fuzz_stuff函数进行后续处理。其中interesting相关定义如下:

static s8 interesting_8[] = { INTERESTING_8 };
static s16 interesting_16[] = { INTERESTING_8, INTERESTING_16 };
static s32 interesting_32[] = { INTERESTING_8, INTERESTING_16, INTERESTING_32 };

dictionary

在dictionary模式下,允许用户使用字典,这样可以用字典文件对测试用例的相关部分进行替换并可以对一些有格式的文本、协议进行fuzz,如html、js、php等。

如果使用字典,参数是-x,后面可以是目录或者是一个字典文件,如:-x dict\xml.dict、-x dict,装载字典的函数是load_extras,如果参数是一个文件,那么用load_extras_file打开并格式化,用于对一些文本文件处理程序进行fuzz,如php、sql等、如果是一个目录,那么load_extras函数把所有文件内容读到extras数组,对二进制文件处理程序进行fuzz,如doc、xls等,此时对字典文件的大小限定为小于128B,但这个版本的load_extras函数写的有问题,没办法使用,但自己可以简单的修改下,如把load_extras_file(dir, &min_len, &max_len, dict_level);goto check_and_sort;这2条语句上移,不进行多余的判断,直接执行load_extras_file函数,至少能用字典跑一些带格式的文本文件,文本格式文件的一些字典可以参考testcases的例子。

havoc

在havoc模式下,通过如下表达式UR(15 + ((extras_cnt + a_extras_cnt) ? 2 : 0))计算出一个值,并通过switch…case进行不同的匹配变形。

other

char *dynamorio_dir;
cmd = alloc_printf
(
"%s\\drrun.exe -pidfile %s -no_follow_children -c winafl.dll %s -fuzzer_id %s -- %s",
dynamorio_dir,
pidfile,
client_params,
fuzzer_id,
target_cmd
);

执行dynamorio进程

destroy_target_process

进行插桩


(二)捕获部分

afl-fuzz进行变形后,调用DynamoRIO的对目标程序进行instrumentation,监视目标程序的运行,如果目标程序crash,那么复制变形后的测试用例到out\crashes目录下,如果目标程序无响应,那么复制变形后的测试用例到out\hangs目录下。

这里负责运行DynamoRIO的函数是run_target函数。

run_target

run_target首先检查目标进程是否存在,如果存在终止进程。

并通过drconfig.exe -nudge_pid %d 0 1命令将一个参数为1的nudge事件送到客户端回调,既目标fuzz程序的参数为1。

然后通过 drrun.exe -pidfile %s -no_follow_children -c winafl.dll %s — %s

命令启动目标程序,并注入winafl.dll到目标程序进程,之后的操作可以通过查看winafl.c文件进行分析,winafl.c的入口函数为dr_client_main函数,先继续一些必要的初始化工作后,首先通过options_init函数处理winafl的相关命令行参数,包括coverage_module、target_module、target_offset等。

接着挂载各种回调函数:event_exit,退出事件回调;onexception,异常回调;instrument_bb_coverage,instrument_edge_coverage,两种模式instrumentation的回调;event_module_load,event_module_unload模块装载、卸载回调。

在event_module_load函数中,可以看到winafl通过drwrap_wrap(to_wrap, pre_fuzz_handler, post_fuzz_handler);

完成在给定偏移(offset)的指向函数进入时运行pre_fuzz_handler函数,退出时运行post_fuzz_handler函数。

moudle设置

coverage_module可以有多个,target_module只有一个。

target_offset指定的是相对于target_module基地址的偏移。

coverage_module和target_module必须是目标被fuzz程序所调用的模块或其自身,确定的办法可以通过-debug参数完成,如运行如下命令行:

C:\test\DynamoRIO\bin32\drrun.exe
-c winafl.dll
-debug
-target_module test_gdiplus.exe
-target_offset 0x1650
-fuzz_iterations 10
-nargs 2
— test_gdiplus.exe input.bmp

winafl会在当前目录下生成log文件,文件名类似afl.test_gdiplus.exe.13280.0000.proc.log,内容如下图所示:

原理

如果一遍遍运行再结束程序,fuzzing的效率将非常低,所以我们可以将函数进程启动,然后找到一个会打开并完全关闭输入文件的函数,通过操作pc寄存器,使得程序反复执行该函数,当然每次执行前需要将我们的输入文件mutate以下,这样我们就节省了创建进程,杀死进程的时间了,每秒可以测试上百个输入文件。而操作pc寄存器是得靠PIN工具(也就是instrumentation工具)来完成,这就是dynamoRIO的作用。另外,如果强行改变pc寄存器,而不去管内存呀寄存器的状态,是很可能造成程序崩溃的,所以必须要在我们的目标函数被调用之前进行现场的保存和恢复,这就是pre_fuzz_handler和post_fuzz_handler的作用,这两个函数其实就是使用dynamoRIO注册了这两个hook函数,分别在目标函数执行前和执行后执行。

首先,想要用winafl fuzz软件,目标软件必须支持命令行,能从命令行读取输入文件。

其次,in目录中的test_case不能为使得程序crash的输入,也就是不能用poc作为test_case,不然fuzzer在第一次迭代就crash,根本不会继续往下执行。

再次,作为target函数的函数有几点要求,一是必须进行一次完整的文件打开和关闭操作,如果没有文件关闭操作,winafl的mutator无法生成新的输入文件(覆盖旧的输入文件),会造成winafl强行kill进程,这样fuzzing效率非常低,而且并不是我们期待的做法。原文中提供的函数0x532a0仅有打开文件操作,没有关闭文件操作,是无法进行正确地fuzzing的。二是目标函数的参数如果有太复杂的指针,fuzzing有可能无法进行,这是因为在pre_fuzz_handler中无法聪明地恢复指针所指向的数据造成的(只能恢复指针本身的值),这种情况下有可能在第二次迭代中(也就是强行修改了pc寄存器后)造成崩溃。

参数

afl-fuzz.exe命令行格式如下:

afl-fuzz [afl options] — [instrumentation options] — target_cmd_line

afl-fuzz.exe参数[afl options]

-i 用于记录输入样本
-o 用于保存输出数据
-t 每次测试的超时时间 -D DynamoRIO的路径
-Y enable the static instrumentation mode -f location read by the fuzzed program (stdin)
-d quick & dirty mode (skips deterministic steps)
-x fuzzing字典(see README) -target_offset 是要测试函数的偏移

winafl.dll参数[instrumentation options]

-coverage_module fuzzing对象程序会调用到的模块
-target_module 目标模块 就是target_offset所在的模块
-target_offset 偏移地址 就是会被instrumentation的偏移
-target_method 与上面相同 存在符号的时候可以使用
-fuzz_iterations 对象程序重启一次内运行目标函数的最大迭代数
-debug – debug模式
-nargs 放在结尾指示参数数目 test.exe @@ //其中的@@代指-i的输入文件

target_cmd_line参数就是要fuzzing对象的启动程序

测试样例


afl-fuzz.exe
-i in
-o out
-D C:\test\DynamoRIO\bin32
-t 20000
--
-coverage_module gdiplus.dll
-coverage_module WindowsCodecs.dll
-fuzz_iterations 5000
-target_module test_gdiplus.exe
-target_offset 0x1650
-nargs 2
--
test_gdiplus.exe @@

测试

使用drrun.exe对winafl.dll进行插桩测试

drrun.exe
-c winafl.dll
-debug
-target_module test.exe
-target_offset 0x1010
-fuzz_iterations 10
-nargs 2
--
test.exe in\file.txt
                   WinAFL 1.11 based on AFL 2.43b (test.exe)

+- process timing -------------------------------------+- overall results ----+
| run time : 0 days, 0 hrs, 0 min, 46 sec | cycles done : 0 |
| last new path : 0 days, 0 hrs, 0 min, 31 sec | total paths : 2 |
| last uniq crash : none seen yet | uniq crashes : 0 |
| last uniq hang : none seen yet | uniq hangs : 0 |
+- cycle progress --------------------+- map coverage -+----------------------+
| now processing : 0 (0.00%) | map density : 0.02% / 0.02% |
| paths timed out : 0 (0.00%) | count coverage : 1.00 bits/tuple |
+- stage progress --------------------+ findings in depth --------------------+
| now trying : interest 16\8 | favored paths : 1 (50.00%) |
| stage execs : 693/1779 (38.95%) | new edges on : 2 (100.00%) |
| total execs : 5096 | total crashes : 0 (0 unique) |
| exec speed : 116.0/sec | total tmouts : 0 (0 unique) |
+- fuzzing strategy yields -----------+---------------+- path geometry -------+
| bit flips : 0/400, 0/399, 0/397 | levels : 2 |
| byte flips : 0/50, 0/49, 0/47 | pending : 2 |
| arithmetics : 1/2800, 0/0, 0/0 | pend fav : 1 |
| known ints : 0/200, 0/0, 0/0 | own finds : 1 |
| dictionary : 0/0, 0/0, 0/0 | imported : n/a |
| havoc : 0/0, 0/0 | stability : 93.75% |
| trim : 0.00%/12, 0.00% +-----------------------+
^C----------------------------------------------------+

WinAFL的更多相关文章

  1. Winafl学习笔记

    最近在跟师傅们学习Winafl,也去搜集了一些资料,有了一些自己的理解,就此记录一下. Winafl是一个运行时插桩工具,可以提高crash的捕获率. 同时也有自己的遗传算法,可以根据代码覆盖程度进行 ...

  2. 初识 Fuzzing 工具 WinAFL

    转:https://paper.seebug.org/323/ 初识 Fuzzing 工具 WinAFL 作者:xd0ol1(知道创宇404实验室) 0 引子 本文前两节将简要讨论 fuzzing 的 ...

  3. 转:初探Windows Fuzzing神器----Winafl

    转:http://www.tuicool.com/articles/j2eqym6 小结:找到感兴趣的函数,计算偏移,自动化fuzz. 这是一篇客座文章,作者是乌云二进制漏洞挖掘白帽子 k0shl . ...

  4. 转:智能模糊测试工具 Winafl 的使用与分析

    本文为 椒图科技 授权嘶吼发布,如若转载,请注明来源于嘶吼: http://www.4hou.com/technology/2800.html 注意: 函数的偏移地址计算方式是以IDA中出现的Imag ...

  5. winafl 源码分析

    前言 winafl 是 afl 在 windows 的移植版, winafl 使用 dynamorio 来统计代码覆盖率,并且使用共享内存的方式让 fuzzer 知道每个测试样本的覆盖率信息.本文主要 ...

  6. [winafl]这几天的折腾

    1.自己写的exe 2.自己写的dll,然后写了接口程序去load...但是这个速度真是迷醉 先把基本的几种类型跑起来,再解决速度和样本的问题...

  7. 使用 WinAFL 图片解析软件进行模糊测试 - FreeImage 图片解析库

    看雪链接:https://bbs.pediy.com/thread-255162.htm

  8. 试写foxit reader的ConvertToPDF功能的wrapper

    相比于直接fuzzing大型程序本身,针对程序的某一特定功能写wrapper后再fuzzing则要高效的多.网上搜了下,仅有两篇关于foxit reader的wrapper文章,一个用python,另 ...

  9. WinRAR代码执行漏洞CVE-2018-20250

    0x01 分析思路 利用https://github.com/googleprojectzero/winafl 漏洞分析框架模糊测试WinRAR. 几个存档格式的崩溃,例如RAR,LZH和ACE,这些 ...

随机推荐

  1. ECharts图表引用json数据

    来讲两个图表,一个折线图,一个饼图. 先来看看效果图: 现在来看看代码,先来折线图,后台: (这里的后台太麻烦了,写的太多.可以使用Linq的方式,Linq比较简单写的也少.参考我的这篇文章的2018 ...

  2. Kafka记录-Kafka简介与单机部署测试

    1.Kafka简介 kafka-分布式发布-订阅消息系统,开发语言-Scala,协议-仿AMQP,不支持事务,支持集群,支持负载均衡,支持zk动态扩容 2.Kafka的架构组件 1.话题(Topic) ...

  3. Dapper总结(二)---事务和存储过程

    一  dapper使用事务 string sql1 = "insert into UserInfo values('user8',27,'s')"; string sql2 = & ...

  4. 四种常见的 POST 提交数据方式(application/x-www-form-urlencoded,multipart/form-data,application/json,text/xml)

    四种常见的 POST 提交数据方式(application/x-www-form-urlencoded,multipart/form-data,application/json,text/xml) 转 ...

  5. 我的vim插件配置

    set nocompatible " be iMproved, required filetype off " required " set the runtime pa ...

  6. Linux之常用命令【service】

    补充说明 service命令 是Redhat Linux兼容的发行版中用来控制系统服务的实用工具,它以启动.停止.重新启动和关闭系统服务,还可以显示所有系统服务的当前状态. 语法 service(选项 ...

  7. [C++]栈区(栈)与堆区(类链表)[转/摘]

     一.预备知识—程序的内存分配   一个由C/C++编译的程序占用的内存分为以下几个部分    1.栈区(stack)—   由编译器自动分配释放   ,存放函数的参数值,局部变量的值等.其    操 ...

  8. UBUNTU18.04安装网易云音乐并直接图标启动

    这是一个网友改的程序,安装好以后把 ~/.cache/netcase-cloud-music 这个目录删除掉,就可以正常使用了,不用root权限

  9. yolov3实践(二)

    这次给大家带来一个有趣的项目,项目主要是Python写的,基于Keras,backend是tf. 首先,当我们回顾视觉目标检测这个任务时,我们可能纠结于如何使这个项目变得更加work,我理解的更加wo ...

  10. C 语言 register 关键字

    register:这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率.注意是尽可能,不是绝对.你想想,一个CPU 的寄存器也就那么几个或几十个,你要是定义了很 ...