-
-
[原创]AFL源码阅读笔记之gcc与fuzz部分
-
发表于: 2021-2-6 21:12 27538
-
阅读经典的afl源码,对于今后深入学习实践fuzz有非常重要的意义,比如后期可能针对afl进行部分功能的魔改与自定义,或者是自己尝试开发对应的fuzz。
本部分主要是对于整个项目的执行流程进行大体的阅读,目标在于掌握大致的流程与功能,但此部分笔记仍留有一些细节问题,会在后面的笔记/随着本人理解的加深进行补充解释
Writen by: ScUpax0s/初号机
特别感谢Sakura师傅和花花姐姐~
AFL(American Fuzzy Loop)可以说是市面上用的最多的fuzzer之一了,趁着寒假时间从0到1学习并调试一下AFL的源码,对以后的深入研究Fuzz、漏洞挖掘、安全研究进阶很有帮助。
关于如何搭建环境我就不赘述了,可以看:https://xz.aliyun.com/t/4314
alf-gcc是gcc的一个封装(wrapper),能够实现对于一些关键节点进行插桩,从而记录一些程序执行路径之类的信息,方便对程序的一些运行情况进行反馈。
/ Main entry point /
在alf-gcc.c的main中主要有如下三个函数的调用:
Try to find our "fake" GNU assembler in AFL_PATH or at the location derived from argv[0]. If that fails, abort.
这个函数是想通过argv[0](也就是当前文件的路径)来去寻找对应的汇编器as(Linux上as是很常用的一个汇编器,负责把生成的汇编代码翻译到二进制)。
首先获取环境中的 AFL_PATH
变量。如果获取成功,接着通过 alloc_printf
来动态的分配一段空间存储对应的路径。之后检查这段路径是否可访问,如果可访问的话赋值给 as_path
,然后free,return。不可访问的话直接free掉。
如果没有读取成功 AFL_PATH
则通过对argv[0]的匹配,提取当前路径 dir
,然后将 {dir}/afl-as
在可访问情况下赋值给 as_path
,然后free,return。不可访问的话直接free掉。
这个函数主要是来设置 CC 的参数。
首先给 cc_params
分配空间。接下来通过找到最后一个 / 取出此时对应的什么编译器(比如afl-gcc)。将这个名字赋值给 name
首先我们看一下整个函数的结构:
如果是afl-clang开头的话,设置 clang_mode=1
。接下来看究竟是 afl-clang++
还是 afl-clang
。并根据环境变量设置,具体表现为,以 afl-clang
举例。首先获取环境变量 AFL_CC
如果获取到了,那么设置 cc_params[0]=getenv("AFL_CC")
;反过来如果没有获取到 那么直接设置为 "clang"
。cc_params[]
是我们保存编译参数的数组。
如果不是afl-clang开头。并且是Apple平台的话话会进入 #ifdef __APPLE__
接下来我们进入一个While循环。在循环中我们扫描 argv[] 数组。并将参数放入 cc_params
当我们跳出循环后,我们向 cc_params
中加上 -B 以及对应的 as_path。之后检查 clang_mode 如果是clang的话设置:-no-integrated-as
。(加入 cc_params[]
中)
接下来进入多个if中。
接下来对于优化选项进行判断。
alf-fuzz.c一共四舍五入8000行~
首先调用 gettimeofday
获取当前的准确时间。接着用srandom根据这个时间与当前进程的pid做亦或之后设定了种子。保证了随机性
至此,整个启动前的初始化完成,准备进入fuzz主循环
CALIBRATION (only if failed earlier on)阶段
TRIMMING修剪阶段
PERFORMANCE SCORE阶段
SIMPLE BITFLIP (+dictionary construction)阶段
接下来进入一个for循环
这个循环中调用了FLIP_BIT(out_buf, stage_cur)
接下来进入bitflip 2/1
经过一个for循环做bit flip和样例运行。
这一次唯一的不同是是每次连续异或翻转两个bit。
更新stage_finds[STAGE_FLIP2]
与stage_cycles[STAGE_FLIP2]
接下来同样的进入bitflip 4/1
,连续翻转4次
生成Effector map
进入"bitflip 8/8"
阶段
这个阶段中不是通过FILP宏来做翻转,而是直接与0xff做异或。
ARITHMETIC INC/DEC 阶段
本阶段主要做加减变异
"arith 8/8"
以byte为单元变异阶段
否则进入一个for循环进行变异->运行
"arith 16/8"
阶段
"arith 32/8"
阶段
INTERESTING VALUES阶段
本阶段主要做替换变异
DICTIONARY STUFF阶段
本阶段主要基于用户提供的extra来进行一定的变异
"user extras (insert)"
插入阶段
"auto extras (over)"
替换阶段2
本阶段类似于over,只不过用于替换的变成了a_extras[j]
而非extras[j]
接下来到达标签:skip_extras:
,如果我们在不跳至havoc_stage或abandon_entry的情况下来到这里,说明我们已经正确的完成了确定的fuzz(deterministic steps)步骤,我们可以对其进行标记如 .state/ 目录
RANDOM HAVOC(随机毁灭)阶段
本阶段做大范围的随机变异。
SPLICING阶段
本函数用于检测我们在run_target
中运行的文件返回的结果是否是“有趣的”,进而确定是否要在未来的分析时保存或者插入队列中。若需要返回1,否则返回0.
mem++,后移到下一组。
对于我们的test case进行修剪。
Calculate case desirability score to adjust the length of havoc fuzzing.
通过深度来调整
写出修改后的测试用例,运行程序,处理result与错误等。
#ifdef AFL_LIB
#else
#endif / ^AFL_LIB /
本函数主要用来检测对于asan的一些设置。
设置共享内存和virgin_bits。
首先需要注意两个数组:
最后判断 trace_bits 有效性。
```c
static const u8 count_class_lookup8[256] = {
};
static u16 count_class_lookup16[65536];
EXP_ST void init_count_class16(void) {
}
注意,以上的操作除了特殊说明,如果创建,打开失败,均abort
本函数从输入(input)读取测试用例并入队,在启动时调用。
在函数开头首先定义了
而struct dirent如下:
进行队列改组。
调用:shuffle_ptrs((void **) nl, nl_cnt)
其中UR是产生一个0 -(limit-1)的随机数。
在i到cnt-1内随机产生一个索引下标j。将数组元素nl[i]
与 nl[j]
位置互换。
个人理解是对于input-qeue做了一个随机化or扰动。
maybe_add_auto(tmp, len),此时tmp(mem)对应的是读取的auto extra文件,len对应长度。
首先定义:
调用如下:
本函数的作用是当恢复本地会话时没有使用-t进行设置时防止不停的调整超时时间。
本函数负责扫描每一个argv[i]是否有@@
子串,如果有就进行替换。
对所有测试用例执行试运行,以确认该应用程序按照预期正常运行。仅对初始输入执行此操作,并且仅执行一次。
本函数对我们所有的输入文件做了评估并将testcase多次运行,并且当发现有新的bits/路径产生时评估此文件是否是variable(通过多次运行看是否产生路径变化?)
测试用例校准 calibrate_case(argv, q, use_mem, 0, 1)
use_mem为测试用例的内容(可执行文件)
Linux 的进程间通信:管道
本函数用来初始化/启动forkserver。首先有如下定义:
在子进程中(forkserver)
将FORKSRV_FD重定向到ctl_pipe[0]。将FORKSRV_FD + 1重定向到st_pipe[1]。
接下来关闭子进程中一些描述符。
Check if the current execution path brings anything new to the table.
Update virgin bits to reflect the finds. Returns 1 if the only change is
the hit-count for a particular tuple; 2 if there are new tuples seen.
Updates the map, so subsequent calls will always return 0.
首先有如下定义:
Write modified data to file for testing. If out_file is set, the old file
is unlinked and a new one is created. Otherwise, out_fd is rewound and
truncated.
Execute target application, monitoring for timeouts. Return status
information. The called program will update trace_bits[].
调用如下fault = run_target(argv, use_tmout);
return FAULT_NONE
结束
classify_counts((u64 *) trace_bits)
调用:count_bytes(trace_bits)
实现如下:
其实就是四个一组,扫描哪个字节不为0。若扫描到不为0的就ret++;最后返回ret。
当我们碰到一个新路径时,判断此路径是否是更有利的。即是否是能遍历到bitmap中的bit的最小的路径集合。
https://blog.csdn.net/La745739773/article/details/89604412
对src进行压缩,压缩到原来的1/8,
static void check_map_coverage(void) {
}
alloc_printf通过snprintf的一个小陷阱来做动态分配,说起来去年校赛好像学长还出了个snprintf的题(笑)。大概就是说,第一个snprintnf那里size=0,那么返回值此时是 “如果在理想情况下,字符串需要的长度”。相当于获取了一下length,接下来再通过获取的lenngth分配空间,然后二次调用snprintf把字符串放过去。
char *strchr(const char *str, int c)
该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。
int sigaction( int sig,const struct sigaction * act,struct sigaction * oact );
Examine or specify the action associated with a signal。检查并指定对应信号的行为。
https://blog.csdn.net/tigerjibo/article/details/11712039
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
The sigaction structure specifies how to handle a signal.
首先展示两个关键的static struct extra_data *
extra_data原型如下:
保存了一条token(语料)的值,大小,以及语料库中的使用次数。
《100个gcc小技巧》
https://www.anquanke.com/post/id/201760#h2-4
http://www.qnx.com/developers/docs/6.5.0SP1.update/com.qnx.doc.neutrino_lib_ref/s/sigaction_struct.html
https://chromium.googlesource.com/chromium/src/+/master/docs/asan.md
AFL(American Fuzzy Lop)实现细节与文件变异
Linux错误代码含义
AFL Reading Notes 2: Virgin Bits, Calibration and Queue Culling
- AFL源码阅读笔记之gcc与fuzz部分
- 开始之前
- alf-gcc.c(a GCC wrapper)
- 概述
- main函数
- find_as
- edit_params
- alf-fuzz.c(Main fuzz .c file)
- Main函数
- fuzz主循环中的关键函数
- fuzz_one(char **argv)
- sync_fuzzers(char **argv)
- save_if_interesting(char **argv, void *mem, u32 len, u8 fault)
- simplify_trace(u64 *mem)
- trim_case(char **argv, struct queue_entry *q, u8 *in_buf)
- write_with_gap(void *mem, u32 len, u32 skip_at, u32 skip_len)
- calculate_score(struct queue_entry *q)
- common_fuzz_stuff(char **argv, u8 *out_buf, u32 len)
- 开始fuzz之前的一些关键函数
- setup_signal_handlers()
- define EXP_ST
- define EXP_ST static
- check_asan_opts()
- setup_shm()
- init_count_class16()
- read_testcases()
- shuffle_ptrs()
- load_auto()
- maybe_add_auto(u8 *mem, u32 len)
- pivot_inputs()
- link_or_copy(u8 *old_path, u8 *new_path)
- mark_as_det_done(struct queue_entry *q)
- load_extras(u8 *dir)
- find_timeout(void)
- detect_file_args(char **argv)
- perform_dry_run(use_argv)
- calibrate_case(char **argv, struct queue_entry *q, u8 *use_mem,u32 handicap, u8 from_queue)
- init_forkserver(char **argv)
- has_new_bits(u8 *virgin_map)
- write_to_testcase(void *mem, u32 len)
- run_target(char **argv, u32 timeout)
- classify_counts(u64 *mem)
- count_bytes(u8 *mem)
- update_bitmap_score(struct queue_entry *q)
- minimize_bits(u8 *dst, u8 *src)
- check_map_coverage()
- mark_as_redundant(struct queue_entry *q, u8 state)
- write_stats_file(double bitmap_cvg, double stability, double eps)
- 附录
- 一些参数/名词
- GCC之fsanitize
- GCC之FORTIFY_SOURCE
- ASAN
- 一些函数/宏
- UR(u32 limit)
- alloc_printf
- strrchr
- sigaction()
- access
- ck_read/ck_write
- DMS
- 一些结构体
- struct sigaction
- struct queue_entry(entry in fuzz queue )
- static struct extra_data
- REF
if
(!strncmp(name,
"afl-clang"
,
9
)){
}
else
{
#ifdef __APPLE__
......
#else
......
#endif /* __APPLE__ */
}
while
(argc
-
-
){
}
if
(getenv(
"AFL_HARDEN"
)){
}
if
(asan_set) {
}
else
if
(getenv(
"AFL_USE_ASAN"
)) {
}
else
if
(getenv(
"AFL_USE_MSAN"
)) {
}
if
(!getenv(
"AFL_DONT_OPTIMIZE"
)) {
}
if
(getenv(
"AFL_NO_BUILTIN"
)) {
}
/
/
over :
-
)
if
(!strncmp(name,
"afl-clang"
,
9
)){
}
else
{
#ifdef __APPLE__
......
#else
......
#endif /* __APPLE__ */
}
while
(argc
-
-
){
}
if
(getenv(
"AFL_HARDEN"
)){
}
if
(asan_set) {
}
else
if
(getenv(
"AFL_USE_ASAN"
)) {
}
else
if
(getenv(
"AFL_USE_MSAN"
)) {
}
if
(!getenv(
"AFL_DONT_OPTIMIZE"
)) {
}
if
(getenv(
"AFL_NO_BUILTIN"
)) {
}
/
/
over :
-
)
stage_short
=
"flip1"
;
stage_name
=
"bitflip 1/1"
;
stage_short
=
"flip1"
;
stage_name
=
"bitflip 1/1"
;
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
stage_cur_byte
=
stage_cur >>
3
;
FLIP_BIT(out_buf, stage_cur);
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
FLIP_BIT(out_buf, stage_cur);
.......
}
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
stage_cur_byte
=
stage_cur >>
3
;
FLIP_BIT(out_buf, stage_cur);
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
FLIP_BIT(out_buf, stage_cur);
.......
}
#define FLIP_BIT(_ar, _b) do { \
u8
*
_arf
=
(u8
*
)(_ar); \
u32 _bf
=
(_b); \
_arf[(_bf) >>
3
] ^
=
(
128
>> ((_bf) &
7
)); \
}
while
(
0
)
#define FLIP_BIT(_ar, _b) do { \
u8
*
_arf
=
(u8
*
)(_ar); \
u32 _bf
=
(_b); \
_arf[(_bf) >>
3
] ^
=
(
128
>> ((_bf) &
7
)); \
}
while
(
0
)
stage_name
=
"bitflip 2/1"
;
stage_short
=
"flip2"
;
stage_max
=
(
len
<<
3
)
-
1
;
orig_hit_cnt
=
new_hit_cnt;
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
stage_cur_byte
=
stage_cur >>
3
;
FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur
+
1
);
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur
+
1
);
}
stage_name
=
"bitflip 2/1"
;
stage_short
=
"flip2"
;
stage_max
=
(
len
<<
3
)
-
1
;
orig_hit_cnt
=
new_hit_cnt;
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
stage_cur_byte
=
stage_cur >>
3
;
FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur
+
1
);
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur
+
1
);
}
/
*
Effector
map
setup. These macros calculate:
EFF_APOS
-
position of a particular
file
offset
in
the
map
.
EFF_ALEN
-
length of a
map
with a particular number of bytes.
EFF_SPAN_ALEN
-
map
span
for
a sequence of bytes.
*
/
#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2)
#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1))
#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l))
#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1)
/
*
Effector
map
setup. These macros calculate:
EFF_APOS
-
position of a particular
file
offset
in
the
map
.
EFF_ALEN
-
length of a
map
with a particular number of bytes.
EFF_SPAN_ALEN
-
map
span
for
a sequence of bytes.
*
/
#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2)
#define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1))
#define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l))
#define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1)
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
stage_cur_byte
=
stage_cur;
out_buf[stage_cur] ^
=
0xFF
;
/
/
直接通过对于out_buf的每一个字节中的每一个bit做异或翻转。
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
/
/
运行对应的test case(翻转后)
if
(!eff_map[EFF_APOS(stage_cur)]) {
/
/
如果eff_map[stage_cur>>
3
]为
0
的话
/
/
EFF_APOS宏也起到了一个将stage_cur>>
3
的效果
u32 cksum;
/
*
If
in
dumb mode
or
if
the
file
is
very short, just flag everything
without wasting time on checksums.
*
/
if
(!dumb_mode &&
len
>
=
EFF_MIN_LEN)
/
/
如果不是dumb_mode且
len
大于最小的EFF_MIN_LEN
cksum
=
hash32(trace_bits, MAP_SIZE, HASH_CONST);
/
/
计算hash32
else
/
/
否则,如果是dumb mode或者
len
过小
cksum
=
~queue_cur
-
>exec_cksum;
if
(cksum !
=
queue_cur
-
>exec_cksum) {
eff_map[EFF_APOS(stage_cur)]
=
1
;
/
/
产生新的路径,发生了变化,此时直接将对应的eff_map中的项标记为
1
eff_cnt
+
+
;
}
}
out_buf[stage_cur] ^
=
0xFF
;
/
/
从新异或回来
}
for
(stage_cur
=
0
; stage_cur < stage_max; stage_cur
+
+
) {
stage_cur_byte
=
stage_cur;
out_buf[stage_cur] ^
=
0xFF
;
/
/
直接通过对于out_buf的每一个字节中的每一个bit做异或翻转。
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
/
/
运行对应的test case(翻转后)
if
(!eff_map[EFF_APOS(stage_cur)]) {
/
/
如果eff_map[stage_cur>>
3
]为
0
的话
/
/
EFF_APOS宏也起到了一个将stage_cur>>
3
的效果
u32 cksum;
/
*
If
in
dumb mode
or
if
the
file
is
very short, just flag everything
without wasting time on checksums.
*
/
if
(!dumb_mode &&
len
>
=
EFF_MIN_LEN)
/
/
如果不是dumb_mode且
len
大于最小的EFF_MIN_LEN
cksum
=
hash32(trace_bits, MAP_SIZE, HASH_CONST);
/
/
计算hash32
else
/
/
否则,如果是dumb mode或者
len
过小
cksum
=
~queue_cur
-
>exec_cksum;
if
(cksum !
=
queue_cur
-
>exec_cksum) {
eff_map[EFF_APOS(stage_cur)]
=
1
;
/
/
产生新的路径,发生了变化,此时直接将对应的eff_map中的项标记为
1
eff_cnt
+
+
;
}
}
out_buf[stage_cur] ^
=
0xFF
;
/
/
从新异或回来
}
-
如果设置了```no_arith
-
如果设置了```no_arith
#define ARITH_MAX 35
#define ARITH_MAX 35
u8 orig
=
out_buf[i];
.......
for
(j
=
1
; j <
=
ARITH_MAX; j
+
+
) {
/
/
依次扫描orig到orig
+
35
u8 r
=
orig ^(orig
+
j);
/
/
将orig与orig
+
j(j最大为
35
)进行异或翻转
/
*
Do arithmetic operations only
if
the result couldn't be a product
of a bitflip.
*
/
if
(!could_be_bitflip(r)) {
/
/
判断是否为可以通过上一阶段bitfilp得到的(这一步是为了防止相同的冗余变异,节省时间)
stage_cur_val
=
j;
out_buf[i]
=
orig
+
j;
/
/
将out_buf[i]本身加j变异
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
/
/
进行fuzz
stage_cur
+
+
;
}
else
stage_max
-
-
;
/
/
否则stage_max减
1
r
=
orig ^ (orig
-
j);
/
/
将orig与orig
-
j(j最大为
35
)进行异或翻转
if
(!could_be_bitflip(r)) {
/
/
如果判断为可以bitfilp
stage_cur_val
=
-
j;
out_buf[i]
=
orig
-
j;
/
/
将out_buf[i]本身减j变异
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
/
/
进行fuzz
stage_cur
+
+
;
}
else
stage_max
-
-
;
out_buf[i]
=
orig;
}
u8 orig
=
out_buf[i];
.......
for
(j
=
1
; j <
=
ARITH_MAX; j
+
+
) {
/
/
依次扫描orig到orig
+
35
u8 r
=
orig ^(orig
+
j);
/
/
将orig与orig
+
j(j最大为
35
)进行异或翻转
/
*
Do arithmetic operations only
if
the result couldn't be a product
of a bitflip.
*
/
if
(!could_be_bitflip(r)) {
/
/
判断是否为可以通过上一阶段bitfilp得到的(这一步是为了防止相同的冗余变异,节省时间)
stage_cur_val
=
j;
out_buf[i]
=
orig
+
j;
/
/
将out_buf[i]本身加j变异
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
/
/
进行fuzz
stage_cur
+
+
;
}
else
stage_max
-
-
;
/
/
否则stage_max减
1
r
=
orig ^ (orig
-
j);
/
/
将orig与orig
-
j(j最大为
35
)进行异或翻转
if
(!could_be_bitflip(r)) {
/
/
如果判断为可以bitfilp
stage_cur_val
=
-
j;
out_buf[i]
=
orig
-
j;
/
/
将out_buf[i]本身减j变异
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
/
/
进行fuzz
stage_cur
+
+
;
}
else
stage_max
-
-
;
out_buf[i]
=
orig;
}
memcpy(out_buf
+
i, extras[j].data, last_len);
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
memcpy(out_buf
+
i, extras[j].data, last_len);
if
(common_fuzz_stuff(argv, out_buf,
len
)) goto abandon_entry;
-
首先```total_tmouts```计数加一。
-
如果```nique_hangs >
=
KEEP_UNIQUE_HANG```那么直接返回keeping
-
如果不是```dumb_mode
-
首先```total_tmouts```计数加一。
-
如果```nique_hangs >
=
KEEP_UNIQUE_HANG```那么直接返回keeping
-
如果不是```dumb_mode
-
```keep_as_crash:
-
```keep_as_crash:
/
*
Destructively simplify trace by eliminating hit count information
and
replacing it with
0x80
or
0x01
depending on whether the
tuple
is
hit
or
not
. Called on every new crash
or
timeout, should be
reasonably fast.
*
/
static const u8 simplify_lookup[
256
]
=
{
[
0
]
=
1
,
[
1
...
255
]
=
128
};
/
*
Destructively simplify trace by eliminating hit count information
and
replacing it with
0x80
or
0x01
depending on whether the
tuple
is
hit
or
not
. Called on every new crash
or
timeout, should be
reasonably fast.
*
/
static const u8 simplify_lookup[
256
]
=
{
[
0
]
=
1
,
[
1
...
255
]
=
128
};
static u8 tmp[
64
];
static u8 clean_trace[MAP_SIZE];
u8 needs_write
=
0
, fault
=
0
;
u32 trim_exec
=
0
;
u32 remove_len;
u32 len_p2;
static u8 tmp[
64
];
static u8 clean_trace[MAP_SIZE];
u8 needs_write
=
0
, fault
=
0
;
u32 trim_exec
=
0
;
u32 remove_len;
u32 len_p2;
static void write_with_gap(void
*
mem, u32
len
, u32 skip_at, u32 skip_len) {
s32 fd
=
out_fd;
u32 tail_len
=
len
-
skip_at
-
skip_len;
if
(out_file) {
unlink(out_file);
/
*
Ignore errors.
*
/
fd
=
open
(out_file, O_WRONLY | O_CREAT | O_EXCL,
0600
);
if
(fd <
0
) PFATAL(
"Unable to create '%s'"
, out_file);
}
else
lseek(fd,
0
, SEEK_SET);
if
(skip_at) ck_write(fd, mem, skip_at, out_file);
if
(tail_len) ck_write(fd, mem
+
skip_at
+
skip_len, tail_len, out_file);
if
(!out_file) {
if
(ftruncate(fd,
len
-
skip_len)) PFATAL(
"ftruncate() failed"
);
lseek(fd,
0
, SEEK_SET);
}
else
close(fd);
}
static void write_with_gap(void
*
mem, u32
len
, u32 skip_at, u32 skip_len) {
s32 fd
=
out_fd;
u32 tail_len
=
len
-
skip_at
-
skip_len;
if
(out_file) {
unlink(out_file);
/
*
Ignore errors.
*
/
fd
=
open
(out_file, O_WRONLY | O_CREAT | O_EXCL,
0600
);
if
(fd <
0
) PFATAL(
"Unable to create '%s'"
, out_file);
}
else
lseek(fd,
0
, SEEK_SET);
if
(skip_at) ck_write(fd, mem, skip_at, out_file);
if
(tail_len) ck_write(fd, mem
+
skip_at
+
skip_len, tail_len, out_file);
if
(!out_file) {
if
(ftruncate(fd,
len
-
skip_len)) PFATAL(
"ftruncate() failed"
);
lseek(fd,
0
, SEEK_SET);
}
else
close(fd);
}
if
(q
-
>exec_us
*
0.1
> avg_exec_us) perf_score
=
10
;
else
if
(q
-
>exec_us
*
0.25
> avg_exec_us) perf_score
=
25
;
else
if
(q
-
>exec_us
*
0.5
> avg_exec_us) perf_score
=
50
;
else
if
(q
-
>exec_us
*
0.75
> avg_exec_us) perf_score
=
75
;
else
if
(q
-
>exec_us
*
4
< avg_exec_us) perf_score
=
300
;
else
if
(q
-
>exec_us
*
3
< avg_exec_us) perf_score
=
200
;
else
if
(q
-
>exec_us
*
2
< avg_exec_us) perf_score
=
150
;
if
(q
-
>exec_us
*
0.1
> avg_exec_us) perf_score
=
10
;
else
if
(q
-
>exec_us
*
0.25
> avg_exec_us) perf_score
=
25
;
else
if
(q
-
>exec_us
*
0.5
> avg_exec_us) perf_score
=
50
;
else
if
(q
-
>exec_us
*
0.75
> avg_exec_us) perf_score
=
75
;
else
if
(q
-
>exec_us
*
4
< avg_exec_us) perf_score
=
300
;
else
if
(q
-
>exec_us
*
3
< avg_exec_us) perf_score
=
200
;
else
if
(q
-
>exec_us
*
2
< avg_exec_us) perf_score
=
150
;
if
(q
-
>bitmap_size
*
0.3
> avg_bitmap_size) perf_score
*
=
3
;
else
if
(q
-
>bitmap_size
*
0.5
> avg_bitmap_size) perf_score
*
=
2
;
else
if
(q
-
>bitmap_size
*
0.75
> avg_bitmap_size) perf_score
*
=
1.5
;
else
if
(q
-
>bitmap_size
*
3
< avg_bitmap_size) perf_score
*
=
0.25
;
else
if
(q
-
>bitmap_size
*
2
< avg_bitmap_size) perf_score
*
=
0.5
;
else
if
(q
-
>bitmap_size
*
1.5
< avg_bitmap_size) perf_score
*
=
0.75
;
if
(q
-
>bitmap_size
*
0.3
> avg_bitmap_size) perf_score
*
=
3
;
else
if
(q
-
>bitmap_size
*
0.5
> avg_bitmap_size) perf_score
*
=
2
;
else
if
(q
-
>bitmap_size
*
0.75
> avg_bitmap_size) perf_score
*
=
1.5
;
else
if
(q
-
>bitmap_size
*
3
< avg_bitmap_size) perf_score
*
=
0.25
;
else
if
(q
-
>bitmap_size
*
2
< avg_bitmap_size) perf_score
*
=
0.5
;
else
if
(q
-
>bitmap_size
*
1.5
< avg_bitmap_size) perf_score
*
=
0.75
;
switch (q
-
>depth) {
case
0
...
3
:
break
;
case
4
...
7
:
perf_score
*
=
2
;
break
;
case
8
...
13
:
perf_score
*
=
3
;
break
;
case
14
...
25
:
perf_score
*
=
4
;
break
;
default:
perf_score
*
=
5
;
}
switch (q
-
>depth) {
case
0
...
3
:
break
;
case
4
...
7
:
perf_score
*
=
2
;
break
;
case
8
...
13
:
perf_score
*
=
3
;
break
;
case
14
...
25
:
perf_score
*
=
4
;
break
;
default:
perf_score
*
=
5
;
}
-
首先我们定义一个 ```struct sigaction sa``` 用于储存信号相关的信息。接着我们设置 ```sa.sa_handler
=
handle_stop_sig``` ,然后使用 ```sigaction()``` 函数指定,当 ```SIGHUP,SIGINT,SIGTERM``` 信号来临时,信号处理函数是:```sa.sa_handler
=
handle_stop_sig```。其实就是指定了当停止是调用的 handler。
-
主要处理包括 cirl
-
C之类的操作。
-
设置stop_soon
=
1
-
杀死 child_pid 和 forksrv_pid 对应的进程。
-
SIGALRM:指定timeout的handler为 ```handle_timeout``` 函数。
-
当处于主进程时杀死子进程
-
child_pid
=
=
-
1
且 forksrv_pid >
0
时杀死forkserver进程。(
-
1
是定义的初始值,不是返回值)
-
均设置child_timed_out
=
1
。
-
SIGWINCH:Window resize为```handle_resize
-
首先我们定义一个 ```struct sigaction sa``` 用于储存信号相关的信息。接着我们设置 ```sa.sa_handler
=
handle_stop_sig``` ,然后使用 ```sigaction()``` 函数指定,当 ```SIGHUP,SIGINT,SIGTERM``` 信号来临时,信号处理函数是:```sa.sa_handler
=
handle_stop_sig```。其实就是指定了当停止是调用的 handler。
-
主要处理包括 cirl
-
C之类的操作。
-
设置stop_soon
=
1
-
杀死 child_pid 和 forksrv_pid 对应的进程。
-
SIGALRM:指定timeout的handler为 ```handle_timeout``` 函数。
-
当处于主进程时杀死子进程
-
child_pid
=
=
-
1
且 forksrv_pid >
0
时杀死forkserver进程。(
-
1
是定义的初始值,不是返回值)
-
均设置child_timed_out
=
1
。
-
SIGWINCH:Window resize为```handle_resize
virgin_tmout[MAP_SIZE],
/
*
Bits we haven't seen
in
tmouts
*
/
virgin_crash[MAP_SIZE];
/
*
Bits we haven't seen
in
crashes
*
/
virgin_tmout[MAP_SIZE],
/
*
Bits we haven't seen
in
tmouts
*
/
virgin_crash[MAP_SIZE];
/
*
Bits we haven't seen
in
crashes
*
/
[
0
]
=
0
,
/
/
0
[
1
]
=
1
,
/
/
1
[
2
]
=
2
,
/
/
2
[
3
]
=
4
,
/
/
3
[
4
...
7
]
=
8
,
/
/
4
[
8
...
15
]
=
16
,
/
/
5
[
16
...
31
]
=
32
,
/
/
6
[
32
...
127
]
=
64
,
/
/
7
[
128
...
255
]
=
128
/
/
8
[
0
]
=
0
,
/
/
0
[
1
]
=
1
,
/
/
1
[
2
]
=
2
,
/
/
2
[
3
]
=
4
,
/
/
3
[
4
...
7
]
=
8
,
/
/
4
[
8
...
15
]
=
16
,
/
/
5
[
16
...
31
]
=
32
,
/
/
6
[
32
...
127
]
=
64
,
/
/
7
[
128
...
255
]
=
128
/
/
8
u32 b1, b2;
for
(b1
=
0
; b1 <
256
; b1
+
+
)
for
(b2
=
0
; b2 <
256
; b2
+
+
)
count_class_lookup16[(b1 <<
8
)
+
b2]
=
(count_class_lookup8[b1] <<
8
) |
count_class_lookup8[b2];
u32 b1, b2;
for
(b1
=
0
; b1 <
256
; b1
+
+
)
for
(b2
=
0
; b2 <
256
; b2
+
+
)
count_class_lookup16[(b1 <<
8
)
+
b2]
=
(count_class_lookup8[b1] <<
8
) |
count_class_lookup8[b2];
本函数用来初始化 ```u16 count_class_lookup16[
65536
]``` 这个数组。
-
将整个 count_class_lookup16 分成
256
段,每一段
256
份儿。初始化的时候利用了 count_class_lookup8。
-
count_class_lookup8中对于执行次数进行了规整,比如执行了
4
-
7
次的其计数为
8
,比如
32
次到
127
次都会认为是
64
次。
-
变量 ```trace_bits``` 来记录分支执行次数,而count_class_lookup8实际就是对于```trace_bits```的规整。
-
而初始化 count_class_lookup16 实际是因为 AFL 中对于一条分支径的表示是由一个二元组来表示的。
-
例如:```A
-
>B
-
>C
-
>D
-
>A
-
B```, 可以用[A,B] [B,C] [C,D] [D,A]四个二元组表示,只需要记录跳转的源地址和目标地址。并且[A,B]执行了两次,其余执行了一次,这里用
hash
映射在一张
map
中。
-
而基于这种二元组的表示的效率考虑,又使用了```u16 count_class_lookup16[
65536
]``` 这个数组,并在此初始化。
在网上找到这样一段解释我觉得很好:
*
这样处理之后,对分支执行次数就会有一个简单的归类。例如,如果对某个测试用例处理时,分支A执行了
32
次;对另外一个测试用例,分支A执行了
33
次,那么AFL就会认为这两次的
*
*
代码覆盖
*
*
是相同的。当然,这样的简单分类肯定不能区分所有的情况,不过在某种程度上,处理了一些因为循环次数的微小区别,而误判为不同执行结果的情况。
*
### setup_dirs_fds()
https:
/
/
www.cnblogs.com
/
xiaofeiIDO
/
p
/
6695459.html
设置输出的文件夹与fd。
-
如果设置了sync_id且通过mkdir(sync_dir,
0700
)创建了对应的文件夹失败,并且errno !
=
EEXIST时,abort
-
如果没设置sync_id,尝试创建了sync_dir,创建失败,且errno !
=
EEXIST时abort。
-
如果创建sync_dir失败,且errno
=
EEXIST。调用maybe_delete_out_dir()收尾。
-
如果创建成功。
-
设置了in_place_resume,则abort
-
否则调用
open
以读权限打开,返回fd,赋值给out_dir_fd。
-
接下来将out_dir与
/
queue拼接为tmp。(output
/
queue)
-
创建tmp文件夹。
-
创建output
/
queue
/
.state
/
(Top
-
level directory
for
queue metadata used
for
session resume
and
related tasks)
-
创建output
/
queue
/
.state
/
deterministic_done
/
(Directory
for
flagging queue entries that went through deterministic fuzzing
in
the past.)
-
创建output
/
queue
/
.state
/
auto_extras
/
(Directory with the auto
-
selected dictionary entries)
-
创建output
/
queue
/
.state
/
redundant_edges
/
(The
set
of paths currently deemed redundant)
-
创建output
/
queue
/
.state
/
variable_behavior
/
(The
set
of paths showing variable behavior)
-
接下来做目录的同步,用于追踪共同工作的fuzzer
-
如果设置了sync_id。那么创建output
/
.synced
/
-
*
*
创建output
/
crashes,记录crash样本。
*
*
(
All
recorded crashes)
-
创建output
/
hangs(
All
recorded hangs.)
-
打开
/
dev
/
null与
/
dev
/
urandom,fd为dev_null_fd和dev_urandom_fd。
-
创建output
/
plot_data,通过```
open
(tmp, O_WRONLY | O_CREAT | O_EXCL,
0600
)``` 打开返回fd。
-
通过fdopen将fd转为文件指针plot_file,通过fprintf将一些信息写入文件。忽略错误。
```c
fprintf(plot_file,
"# unix_time, cycles_done, cur_path, paths_total, "
"pending_total, pending_favs, map_size, unique_crashes, "
"unique_hangs, max_depth, execs_per_sec\n"
);
本函数用来初始化 ```u16 count_class_lookup16[
65536
]``` 这个数组。
-
将整个 count_class_lookup16 分成
256
段,每一段
256
份儿。初始化的时候利用了 count_class_lookup8。
-
count_class_lookup8中对于执行次数进行了规整,比如执行了
4
-
7
次的其计数为
8
,比如
32
次到
127
次都会认为是
64
次。
-
变量 ```trace_bits``` 来记录分支执行次数,而count_class_lookup8实际就是对于```trace_bits```的规整。
-
而初始化 count_class_lookup16 实际是因为 AFL 中对于一条分支径的表示是由一个二元组来表示的。
-
例如:```A
-
>B
-
>C
-
>D
-
>A
-
B```, 可以用[A,B] [B,C] [C,D] [D,A]四个二元组表示,只需要记录跳转的源地址和目标地址。并且[A,B]执行了两次,其余执行了一次,这里用
hash
映射在一张
map
中。
-
而基于这种二元组的表示的效率考虑,又使用了```u16 count_class_lookup16[
65536
]``` 这个数组,并在此初始化。
在网上找到这样一段解释我觉得很好:
*
这样处理之后,对分支执行次数就会有一个简单的归类。例如,如果对某个测试用例处理时,分支A执行了
32
次;对另外一个测试用例,分支A执行了
33
次,那么AFL就会认为这两次的
*
*
代码覆盖
*
*
是相同的。当然,这样的简单分类肯定不能区分所有的情况,不过在某种程度上,处理了一些因为循环次数的微小区别,而误判为不同执行结果的情况。
*
### setup_dirs_fds()
https:
/
/
www.cnblogs.com
/
xiaofeiIDO
/
p
/
6695459.html
设置输出的文件夹与fd。
-
如果设置了sync_id且通过mkdir(sync_dir,
0700
)创建了对应的文件夹失败,并且errno !
=
EEXIST时,abort
-
如果没设置sync_id,尝试创建了sync_dir,创建失败,且errno !
=
EEXIST时abort。
-
如果创建sync_dir失败,且errno
=
EEXIST。调用maybe_delete_out_dir()收尾。
-
如果创建成功。
-
设置了in_place_resume,则abort
-
否则调用
open
以读权限打开,返回fd,赋值给out_dir_fd。
-
接下来将out_dir与
/
queue拼接为tmp。(output
/
queue)
-
创建tmp文件夹。
-
创建output
/
queue
/
.state
/
(Top
-
level directory
for
queue metadata used
for
session resume
and
related tasks)
-
创建output
/
queue
/
.state
/
deterministic_done
/
(Directory
for
flagging queue entries that went through deterministic fuzzing
in
the past.)
-
创建output
/
queue
/
.state
/
auto_extras
/
(Directory with the auto
-
selected dictionary entries)
-
创建output
/
queue
/
.state
/
redundant_edges
/
(The
set
of paths currently deemed redundant)
-
创建output
/
queue
/
.state
/
variable_behavior
/
(The
set
of paths showing variable behavior)
-
接下来做目录的同步,用于追踪共同工作的fuzzer
-
如果设置了sync_id。那么创建output
/
.synced
/
-
*
*
创建output
/
crashes,记录crash样本。
*
*
(
All
recorded crashes)
-
创建output
/
hangs(
All
recorded hangs.)
-
打开
/
dev
/
null与
/
dev
/
urandom,fd为dev_null_fd和dev_urandom_fd。
-
创建output
/
plot_data,通过```
open
(tmp, O_WRONLY | O_CREAT | O_EXCL,
0600
)``` 打开返回fd。
-
通过fdopen将fd转为文件指针plot_file,通过fprintf将一些信息写入文件。忽略错误。
```c
fprintf(plot_file,
"# unix_time, cycles_done, cur_path, paths_total, "
"pending_total, pending_favs, map_size, unique_crashes, "
"unique_hangs, max_depth, execs_per_sec\n"
);
struct dirent
*
*
nl;
s32 nl_cnt;
struct dirent
*
*
nl;
s32 nl_cnt;
/
/
Data structure
for
a directory entry
struct dirent
{
long
d_ino;
/
*
inode number 索引节点号
*
/
off_t d_off;
/
*
offset to this dirent 在目录文件中的偏移
*
/
unsigned short d_reclen;
/
*
length of this d_name 文件名长
*
/
unsigned char d_type;
/
*
the
type
of d_name 文件类型
*
/
char d_name [NAME_MAX
+
1
];
/
*
file
name (null
-
terminated) 文件名,最长
255
字符
*
/
}
/
/
Data structure
for
a directory entry
struct dirent
{
long
d_ino;
/
*
inode number 索引节点号
*
/
off_t d_off;
/
*
offset to this dirent 在目录文件中的偏移
*
/
unsigned short d_reclen;
/
*
length of this d_name 文件名长
*
/
unsigned char d_type;
/
*
the
type
of d_name 文件类型
*
/
char d_name [NAME_MAX
+
1
];
/
*
file
name (null
-
terminated) 文件名,最长
255
字符
*
/
}
/
*
Shuffle an array of pointers. Might be slightly biased.
*
/
static void shuffle_ptrs(void
*
*
ptrs, u32 cnt) {
u32 i;
for
(i
=
0
; i < cnt
-
2
; i
+
+
) {
u32 j
=
i
+
UR(cnt
-
i);
void
*
s
=
ptrs[i];
ptrs[i]
=
ptrs[j];
ptrs[j]
=
s;
}
}
/
*
Shuffle an array of pointers. Might be slightly biased.
*
/
static void shuffle_ptrs(void
*
*
ptrs, u32 cnt) {
u32 i;
for
(i
=
0
; i < cnt
-
2
; i
+
+
) {
u32 j
=
i
+
UR(cnt
-
i);
void
*
s
=
ptrs[i];
ptrs[i]
=
ptrs[j];
ptrs[j]
=
s;
}
}
/
*
Interesting values, as per config.h
*
/
static s8 interesting_8[]
=
{INTERESTING_8};
static s16 interesting_16[]
=
{INTERESTING_8, INTERESTING_16};
static s32 interesting_32[]
=
{INTERESTING_8, INTERESTING_16, INTERESTING_32};
static struct extra_data
*
extras;
/
*
Extra tokens to fuzz with
*
/
static u32 extras_cnt;
/
*
Total number of tokens read
*
/
static struct extra_data
*
a_extras;
/
*
Automatically selected extras
*
/
static u32 a_extras_cnt;
/
*
Total number of tokens available
*
/
/
*
Interesting values, as per config.h
*
/
static s8 interesting_8[]
=
{INTERESTING_8};
static s16 interesting_16[]
=
{INTERESTING_8, INTERESTING_16};
static s32 interesting_32[]
=
{INTERESTING_8, INTERESTING_16, INTERESTING_32};
static struct extra_data
*
extras;
/
*
Extra tokens to fuzz with
*
/
static u32 extras_cnt;
/
*
Total number of tokens read
*
/
static struct extra_data
*
a_extras;
/
*
Automatically selected extras
*
/
static u32 a_extras_cnt;
/
*
Total number of tokens available
*
/
struct queue_entry
*
q
=
queue;
/
/
指向
input
case queue 队头
u32
id
=
0
;
#ifndef SIMPLE_FILES
# define CASE_PREFIX "id:"
#else
# define CASE_PREFIX "id_"
#endif /* ^!SIMPLE_FILES */
struct queue_entry
*
q
=
queue;
/
/
指向
input
case queue 队头
u32
id
=
0
;
#ifndef SIMPLE_FILES
# define CASE_PREFIX "id:"
#else
# define CASE_PREFIX "id_"
#endif /* ^!SIMPLE_FILES */
link_or_copy(q
-
>fname, nfn)
/
/
q
-
>fname:
"input/a.out"
/
/
nfn:
"output/queue/id:000000,orig:a.out"
link_or_copy(q
-
>fname, nfn)
/
/
q
-
>fname:
"input/a.out"
/
/
nfn:
"output/queue/id:000000,orig:a.out"
while
((i
=
read(sfd, tmp,
64
*
1024
)) >
0
)
ck_write(dfd, tmp, i, new_path);
while
((i
=
read(sfd, tmp,
64
*
1024
)) >
0
)
ck_write(dfd, tmp, i, new_path);
/
*
Mark deterministic checks as done
for
a particular queue entry. We use the
.state
file
to avoid repeating deterministic fuzzing when resuming aborted
scans.
*
/
static void mark_as_det_done(struct queue_entry
*
q) {
u8
*
fn
=
strrchr(q
-
>fname,
'/'
);
s32 fd;
fn
=
alloc_printf(
"%s/queue/.state/deterministic_done/%s"
, out_dir, fn
+
1
);
fd
=
open
(fn, O_WRONLY | O_CREAT | O_EXCL,
0600
);
/
/
打开fn对应的文件,若没有则创建。
if
(fd <
0
) PFATAL(
"Unable to create '%s'"
, fn);
close(fd);
ck_free(fn);
q
-
>passed_det
=
1
;
/
/
设置queue中这一项的passed_det
=
1
代表已经fuzz过了
}
/
*
Mark deterministic checks as done
for
a particular queue entry. We use the
.state
file
to avoid repeating deterministic fuzzing when resuming aborted
scans.
*
/
static void mark_as_det_done(struct queue_entry
*
q) {
u8
*
fn
=
strrchr(q
-
>fname,
'/'
);
s32 fd;
fn
=
alloc_printf(
"%s/queue/.state/deterministic_done/%s"
, out_dir, fn
+
1
);
fd
=
open
(fn, O_WRONLY | O_CREAT | O_EXCL,
0600
);
/
/
打开fn对应的文件,若没有则创建。
if
(fd <
0
) PFATAL(
"Unable to create '%s'"
, fn);
close(fd);
ck_free(fn);
q
-
>passed_det
=
1
;
/
/
设置queue中这一项的passed_det
=
1
代表已经fuzz过了
}
首先定义:
```c
DIR
*
d;
struct dirent
*
de;
u32 min_len
=
MAX_DICT_FILE, max_len
=
0
, dict_level
=
0
;
u8
*
x;
首先定义:
```c
DIR
*
d;
struct dirent
*
de;
u32 min_len
=
MAX_DICT_FILE, max_len
=
0
, dict_level
=
0
;
u8
*
x;
static u8 tmp[
4096
];
/
*
Ought to be enough
for
anybody.
*
/
u8
*
fn,
*
off;
s32 fd, i;
u32 ret;
static u8 tmp[
4096
];
/
*
Ought to be enough
for
anybody.
*
/
u8
*
fn,
*
off;
s32 fd, i;
u32 ret;
u32 i
=
0
;
u8
*
cwd
=
getcwd(NULL,
0
);
u32 i
=
0
;
u8
*
cwd
=
getcwd(NULL,
0
);
struct queue_entry
*
q
=
queue;
u32 cal_failures
=
0
;
u8
*
skip_crashes
=
getenv(
"AFL_SKIP_CRASHES"
);
struct queue_entry
*
q
=
queue;
u32 cal_failures
=
0
;
u8
*
skip_crashes
=
getenv(
"AFL_SKIP_CRASHES"
);
static u8 first_trace[MAP_SIZE];
u8 fault
=
0
, new_bits
=
0
, var_detected
=
0
, hnb
=
0
,
first_run
=
(q
-
>exec_cksum
=
=
0
);
u64 start_us, stop_us;
s32 old_sc
=
stage_cur, old_sm
=
stage_max;
u32 use_tmout
=
exec_tmout;
u8
*
old_sn
=
stage_name;
static u8 first_trace[MAP_SIZE];
u8 fault
=
0
, new_bits
=
0
, var_detected
=
0
, hnb
=
0
,
first_run
=
(q
-
>exec_cksum
=
=
0
);
u64 start_us, stop_us;
s32 old_sc
=
stage_cur, old_sm
=
stage_max;
u32 use_tmout
=
exec_tmout;
u8
*
old_sn
=
stage_name;
static struct itimerval it;
int
st_pipe[
2
], ctl_pipe[
2
];
int
status;
s32 rlen;
static struct itimerval it;
int
st_pipe[
2
], ctl_pipe[
2
];
int
status;
s32 rlen;
close(ctl_pipe[
0
]);
close(ctl_pipe[
1
]);
close(st_pipe[
0
]);
close(st_pipe[
1
]);
close(out_dir_fd);
close(dev_null_fd);
close(dev_urandom_fd);
close(fileno(plot_file));
close(ctl_pipe[
0
]);
close(ctl_pipe[
1
]);
close(st_pipe[
0
]);
close(st_pipe[
1
]);
close(out_dir_fd);
close(dev_null_fd);
close(dev_urandom_fd);
close(fileno(plot_file));
#ifdef WORD_SIZE_64
u64
*
current
=
(u64
*
) trace_bits;
u64
*
virgin
=
(u64
*
) virgin_map;
u32 i
=
(MAP_SIZE >>
3
);
这里 MAP_SIZE>>
3
代表 MAP_SIZE
/
8
也就是按照
8
个字节一组,一共分了i组 (
64
位实现)
#else
u32
*
current
=
(u32
*
)trace_bits;
u32
*
virgin
=
(u32
*
)virgin_map;
u32 i
=
(MAP_SIZE >>
2
);
这里 MAP_SIZE>>
3
代表 MAP_SIZE
/
4
也就是按照
4
个字节一组,一共分了i组 (
32
位实现)
#endif /* ^WORD_SIZE_64 */
#ifdef WORD_SIZE_64
u64
*
current
=
(u64
*
) trace_bits;
u64
*
virgin
=
(u64
*
) virgin_map;
u32 i
=
(MAP_SIZE >>
3
);
这里 MAP_SIZE>>
3
代表 MAP_SIZE
/
8
也就是按照
8
个字节一组,一共分了i组 (
64
位实现)
#else
u32
*
current
=
(u32
*
)trace_bits;
u32
*
virgin
=
(u32
*
)virgin_map;
u32 i
=
(MAP_SIZE >>
2
);
这里 MAP_SIZE>>
3
代表 MAP_SIZE
/
4
也就是按照
4
个字节一组,一共分了i组 (
32
位实现)
#endif /* ^WORD_SIZE_64 */
if
((cur[
0
] && vir[
0
]
=
=
0xff
) || (cur[
1
] && vir[
1
]
=
=
0xff
) ||
(cur[
2
] && vir[
2
]
=
=
0xff
) || (cur[
3
] && vir[
3
]
=
=
0xff
) ||
(cur[
4
] && vir[
4
]
=
=
0xff
) || (cur[
5
] && vir[
5
]
=
=
0xff
) ||
(cur[
6
] && vir[
6
]
=
=
0xff
) || (cur[
7
] && vir[
7
]
=
=
0xff
))
if
((cur[
0
] && vir[
0
]
=
=
0xff
) || (cur[
1
] && vir[
1
]
=
=
0xff
) ||
(cur[
2
] && vir[
2
]
=
=
0xff
) || (cur[
3
] && vir[
3
]
=
=
0xff
) ||
(cur[
4
] && vir[
4
]
=
=
0xff
) || (cur[
5
] && vir[
5
]
=
=
0xff
) ||
(cur[
6
] && vir[
6
]
=
=
0xff
) || (cur[
7
] && vir[
7
]
=
=
0xff
))
static inline void classify_counts(u64
*
mem) {
u32 i
=
MAP_SIZE >>
3
;
/
/
八个一组,一共i组
while
(i
-
-
) {
/
/
每一组扫描
/
*
Optimize
for
sparse bitmaps.
*
/
if
(unlikely(
*
mem)) {
/
/
如果对应的mem中的值不为
0
u16
*
mem16
=
(u16
*
) mem;
mem16[
0
]
=
count_class_lookup16[mem16[
0
]];
mem16[
1
]
=
count_class_lookup16[mem16[
1
]];
mem16[
2
]
=
count_class_lookup16[mem16[
2
]];
mem16[
3
]
=
count_class_lookup16[mem16[
3
]];
}
mem
+
+
;
}
}
static inline void classify_counts(u64
*
mem) {
u32 i
=
MAP_SIZE >>
3
;
/
/
八个一组,一共i组
while
(i
-
-
) {
/
/
每一组扫描
/
*
Optimize
for
sparse bitmaps.
*
/
if
(unlikely(
*
mem)) {
/
/
如果对应的mem中的值不为
0
u16
*
mem16
=
(u16
*
) mem;
mem16[
0
]
=
count_class_lookup16[mem16[
0
]];
mem16[
1
]
=
count_class_lookup16[mem16[
1
]];
mem16[
2
]
=
count_class_lookup16[mem16[
2
]];
mem16[
3
]
=
count_class_lookup16[mem16[
3
]];
}
mem
+
+
;
}
}
#define FF(_b) (0xff << ((_b) << 3))
/
*
Count the number of bytes
set
in
the bitmap. Called fairly sporadically,
mostly to update the status screen
or
calibrate
and
examine confirmed
new paths.
*
/
static u32 count_bytes(u8
*
mem) {
u32
*
ptr
=
(u32
*
) mem;
/
/
ptr指向trace bits的首位
u32 i
=
(MAP_SIZE >>
2
);
/
/
四个字节一组
u32 ret
=
0
;
/
/
ret计数初始化为
0
while
(i
-
-
) {
/
/
一组一组扫描
u32 v
=
*
(ptr
+
+
);
/
/
v每次取
4
个字节
if
(!v)
continue
;
/
/
如果
4
个字节全部是
0
的话直接跳过这四个字节取下一组。
if
(v & FF(
0
)) ret
+
+
;
/
/
0xff
if
(v & FF(
1
)) ret
+
+
;
/
/
0xff00
if
(v & FF(
2
)) ret
+
+
;
/
/
0xff0000
if
(v & FF(
3
)) ret
+
+
;
/
/
0xff000000
}
return
ret;
}
#define FF(_b) (0xff << ((_b) << 3))
/
*
Count the number of bytes
set
in
the bitmap. Called fairly sporadically,
mostly to update the status screen
or
calibrate
and
examine confirmed
new paths.
*
/
static u32 count_bytes(u8
*
mem) {
u32
*
ptr
=
(u32
*
) mem;
/
/
ptr指向trace bits的首位
u32 i
=
(MAP_SIZE >>
2
);
/
/
四个字节一组
u32 ret
=
0
;
/
/
ret计数初始化为
0
while
(i
-
-
) {
/
/
一组一组扫描
u32 v
=
*
(ptr
+
+
);
/
/
v每次取
4
个字节
if
(!v)
continue
;
/
/
如果
4
个字节全部是
0
的话直接跳过这四个字节取下一组。
if
(v & FF(
0
)) ret
+
+
;
/
/
0xff
if
(v & FF(
1
)) ret
+
+
;
/
/
0xff00
if
(v & FF(
2
)) ret
+
+
;
/
/
0xff0000
if
(v & FF(
3
)) ret
+
+
;
/
/
0xff000000
}
return
ret;
}
u32 i;
u64 fav_factor
=
q
-
>exec_us
*
q
-
>
len
;
u32 i;
u64 fav_factor
=
q
-
>exec_us
*
q
-
>
len
;
/
*
Compact trace bytes into a smaller bitmap. We effectively just drop the
count information here. This
is
called only sporadically,
for
some
new paths.
*
/
static void minimize_bits(u8
*
dst, u8
*
src) {
u32 i
=
0
;
while
(i < MAP_SIZE) {
if
(
*
(src
+
+
)) dst[i >>
3
] |
=
1
<< (i &
7
);
i
+
+
;
}
}
/
*
Compact trace bytes into a smaller bitmap. We effectively just drop the
count information here. This
is
called only sporadically,
for
some
new paths.
*
/
static void minimize_bits(u8
*
dst, u8
*
src) {
u32 i
=
0
;
while
(i < MAP_SIZE) {
if
(
*
(src
+
+
)) dst[i >>
3
] |
=
1
<< (i &
7
);
i
+
+
;
}
}
u32 i;
if
(count_bytes(trace_bits) <
100
)
return
;
for
(i
=
(
1
<< (MAP_SIZE_POW2
-
1
)); i < MAP_SIZE; i
+
+
)
if
(trace_bits[i])
return
;
WARNF(
"Recompile binary with newer version of afl to improve coverage!"
);
u32 i;
if
(count_bytes(trace_bits) <
100
)
return
;
for
(i
=
(
1
<< (MAP_SIZE_POW2
-
1
)); i < MAP_SIZE; i
+
+
)
if
(trace_bits[i])
return
;
WARNF(
"Recompile binary with newer version of afl to improve coverage!"
);
-
利用```count_bytes(trace_bits)```统计发现的路径数量(不为
0
的字节),小于
100
则
return
。
-
扫描trace_bits后半部分,如果有不为零的就返回。
-
否则告诉用户改进覆盖率。
### cull_queue()
用于精简队列。
```c
struct queue_entry
*
q;
static u8 temp_v[MAP_SIZE >>
3
];
/
/
temp_v[
8192
]
u32 i;
-
利用```count_bytes(trace_bits)```统计发现的路径数量(不为
0
的字节),小于
100
则
return
。
-
扫描trace_bits后半部分,如果有不为零的就返回。
-
否则告诉用户改进覆盖率。
### cull_queue()
用于精简队列。
```c
struct queue_entry
*
q;
static u8 temp_v[MAP_SIZE >>
3
];
/
/
temp_v[
8192
]
u32 i;
"start_time : %llu\n"
"last_update : %llu\n"
"fuzzer_pid : %u\n"
"cycles_done : %llu\n"
"execs_done : %llu\n"
"execs_per_sec : %0.02f\n"
"paths_total : %u\n"
"paths_favored : %u\n"
"paths_found : %u\n"
"paths_imported : %u\n"
"max_depth : %u\n"
"cur_path : %u\n"
/
*
Must match find_start_position()
*
/
"pending_favs : %u\n"
"pending_total : %u\n"
"variable_paths : %u\n"
"stability : %0.02f%%\n"
"bitmap_cvg : %0.02f%%\n"
"unique_crashes : %llu\n"
"unique_hangs : %llu\n"
"last_path : %llu\n"
"last_crash : %llu\n"
"last_hang : %llu\n"
"execs_since_crash : %llu\n"
"exec_timeout : %u\n"
/
*
Must match find_timeout()
*
/
"afl_banner : %s\n"
"afl_version : "
VERSION
"\n"
"target_mode : %s%s%s%s%s%s%s\n"
"command_line : %s\n"
"slowest_exec_ms : %llu\n"
,
"start_time : %llu\n"
"last_update : %llu\n"
"fuzzer_pid : %u\n"
"cycles_done : %llu\n"
"execs_done : %llu\n"
"execs_per_sec : %0.02f\n"
"paths_total : %u\n"
"paths_favored : %u\n"
"paths_found : %u\n"
"paths_imported : %u\n"
"max_depth : %u\n"
"cur_path : %u\n"
/
*
Must match find_start_position()
*
/
"pending_favs : %u\n"
"pending_total : %u\n"
"variable_paths : %u\n"
"stability : %0.02f%%\n"
"bitmap_cvg : %0.02f%%\n"
"unique_crashes : %llu\n"
"unique_hangs : %llu\n"
"last_path : %llu\n"
"last_crash : %llu\n"
"last_hang : %llu\n"
"execs_since_crash : %llu\n"
"exec_timeout : %u\n"
/
*
Must match find_timeout()
*
/
"afl_banner : %s\n"
"afl_version : "
VERSION
"\n"
"target_mode : %s%s%s%s%s%s%s\n"
"command_line : %s\n"
"slowest_exec_ms : %llu\n"
,
/
*
Generate a random number (
from
0
to limit
-
1
). This may
have slight bias.
*
/
static inline u32 UR(u32 limit) {
if
(unlikely(!rand_cnt
-
-
)) {
u32 seed[
2
];
ck_read(dev_urandom_fd, &seed, sizeof(seed),
"/dev/urandom"
);
srandom(seed[
0
]);
rand_cnt
=
(RESEED_RNG
/
2
)
+
(seed[
1
]
%
RESEED_RNG);
}
return
random()
%
limit;
}
/
*
Generate a random number (
from
0
to limit
-
1
). This may
have slight bias.
*
/
static inline u32 UR(u32 limit) {
if
(unlikely(!rand_cnt
-
-
)) {
u32 seed[
2
];
ck_read(dev_urandom_fd, &seed, sizeof(seed),
"/dev/urandom"
);
srandom(seed[
0
]);
rand_cnt
=
(RESEED_RNG
/
2
)
+
(seed[
1
]
%
RESEED_RNG);
}
return
random()
%
limit;
}
/
*
User
-
facing macro to sprintf() to a dynamically allocated
buffer
.
*
/
#define alloc_printf(_str...) ({ \
u8
*
_tmp; \
s32 _len
=
snprintf(NULL,
0
, _str); \
if
(_len <
0
) FATAL(
"Whoa, snprintf() fails?!"
); \
_tmp
=
ck_alloc(_len
+
1
); \
snprintf((char
*
)_tmp, _len
+
1
, _str); \
_tmp; \
})
/
*
User
-
facing macro to sprintf() to a dynamically allocated
buffer
.
*
/
#define alloc_printf(_str...) ({ \
u8
*
_tmp; \
s32 _len
=
snprintf(NULL,
0
, _str); \
if
(_len <
0
) FATAL(
"Whoa, snprintf() fails?!"
); \
_tmp
=
ck_alloc(_len
+
1
); \
snprintf((char
*
)_tmp, _len
+
1
, _str); \
_tmp; \
})
EINVAL: 模式值无效
EACCES: 文件或路径名中包含的目录不可访问
ELOOP : 解释路径名过程中存在太多的符号连接
ENAMETOOLONG:路径名太长
ENOENT:路径名中的目录不存在或是无效的符号连接
ENOTDIR: 路径名中当作目录的组件并非目录
EROFS: 文件系统只读
EFAULT: 路径名指向可访问的空间外
EIO:输入输出错误
ENOMEM: 不能获取足够的内核内存
ETXTBSY:对程序写入出错
EINVAL: 模式值无效
EACCES: 文件或路径名中包含的目录不可访问
ELOOP : 解释路径名过程中存在太多的符号连接
ENAMETOOLONG:路径名太长
ENOENT:路径名中的目录不存在或是无效的符号连接
ENOTDIR: 路径名中当作目录的组件并非目录
EROFS: 文件系统只读
EFAULT: 路径名指向可访问的空间外
EIO:输入输出错误
ENOMEM: 不能获取足够的内核内存
ETXTBSY:对程序写入出错
/
*
Error
-
checking versions of read()
and
write() that call RPFATAL() as
appropriate.
*
/
#define ck_write(fd, buf, len, fn) do { \
u32 _len
=
(
len
); \
s32 _res
=
write(fd, buf, _len); \
if
(_res !
=
_len) RPFATAL(_res,
"Short write to %s"
, fn); \
}
while
(
0
)
#define ck_read(fd, buf, len, fn) do { \
u32 _len
=
(
len
); \
s32 _res
=
read(fd, buf, _len); \
if
(_res !
=
_len) RPFATAL(_res,
"Short read from %s"
, fn); \
}
while
(
0
)
/
*
Error
-
checking versions of read()
and
write() that call RPFATAL() as
appropriate.
*
/
#define ck_write(fd, buf, len, fn) do { \
u32 _len
=
(
len
); \
s32 _res
=
write(fd, buf, _len); \
if
(_res !
=
_len) RPFATAL(_res,
"Short write to %s"
, fn); \
}
while
(
0
)
#define ck_read(fd, buf, len, fn) do { \
u32 _len
=
(
len
); \
s32 _res
=
read(fd, buf, _len); \
if
(_res !
=
_len) RPFATAL(_res,
"Short read from %s"
, fn); \
}
while
(
0
)
/
*
Describe integer as memory size.
*
/
static u8
*
DMS(u64 val) {
static u8 tmp[
12
][
16
];
static u8 cur;
cur
=
(cur
+
1
)
%
12
;
/
*
0
-
9999
*
/
CHK_FORMAT(
1
,
10000
,
"%llu B"
, u64);
/
*
10.0k
-
99.9k
*
/
CHK_FORMAT(
1024
,
99.95
,
"%0.01f kB"
, double);
/
*
100k
-
999k
*
/
CHK_FORMAT(
1024
,
1000
,
"%llu kB"
, u64);
/
*
1.00M
-
9.99M
*
/
CHK_FORMAT(
1024
*
1024
,
9.995
,
"%0.02f MB"
, double);
/
*
10.0M
-
99.9M
*
/
CHK_FORMAT(
1024
*
1024
,
99.95
,
"%0.01f MB"
, double);
/
*
100M
-
999M
*
/
CHK_FORMAT(
1024
*
1024
,
1000
,
"%llu MB"
, u64);
/
*
1.00G
-
9.99G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
,
9.995
,
"%0.02f GB"
, double);
/
*
10.0G
-
99.9G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
,
99.95
,
"%0.01f GB"
, double);
/
*
100G
-
999G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
,
1000
,
"%llu GB"
, u64);
/
*
1.00T
-
9.99G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
*
1024
,
9.995
,
"%0.02f TB"
, double);
/
*
10.0T
-
99.9T
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
*
1024
,
99.95
,
"%0.01f TB"
, double);
#undef CHK_FORMAT
/
*
100T
+
*
/
strcpy(tmp[cur],
"infty"
);
return
tmp[cur];
}
/
*
Describe integer as memory size.
*
/
static u8
*
DMS(u64 val) {
static u8 tmp[
12
][
16
];
static u8 cur;
cur
=
(cur
+
1
)
%
12
;
/
*
0
-
9999
*
/
CHK_FORMAT(
1
,
10000
,
"%llu B"
, u64);
/
*
10.0k
-
99.9k
*
/
CHK_FORMAT(
1024
,
99.95
,
"%0.01f kB"
, double);
/
*
100k
-
999k
*
/
CHK_FORMAT(
1024
,
1000
,
"%llu kB"
, u64);
/
*
1.00M
-
9.99M
*
/
CHK_FORMAT(
1024
*
1024
,
9.995
,
"%0.02f MB"
, double);
/
*
10.0M
-
99.9M
*
/
CHK_FORMAT(
1024
*
1024
,
99.95
,
"%0.01f MB"
, double);
/
*
100M
-
999M
*
/
CHK_FORMAT(
1024
*
1024
,
1000
,
"%llu MB"
, u64);
/
*
1.00G
-
9.99G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
,
9.995
,
"%0.02f GB"
, double);
/
*
10.0G
-
99.9G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
,
99.95
,
"%0.01f GB"
, double);
/
*
100G
-
999G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
,
1000
,
"%llu GB"
, u64);
/
*
1.00T
-
9.99G
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
*
1024
,
9.995
,
"%0.02f TB"
, double);
/
*
10.0T
-
99.9T
*
/
CHK_FORMAT(
1024LL
*
1024
*
1024
*
1024
,
99.95
,
"%0.01f TB"
, double);
#undef CHK_FORMAT
/
*
100T
+
*
/
strcpy(tmp[cur],
"infty"
);
return
tmp[cur];
}
struct sigaction {
void (
*
sa_handler)(
int
);
/
*
address of signal handler
*
/
sigset_t sa_mask;
/
*
additional signals to block
*
/
int
sa_flags;
/
*
signal options
*
/
/
*
alternate signal handler
*
/
void (
*
sa_sigaction)(
int
, siginfo_t
*
, void
*
);
};
struct sigaction {
void (
*
sa_handler)(
int
);
/
*
address of signal handler
*
/
sigset_t sa_mask;
/
*
additional signals to block
*
/
int
sa_flags;
/
*
signal options
*
/
/
*
alternate signal handler
*
/
void (
*
sa_sigaction)(
int
, siginfo_t
*
, void
*
);
};
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
*
/
};
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
*
/
};
static struct extra_data
*
extras;
/
*
Extra tokens to fuzz with
*
/
static u32 extras_cnt;
/
*
Total number of tokens read
*
/
static struct extra_data
*
a_extras;
/
*
Automatically selected extras
*
/
static u32 a_extras_cnt;
/
*
Total number of tokens available
*
/
static struct extra_data
*
extras;
/
*
Extra tokens to fuzz with
*
/
static u32 extras_cnt;
/
*
Total number of tokens read
*
/
static struct extra_data
*
a_extras;
/
*
Automatically selected extras
*
/
static u32 a_extras_cnt;
/
*
Total number of tokens available
*
/
struct extra_data {
u8
*
data;
/
*
Dictionary token data
*
/
u32
len
;
/
*
Dictionary token length
*
/
u32 hit_cnt;
/
*
Use count
in
the corpus
*
/
};
struct extra_data {
u8
*
data;
/
*
Dictionary token data
*
/
u32
len
;
/
*
Dictionary token length
*
/
u32 hit_cnt;
/
*
Use count
in
the corpus
*
/
};
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
|
if (!strncmp(name, "afl-clang" , 9 )){
} else {
#ifdef __APPLE__
......
#else
......
#endif /* __APPLE__ */
} while (argc - - ){
}
if (getenv( "AFL_HARDEN" )){
}
if (asan_set) {
} else if (getenv( "AFL_USE_ASAN" )) {
} else if (getenv( "AFL_USE_MSAN" )) {
}
if (!getenv( "AFL_DONT_OPTIMIZE" )) {
}
if (getenv( "AFL_NO_BUILTIN" )) {
}
/ / over : - )
|
1
2
|
stage_short = "flip1" ;
stage_name = "bitflip 1/1" ;
|
1
2
3
4
5
6
7
8
9
10
11
12
|
for (stage_cur = 0 ; stage_cur < stage_max; stage_cur + + ) {
stage_cur_byte = stage_cur >> 3 ;
FLIP_BIT(out_buf, stage_cur);
if (common_fuzz_stuff(argv, out_buf, len )) goto abandon_entry;
FLIP_BIT(out_buf, stage_cur);
.......
} |
1
2
3
4
5
|
#define FLIP_BIT(_ar, _b) do { \ u8 * _arf = (u8 * )(_ar); \
u32 _bf = (_b); \
_arf[(_bf) >> 3 ] ^ = ( 128 >> ((_bf) & 7 )); \
} while ( 0 )
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
stage_name = "bitflip 2/1" ;
stage_short = "flip2" ;
stage_max = ( len << 3 ) - 1 ;
orig_hit_cnt = new_hit_cnt;
for (stage_cur = 0 ; stage_cur < stage_max; stage_cur + + ) {
stage_cur_byte = stage_cur >> 3 ;
FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur + 1 );
if (common_fuzz_stuff(argv, out_buf, len )) goto abandon_entry;
FLIP_BIT(out_buf, stage_cur);
FLIP_BIT(out_buf, stage_cur + 1 );
} |
1
2
3
4
5
6
7
8
9
10
11
12
|
/ * Effector map setup. These macros calculate:
EFF_APOS - position of a particular file offset in the map .
EFF_ALEN - length of a map with a particular number of bytes.
EFF_SPAN_ALEN - map span for a sequence of bytes.
* /
#define EFF_APOS(_p) ((_p) >> EFF_MAP_SCALE2) #define EFF_REM(_x) ((_x) & ((1 << EFF_MAP_SCALE2) - 1)) #define EFF_ALEN(_l) (EFF_APOS(_l) + !!EFF_REM(_l)) #define EFF_SPAN_ALEN(_p, _l) (EFF_APOS((_p) + (_l) - 1) - EFF_APOS(_p) + 1) |
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
|
for (stage_cur = 0 ; stage_cur < stage_max; stage_cur + + ) {
stage_cur_byte = stage_cur;
out_buf[stage_cur] ^ = 0xFF ; / / 直接通过对于out_buf的每一个字节中的每一个bit做异或翻转。
if (common_fuzz_stuff(argv, out_buf, len )) goto abandon_entry; / / 运行对应的test case(翻转后)
if (!eff_map[EFF_APOS(stage_cur)]) { / / 如果eff_map[stage_cur>> 3 ]为 0 的话
/ / EFF_APOS宏也起到了一个将stage_cur>> 3 的效果
u32 cksum;
/ * If in dumb mode or if the file is very short, just flag everything
without wasting time on checksums. * /
if (!dumb_mode && len > = EFF_MIN_LEN) / / 如果不是dumb_mode且 len 大于最小的EFF_MIN_LEN
cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST); / / 计算hash32
else / / 否则,如果是dumb mode或者 len 过小
cksum = ~queue_cur - >exec_cksum;
if (cksum ! = queue_cur - >exec_cksum) {
eff_map[EFF_APOS(stage_cur)] = 1 ; / / 产生新的路径,发生了变化,此时直接将对应的eff_map中的项标记为 1
eff_cnt + + ;
}
}
out_buf[stage_cur] ^ = 0xFF ; / / 从新异或回来
} |
1 |
- 如果设置了```no_arith
|
1 |
#define ARITH_MAX 35 |
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
|
u8 orig = out_buf[i];
.......
for (j = 1 ; j < = ARITH_MAX; j + + ) { / / 依次扫描orig到orig + 35
u8 r = orig ^(orig + j); / / 将orig与orig + j(j最大为 35 )进行异或翻转
/ * Do arithmetic operations only if the result couldn't be a product
of a bitflip. * /
if (!could_be_bitflip(r)) { / / 判断是否为可以通过上一阶段bitfilp得到的(这一步是为了防止相同的冗余变异,节省时间)
stage_cur_val = j;
out_buf[i] = orig + j; / / 将out_buf[i]本身加j变异
if (common_fuzz_stuff(argv, out_buf, len )) goto abandon_entry; / / 进行fuzz
stage_cur + + ;
} else stage_max - - ; / / 否则stage_max减 1
r = orig ^ (orig - j); / / 将orig与orig - j(j最大为 35 )进行异或翻转
if (!could_be_bitflip(r)) { / / 如果判断为可以bitfilp
stage_cur_val = - j;
out_buf[i] = orig - j; / / 将out_buf[i]本身减j变异
if (common_fuzz_stuff(argv, out_buf, len )) goto abandon_entry; / / 进行fuzz
stage_cur + + ;
} else stage_max - - ;
out_buf[i] = orig;
} |
1
2
|
memcpy(out_buf + i, extras[j].data, last_len);
if (common_fuzz_stuff(argv, out_buf, len )) goto abandon_entry;
|
1
2
3
|
- 首先```total_tmouts```计数加一。
- 如果```nique_hangs > = KEEP_UNIQUE_HANG```那么直接返回keeping
- 如果不是```dumb_mode
|
1 |
- ```keep_as_crash:
|
1
2
3
4
5
6
7
8
9
10
11
|
/ * Destructively simplify trace by eliminating hit count information
and replacing it with 0x80 or 0x01 depending on whether the tuple
is hit or not . Called on every new crash or timeout, should be
reasonably fast. * /
static const u8 simplify_lookup[ 256 ] = {
[ 0 ] = 1 ,
[ 1 ... 255 ] = 128
}; |
1
2
3
4
5
6
7
|
static u8 tmp[ 64 ];
static u8 clean_trace[MAP_SIZE]; u8 needs_write = 0 , fault = 0 ;
u32 trim_exec = 0 ;
u32 remove_len; u32 len_p2; |
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
|
static void write_with_gap(void * mem, u32 len , u32 skip_at, u32 skip_len) {
s32 fd = out_fd;
u32 tail_len = len - skip_at - skip_len;
if (out_file) {
unlink(out_file); / * Ignore errors. * /
fd = open (out_file, O_WRONLY | O_CREAT | O_EXCL, 0600 );
if (fd < 0 ) PFATAL( "Unable to create '%s'" , out_file);
} else lseek(fd, 0 , SEEK_SET);
if (skip_at) ck_write(fd, mem, skip_at, out_file);
if (tail_len) ck_write(fd, mem + skip_at + skip_len, tail_len, out_file);
if (!out_file) {
if (ftruncate(fd, len - skip_len)) PFATAL( "ftruncate() failed" );
lseek(fd, 0 , SEEK_SET);
} else close(fd);
} |
1
2
3
4
5
6
7
|
if (q - >exec_us * 0.1 > avg_exec_us) perf_score = 10 ;
else if (q - >exec_us * 0.25 > avg_exec_us) perf_score = 25 ;
else if (q - >exec_us * 0.5 > avg_exec_us) perf_score = 50 ;
else if (q - >exec_us * 0.75 > avg_exec_us) perf_score = 75 ;
else if (q - >exec_us * 4 < avg_exec_us) perf_score = 300 ;
else if (q - >exec_us * 3 < avg_exec_us) perf_score = 200 ;
else if (q - >exec_us * 2 < avg_exec_us) perf_score = 150 ;
|
1
2
3
4
5
6
|
if (q - >bitmap_size * 0.3 > avg_bitmap_size) perf_score * = 3 ;
else if (q - >bitmap_size * 0.5 > avg_bitmap_size) perf_score * = 2 ;
else if (q - >bitmap_size * 0.75 > avg_bitmap_size) perf_score * = 1.5 ;
else if (q - >bitmap_size * 3 < avg_bitmap_size) perf_score * = 0.25 ;
else if (q - >bitmap_size * 2 < avg_bitmap_size) perf_score * = 0.5 ;
else if (q - >bitmap_size * 1.5 < avg_bitmap_size) perf_score * = 0.75 ;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
switch (q - >depth) {
case 0 ... 3 :
break ;
case 4 ... 7 :
perf_score * = 2 ;
break ;
case 8 ... 13 :
perf_score * = 3 ;
break ;
case 14 ... 25 :
perf_score * = 4 ;
break ;
default:
perf_score * = 5 ;
} |
1
2
3
4
5
6
7
8
9
|
- 首先我们定义一个 ```struct sigaction sa``` 用于储存信号相关的信息。接着我们设置 ```sa.sa_handler = handle_stop_sig``` ,然后使用 ```sigaction()``` 函数指定,当 ```SIGHUP,SIGINT,SIGTERM``` 信号来临时,信号处理函数是:```sa.sa_handler = handle_stop_sig```。其实就是指定了当停止是调用的 handler。
- 主要处理包括 cirl - C之类的操作。
- 设置stop_soon = 1
- 杀死 child_pid 和 forksrv_pid 对应的进程。
- SIGALRM:指定timeout的handler为 ```handle_timeout``` 函数。
- 当处于主进程时杀死子进程
- child_pid = = - 1 且 forksrv_pid > 0 时杀死forkserver进程。( - 1 是定义的初始值,不是返回值)
- 均设置child_timed_out = 1 。
- SIGWINCH:Window resize为```handle_resize
|
1
2
|
virgin_tmout[MAP_SIZE], / * Bits we haven't seen in tmouts * /
virgin_crash[MAP_SIZE]; / * Bits we haven't seen in crashes * /
|
1
2
3
4
5
6
7
8
9
|
[ 0 ] = 0 , / / 0
[ 1 ] = 1 , / / 1
[ 2 ] = 2 , / / 2
[ 3 ] = 4 , / / 3
[ 4 ... 7 ] = 8 , / / 4
[ 8 ... 15 ] = 16 , / / 5
[ 16 ... 31 ] = 32 , / / 6
[ 32 ... 127 ] = 64 , / / 7
[ 128 ... 255 ] = 128 / / 8
|
1
2
3
4
5
6
7
|
u32 b1, b2; for (b1 = 0 ; b1 < 256 ; b1 + + )
for (b2 = 0 ; b2 < 256 ; b2 + + )
count_class_lookup16[(b1 << 8 ) + b2] =
(count_class_lookup8[b1] << 8 ) |
count_class_lookup8[b2];
|
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
|
本函数用来初始化 ```u16 count_class_lookup16[ 65536 ]``` 这个数组。
- 将整个 count_class_lookup16 分成 256 段,每一段 256 份儿。初始化的时候利用了 count_class_lookup8。
- count_class_lookup8中对于执行次数进行了规整,比如执行了 4 - 7 次的其计数为 8 ,比如 32 次到 127 次都会认为是 64 次。
- 变量 ```trace_bits``` 来记录分支执行次数,而count_class_lookup8实际就是对于```trace_bits```的规整。
- 而初始化 count_class_lookup16 实际是因为 AFL 中对于一条分支径的表示是由一个二元组来表示的。
- 例如:```A - >B - >C - >D - >A - B```, 可以用[A,B] [B,C] [C,D] [D,A]四个二元组表示,只需要记录跳转的源地址和目标地址。并且[A,B]执行了两次,其余执行了一次,这里用 hash 映射在一张 map 中。
- 而基于这种二元组的表示的效率考虑,又使用了```u16 count_class_lookup16[ 65536 ]``` 这个数组,并在此初始化。
在网上找到这样一段解释我觉得很好: * 这样处理之后,对分支执行次数就会有一个简单的归类。例如,如果对某个测试用例处理时,分支A执行了 32 次;对另外一个测试用例,分支A执行了 33 次,那么AFL就会认为这两次的 * * 代码覆盖 * * 是相同的。当然,这样的简单分类肯定不能区分所有的情况,不过在某种程度上,处理了一些因为循环次数的微小区别,而误判为不同执行结果的情况。 *
### setup_dirs_fds() https: / / www.cnblogs.com / xiaofeiIDO / p / 6695459.html
设置输出的文件夹与fd。 - 如果设置了sync_id且通过mkdir(sync_dir, 0700 )创建了对应的文件夹失败,并且errno ! = EEXIST时,abort
- 如果没设置sync_id,尝试创建了sync_dir,创建失败,且errno ! = EEXIST时abort。
- 如果创建sync_dir失败,且errno = EEXIST。调用maybe_delete_out_dir()收尾。
- 如果创建成功。
- 设置了in_place_resume,则abort
- 否则调用 open 以读权限打开,返回fd,赋值给out_dir_fd。
- 接下来将out_dir与 / queue拼接为tmp。(output / queue)
- 创建tmp文件夹。
- 创建output / queue / .state / (Top - level directory for queue metadata used for session resume and related tasks)
- 创建output / queue / .state / deterministic_done / (Directory for flagging queue entries that went through deterministic fuzzing in the past.)
- 创建output / queue / .state / auto_extras / (Directory with the auto - selected dictionary entries)
- 创建output / queue / .state / redundant_edges / (The set of paths currently deemed redundant)
- 创建output / queue / .state / variable_behavior / (The set of paths showing variable behavior)
- 接下来做目录的同步,用于追踪共同工作的fuzzer
- 如果设置了sync_id。那么创建output / .synced /
- * * 创建output / crashes,记录crash样本。 * * ( All recorded crashes)
- 创建output / hangs( All recorded hangs.)
- 打开 / dev / null与 / dev / urandom,fd为dev_null_fd和dev_urandom_fd。
- 创建output / plot_data,通过``` open (tmp, O_WRONLY | O_CREAT | O_EXCL, 0600 )``` 打开返回fd。
- 通过fdopen将fd转为文件指针plot_file,通过fprintf将一些信息写入文件。忽略错误。
```c fprintf(plot_file, "# unix_time, cycles_done, cur_path, paths_total, "
"pending_total, pending_favs, map_size, unique_crashes, "
"unique_hangs, max_depth, execs_per_sec\n" );
|
1
2
|
struct dirent * * nl;
s32 nl_cnt; |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/ / Data structure for a directory entry
struct dirent { long d_ino; / * inode number 索引节点号 * / off_t d_off; / * offset to this dirent 在目录文件中的偏移 * / unsigned short d_reclen; / * length of this d_name 文件名长 * / unsigned char d_type; / * the type of d_name 文件类型 * / char d_name [NAME_MAX + 1 ]; / * file name (null - terminated) 文件名,最长 255 字符 * / } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/ * Shuffle an array of pointers. Might be slightly biased. * /
static void shuffle_ptrs(void * * ptrs, u32 cnt) {
u32 i;
for (i = 0 ; i < cnt - 2 ; i + + ) {
u32 j = i + UR(cnt - i);
void * s = ptrs[i];
ptrs[i] = ptrs[j];
ptrs[j] = s;
}
} |
1
2
3
4
5
6
7
8
9
10
11
12
|
/ * Interesting values, as per config.h * /
static s8 interesting_8[] = {INTERESTING_8};
static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
static struct extra_data * extras; / * Extra tokens to fuzz with * /
static u32 extras_cnt; / * Total number of tokens read * /
static struct extra_data * a_extras; / * Automatically selected extras * /
static u32 a_extras_cnt; / * Total number of tokens available * /
|
1
2
3
4
5
6
7
8
|
struct queue_entry * q = queue; / / 指向 input case queue 队头
u32 id = 0 ;
#ifndef SIMPLE_FILES # define CASE_PREFIX "id:" #else # define CASE_PREFIX "id_" #endif /* ^!SIMPLE_FILES */ |
1
2
3
|
link_or_copy(q - >fname, nfn)
/ / q - >fname: "input/a.out"
/ / nfn: "output/queue/id:000000,orig:a.out"
|
1
2
|
while ((i = read(sfd, tmp, 64 * 1024 )) > 0 )
ck_write(dfd, tmp, i, new_path);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/ * Mark deterministic checks as done for a particular queue entry. We use the
.state file to avoid repeating deterministic fuzzing when resuming aborted
scans. * /
static void mark_as_det_done(struct queue_entry * q) {
u8 * fn = strrchr(q - >fname, '/' );
s32 fd;
fn = alloc_printf( "%s/queue/.state/deterministic_done/%s" , out_dir, fn + 1 );
fd = open (fn, O_WRONLY | O_CREAT | O_EXCL, 0600 ); / / 打开fn对应的文件,若没有则创建。
if (fd < 0 ) PFATAL( "Unable to create '%s'" , fn);
close(fd);
ck_free(fn);
q - >passed_det = 1 ; / / 设置queue中这一项的passed_det = 1 代表已经fuzz过了
} |
1
2
3
4
5
6
|
首先定义: ```c DIR * d;
struct dirent * de;
u32 min_len = MAX_DICT_FILE, max_len = 0 , dict_level = 0 ;
u8 * x;
|
1
2
3
4
5
|
static u8 tmp[ 4096 ]; / * Ought to be enough for anybody. * /
u8 * fn, * off;
s32 fd, i; u32 ret; |
1
2
|
u32 i = 0 ;
u8 * cwd = getcwd(NULL, 0 );
|
1
2
3
|
struct queue_entry * q = queue;
u32 cal_failures = 0 ;
u8 * skip_crashes = getenv( "AFL_SKIP_CRASHES" );
|
1
2
3
4
5
6
7
8
9
10
|
static u8 first_trace[MAP_SIZE]; u8 fault = 0 , new_bits = 0 , var_detected = 0 , hnb = 0 ,
first_run = (q - >exec_cksum = = 0 );
u64 start_us, stop_us; s32 old_sc = stage_cur, old_sm = stage_max;
u32 use_tmout = exec_tmout;
u8 * old_sn = stage_name;
|
1
2
3
4
|
static struct itimerval it; int st_pipe[ 2 ], ctl_pipe[ 2 ];
int status;
s32 rlen; |
1
2
3
4
5
6
7
8
9
|
close(ctl_pipe[ 0 ]);
close(ctl_pipe[ 1 ]);
close(st_pipe[ 0 ]);
close(st_pipe[ 1 ]);
close(out_dir_fd);
close(dev_null_fd);
close(dev_urandom_fd);
close(fileno(plot_file));
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#ifdef WORD_SIZE_64 u64 * current = (u64 * ) trace_bits;
u64 * virgin = (u64 * ) virgin_map;
u32 i = (MAP_SIZE >> 3 );
这里 MAP_SIZE>> 3 代表 MAP_SIZE / 8 也就是按照 8 个字节一组,一共分了i组 ( 64 位实现)
#else u32 * current = (u32 * )trace_bits;
u32 * virgin = (u32 * )virgin_map;
u32 i = (MAP_SIZE >> 2 );
这里 MAP_SIZE>> 3 代表 MAP_SIZE / 4 也就是按照 4 个字节一组,一共分了i组 ( 32 位实现)
#endif /* ^WORD_SIZE_64 */ |
1
2
3
4
|
if ((cur[ 0 ] && vir[ 0 ] = = 0xff ) || (cur[ 1 ] && vir[ 1 ] = = 0xff ) ||
(cur[ 2 ] && vir[ 2 ] = = 0xff ) || (cur[ 3 ] && vir[ 3 ] = = 0xff ) ||
(cur[ 4 ] && vir[ 4 ] = = 0xff ) || (cur[ 5 ] && vir[ 5 ] = = 0xff ) ||
(cur[ 6 ] && vir[ 6 ] = = 0xff ) || (cur[ 7 ] && vir[ 7 ] = = 0xff ))
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
static inline void classify_counts(u64 * mem) {
u32 i = MAP_SIZE >> 3 ; / / 八个一组,一共i组
while (i - - ) { / / 每一组扫描
/ * Optimize for sparse bitmaps. * /
if (unlikely( * mem)) { / / 如果对应的mem中的值不为 0
u16 * mem16 = (u16 * ) mem;
mem16[ 0 ] = count_class_lookup16[mem16[ 0 ]];
mem16[ 1 ] = count_class_lookup16[mem16[ 1 ]];
mem16[ 2 ] = count_class_lookup16[mem16[ 2 ]];
mem16[ 3 ] = count_class_lookup16[mem16[ 3 ]];
}
mem + + ;
}
} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#define FF(_b) (0xff << ((_b) << 3)) / * Count the number of bytes set in the bitmap. Called fairly sporadically,
mostly to update the status screen or calibrate and examine confirmed
new paths. * /
static u32 count_bytes(u8 * mem) {
u32 * ptr = (u32 * ) mem; / / ptr指向trace bits的首位
u32 i = (MAP_SIZE >> 2 ); / / 四个字节一组
u32 ret = 0 ; / / ret计数初始化为 0
while (i - - ) { / / 一组一组扫描
u32 v = * (ptr + + ); / / v每次取 4 个字节
if (!v) continue ; / / 如果 4 个字节全部是 0 的话直接跳过这四个字节取下一组。
if (v & FF( 0 )) ret + + ; / / 0xff
if (v & FF( 1 )) ret + + ; / / 0xff00
if (v & FF( 2 )) ret + + ; / / 0xff0000
if (v & FF( 3 )) ret + + ; / / 0xff000000
}
return ret;
} |
1
2
|
u32 i; u64 fav_factor = q - >exec_us * q - > len ;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/ * Compact trace bytes into a smaller bitmap. We effectively just drop the
count information here. This is called only sporadically, for some
new paths. * /
static void minimize_bits(u8 * dst, u8 * src) {
u32 i = 0 ;
while (i < MAP_SIZE) {
if ( * (src + + )) dst[i >> 3 ] | = 1 << (i & 7 );
i + + ;
}
} |
1
2
3
4
5
6
7
8
|
u32 i; if (count_bytes(trace_bits) < 100 ) return ;
for (i = ( 1 << (MAP_SIZE_POW2 - 1 )); i < MAP_SIZE; i + + )
if (trace_bits[i]) return ;
WARNF( "Recompile binary with newer version of afl to improve coverage!" );
|
1
2
3
4
5
6
7
8
9
|
- 利用```count_bytes(trace_bits)```统计发现的路径数量(不为 0 的字节),小于 100 则 return 。
- 扫描trace_bits后半部分,如果有不为零的就返回。
- 否则告诉用户改进覆盖率。
### cull_queue() 用于精简队列。 ```c struct queue_entry * q;
static u8 temp_v[MAP_SIZE >> 3 ]; / / temp_v[ 8192 ]
u32 i;
|
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
|
"start_time : %llu\n" "last_update : %llu\n"
"fuzzer_pid : %u\n"
"cycles_done : %llu\n"
"execs_done : %llu\n"
"execs_per_sec : %0.02f\n"
"paths_total : %u\n"
"paths_favored : %u\n"
"paths_found : %u\n"
"paths_imported : %u\n"
"max_depth : %u\n"
"cur_path : %u\n" / * Must match find_start_position() * /
"pending_favs : %u\n"
"pending_total : %u\n"
"variable_paths : %u\n"
"stability : %0.02f%%\n"
"bitmap_cvg : %0.02f%%\n"
"unique_crashes : %llu\n"
"unique_hangs : %llu\n"
"last_path : %llu\n"
"last_crash : %llu\n"
"last_hang : %llu\n"
"execs_since_crash : %llu\n"
"exec_timeout : %u\n" / * Must match find_timeout() * /
"afl_banner : %s\n"
"afl_version : " VERSION "\n"
"target_mode : %s%s%s%s%s%s%s\n"
"command_line : %s\n"
"slowest_exec_ms : %llu\n" ,
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/ * Generate a random number ( from 0 to limit - 1 ). This may
have slight bias. * /
static inline u32 UR(u32 limit) { if (unlikely(!rand_cnt - - )) {
u32 seed[ 2 ];
ck_read(dev_urandom_fd, &seed, sizeof(seed), "/dev/urandom" );
srandom(seed[ 0 ]);
rand_cnt = (RESEED_RNG / 2 ) + (seed[ 1 ] % RESEED_RNG);
}
return random() % limit;
} |
1
2
3
4
5
6
7
8
9
10
|
/ * User - facing macro to sprintf() to a dynamically allocated buffer . * /
#define alloc_printf(_str...) ({ \ u8 * _tmp; \
s32 _len = snprintf(NULL, 0 , _str); \
if (_len < 0 ) FATAL( "Whoa, snprintf() fails?!" ); \
_tmp = ck_alloc(_len + 1 ); \
snprintf((char * )_tmp, _len + 1 , _str); \
_tmp; \
})
|
1
2
3
4
5
6
7
8
9
10
11
|
EINVAL: 模式值无效 EACCES: 文件或路径名中包含的目录不可访问 ELOOP : 解释路径名过程中存在太多的符号连接 ENAMETOOLONG:路径名太长 ENOENT:路径名中的目录不存在或是无效的符号连接 ENOTDIR: 路径名中当作目录的组件并非目录 EROFS: 文件系统只读 EFAULT: 路径名指向可访问的空间外 EIO:输入输出错误 ENOMEM: 不能获取足够的内核内存 ETXTBSY:对程序写入出错 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/ * Error - checking versions of read() and write() that call RPFATAL() as
appropriate. * /
#define ck_write(fd, buf, len, fn) do { \ u32 _len = ( len ); \
s32 _res = write(fd, buf, _len); \
if (_res ! = _len) RPFATAL(_res, "Short write to %s" , fn); \
} while ( 0 )
#define ck_read(fd, buf, len, fn) do { \ u32 _len = ( len ); \
s32 _res = read(fd, buf, _len); \
if (_res ! = _len) RPFATAL(_res, "Short read from %s" , fn); \
} while ( 0 )
|
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
49
|
/ * Describe integer as memory size. * /
static u8 * DMS(u64 val) {
static u8 tmp[ 12 ][ 16 ];
static u8 cur;
cur = (cur + 1 ) % 12 ;
/ * 0 - 9999 * /
CHK_FORMAT( 1 , 10000 , "%llu B" , u64);
/ * 10.0k - 99.9k * /
CHK_FORMAT( 1024 , 99.95 , "%0.01f kB" , double);
/ * 100k - 999k * /
CHK_FORMAT( 1024 , 1000 , "%llu kB" , u64);
/ * 1.00M - 9.99M * /
CHK_FORMAT( 1024 * 1024 , 9.995 , "%0.02f MB" , double);
/ * 10.0M - 99.9M * /
CHK_FORMAT( 1024 * 1024 , 99.95 , "%0.01f MB" , double);
/ * 100M - 999M * /
CHK_FORMAT( 1024 * 1024 , 1000 , "%llu MB" , u64);
/ * 1.00G - 9.99G * /
CHK_FORMAT( 1024LL * 1024 * 1024 , 9.995 , "%0.02f GB" , double);
/ * 10.0G - 99.9G * /
CHK_FORMAT( 1024LL * 1024 * 1024 , 99.95 , "%0.01f GB" , double);
/ * 100G - 999G * /
CHK_FORMAT( 1024LL * 1024 * 1024 , 1000 , "%llu GB" , u64);
/ * 1.00T - 9.99G * /
CHK_FORMAT( 1024LL * 1024 * 1024 * 1024 , 9.995 , "%0.02f TB" , double);
/ * 10.0T - 99.9T * /
CHK_FORMAT( 1024LL * 1024 * 1024 * 1024 , 99.95 , "%0.01f TB" , double);
#undef CHK_FORMAT / * 100T + * /
strcpy(tmp[cur], "infty" );
return tmp[cur];
} |
1
2
3
4
5
6
7
8
|
struct sigaction { void ( * sa_handler)( int ); / * address of signal handler * /
sigset_t sa_mask; / * additional signals to block * /
int sa_flags; / * signal options * /
/ * alternate signal handler * /
void ( * sa_sigaction)( int , siginfo_t * , void * );
}; |
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
|
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 * /
}; |
1
2
3
4
5
|
static struct extra_data * extras; / * Extra tokens to fuzz with * /
static u32 extras_cnt; / * Total number of tokens read * /
static struct extra_data * a_extras; / * Automatically selected extras * /
static u32 a_extras_cnt; / * Total number of tokens available * /
|
1
2
3
4
5
|
struct extra_data { u8 * data; / * Dictionary token data * /
u32 len ; / * Dictionary token length * /
u32 hit_cnt; / * Use count in the corpus * /
}; |
- AFL源码阅读笔记之gcc与fuzz部分
- 开始之前
- alf-gcc.c(a GCC wrapper)
- 概述
- main函数
- find_as
- edit_params
- alf-fuzz.c(Main fuzz .c file)
- Main函数
- fuzz主循环中的关键函数
- fuzz_one(char **argv)
- sync_fuzzers(char **argv)
- save_if_interesting(char **argv, void *mem, u32 len, u8 fault)
- simplify_trace(u64 *mem)
- trim_case(char **argv, struct queue_entry *q, u8 *in_buf)
- write_with_gap(void *mem, u32 len, u32 skip_at, u32 skip_len)
- calculate_score(struct queue_entry *q)
- common_fuzz_stuff(char **argv, u8 *out_buf, u32 len)
- 开始fuzz之前的一些关键函数
- setup_signal_handlers()
- define EXP_ST
- define EXP_ST static
- check_asan_opts()
- setup_shm()
- init_count_class16()
- read_testcases()
- shuffle_ptrs()
- load_auto()
- maybe_add_auto(u8 *mem, u32 len)
- pivot_inputs()
- link_or_copy(u8 *old_path, u8 *new_path)
- mark_as_det_done(struct queue_entry *q)
- load_extras(u8 *dir)
- find_timeout(void)
- detect_file_args(char **argv)
- perform_dry_run(use_argv)
- calibrate_case(char **argv, struct queue_entry *q, u8 *use_mem,u32 handicap, u8 from_queue)
- init_forkserver(char **argv)
- has_new_bits(u8 *virgin_map)
- write_to_testcase(void *mem, u32 len)
- run_target(char **argv, u32 timeout)
- classify_counts(u64 *mem)
- count_bytes(u8 *mem)
- update_bitmap_score(struct queue_entry *q)
- minimize_bits(u8 *dst, u8 *src)
- check_map_coverage()
- mark_as_redundant(struct queue_entry *q, u8 state)
- write_stats_file(double bitmap_cvg, double stability, double eps)
- 附录
- 一些参数/名词
- GCC之fsanitize
- GCC之FORTIFY_SOURCE
- ASAN
- 一些函数/宏
- UR(u32 limit)
- alloc_printf
- strrchr
- sigaction()
- access
- ck_read/ck_write
- DMS
- 一些结构体
- struct sigaction
- struct queue_entry(entry in fuzz queue )
- static struct extra_data
- REF
-
find_as(argv[0])
主要来查找汇编器 -
edit_params(argc, argv)
通过我们传入编译的参数来进行参数处理,将确定好的参数放入cc_params[]
数组。 - 在以上的步骤都完成之后,调用
execvp(cc_params[0], (char **) cc_params)
执行afl-gcc。find_as
在函数开头有这样一条注释。
-
首先获取环境中的
AFL_PATH
变量。如果获取成功,接着通过alloc_printf
来动态的分配一段空间存储对应的路径。之后检查这段路径是否可访问,如果可访问的话赋值给as_path
,然后free,return。不可访问的话直接free掉。 -
如果没有读取成功
AFL_PATH
则通过对argv[0]的匹配,提取当前路径dir
,然后将{dir}/afl-as
在可访问情况下赋值给as_path
,然后free,return。不可访问的话直接free掉。 - 如果以上两种情况因为种种原因都没有成功,那么直接找as,找到了且可访问赋值,没找到就通过
FATAL
输出错误信息然后exit(1)
-
如果是afl-clang开头的话,设置
clang_mode=1
。接下来看究竟是afl-clang++
还是afl-clang
。并根据环境变量设置,具体表现为,以afl-clang
举例。首先获取环境变量AFL_CC
如果获取到了,那么设置cc_params[0]=getenv("AFL_CC")
;反过来如果没有获取到 那么直接设置为"clang"
。cc_params[]
是我们保存编译参数的数组。 -
如果不是afl-clang开头。并且是Apple平台的话话会进入
#ifdef __APPLE__
- 在Apple平台下,开始对
name
进行对比,并通过cc_params[0] = getenv("")
对cc_params的首项进行赋值。在我这里是cc_params[0] = gcc-9
- 在Apple平台下,开始对
-
接下来我们进入一个While循环。在循环中我们扫描 argv[] 数组。并将参数放入
cc_params
- 如果扫描到
-B
,-B 选项用于设置编译器的搜索路径。这里直接跳过。(因为我们之前已经处理过as_path了) - 如果扫描到
-integrated-as
跳过 - 如果扫描到
-pipe
跳过 - 如果扫描到
-fsanitize=address
和-fsanitize=memory
告诉gcc检查内存访问的错误,比如数组越界之类的。如果扫描到了,就设置asan_set = 1
- 如果扫描到
FORTIFY_SOURCE
,设置fortify_set = 1
FORTIFY_SOURCE在使用各种字符串和内存操作功能时执行一些轻量级检查,以检测一些缓冲区溢出错误。比如strcpy这种。
- 如果扫描到
-
当我们跳出循环后,我们向
cc_params
中加上 -B 以及对应的 as_path。之后检查 clang_mode 如果是clang的话设置:-no-integrated-as
。(加入cc_params[]
中) - 接下来取环境变量中的
AFL_HARDEN
如果有的话,添加编译选项-fstack-protector-all
到数组中,紧接着如果没有设置fortify_set
那么添加-D_FORTIFY_SOURCE=2
选项。
- 在Apple平台下,开始对
name
进行对比,并通过cc_params[0] = getenv("")
对cc_params的首项进行赋值。在我这里是cc_params[0] = gcc-9
- 如果扫描到
-B
,-B 选项用于设置编译器的搜索路径。这里直接跳过。(因为我们之前已经处理过as_path了) - 如果扫描到
-integrated-as
跳过 - 如果扫描到
-pipe
跳过 - 如果扫描到
-fsanitize=address
和-fsanitize=memory
告诉gcc检查内存访问的错误,比如数组越界之类的。如果扫描到了,就设置asan_set = 1
- 如果扫描到
FORTIFY_SOURCE
,设置fortify_set = 1
FORTIFY_SOURCE在使用各种字符串和内存操作功能时执行一些轻量级检查,以检测一些缓冲区溢出错误。比如strcpy这种。
- 如果通过
-fsanitize=
设置了asan_set,那么设置环境变量AFL_USE_ASAN = 1
。 - 如果设置了
AFL_USE_ASAN
- 继续检测
AFL_USE_MSAN
与AFL_HARDEN
是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加-U_FORTIFY_SOURCE
与-fsanitize=address
到编译选项中。
- 继续检测
- 如果设置了
AFL_USE_MSAN
- 继续检测
AFL_USE_ASAN
与AFL_HARDEN
是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加-U_FORTIFY_SOURCE
与-fsanitize=memory
到编译选项中。
- 继续检测
- 继续检测
AFL_USE_MSAN
与AFL_HARDEN
是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加-U_FORTIFY_SOURCE
与-fsanitize=address
到编译选项中。
- 继续检测
AFL_USE_ASAN
与AFL_HARDEN
是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加-U_FORTIFY_SOURCE
与-fsanitize=memory
到编译选项中。
- 如果没有设置
AFL_DONT_OPTIMIZE
,也就是允许进行优化。- 向储存编译选项的数组中加入:
-g -O3 -funroll-loops -D__AFL_COMPILER=1 -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1
- 向储存编译选项的数组中加入:
- 向储存编译选项的数组中加入:
-g -O3 -funroll-loops -D__AFL_COMPILER=1 -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1
- 最后,如果设置了
AFL_NO_BUILTIN
,那么连续添加如下编译选项:-fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp -fno-builtin-strncasecmp -fno-builtin-memcmp -fno-builtin-strstr -fno-builtin-strcasestr
- 最后的最后在
cc_params[]
补一个NULL标志结束,return。
- 接下来进入一个大While循环,通过
getopt
扫描我们 argv 里的参数。我们打印出来如下:/Users/apple/Desktop/AFL/AFL/cmake-build-debug/afl-fuzz -i input -o output -- ./test
- 首先扫描到 -i , 将我们 -i 后面的 input 赋值给 in_dir。如果此时的
in_dir = "-"
,那么设置 -in_place_resume = 1
- 接下来扫描到 -o , 将我们的 -o 的参数output赋值给out_dir,
out_dir = optarg
- 首先扫描到 -i , 将我们 -i 后面的 input 赋值给 in_dir。如果此时的
- 参数准备完毕后,调用
setup_signal_handlers()
设置信号处理的句柄。 - 然后调用
check_asan_opts()
检测asan设置是否正确。 - 如果设置了 sync_id = N ,那么在 fix_up_sync 中检测是否有互斥的参数、N是否过大。拼接out_dir, sync_id为新的out_dir。最后,如果force_deterministic没有设置,那么skip_deterministic和use_splicing为1.
- 如果设置了dumb_mode,那么不能设置互斥的crash_mode和qemu_mode。
- 获取环境变量:AFL_NO_FORKSRV、AFL_NO_CPU_RED、FL_NO_ARITH、AFL_SHUFFLE_QUEUE、AFL_FAST_CAL,设置对应的:no_forkserver、no_cpu_meter_red、no_arith、shuffle_queue、fast_cal=1。
- 通过AFL_HANG_TMOUT设置对应的hang_out时间。
- 保证dumb_mode == 2 与 no_forkserver的互斥。
- 设置LD_PRELOAD和DYLD_INSERT_LIBRARIES为AFL_PRELOAD。
- save_cmdline 保存argv到局部变量buf中,没看出来有什么用。
- check_if_tty 检测是否是终端环境,根据AFL_NO_UI设置not_on_tty = 1。然后通过IOCTL设置TIOCGWINSZ。
- get_core_count() 获取cpu数量。
- 然后根据亲缘性设置绑定CPU。
- check_crash_handling():确保core dump的设置正确。
- check_cpu_governor():
- setup_post():设置后处理函数?
- setup_shm():设置共享内存和virgin_bits。
- init_count_class16():初始化count_class_lookup16数组,为分支路径的规整做准备。
- setup_dirs_fds():创建一些相关文件夹,写入一些信息。
- read_testcases():读取测试用例并入队,在启动时调用。
- load_auto():加载automatic extra
- pivot_inputs():在输出目录中为输入测试用例创建硬链接,选择好名字,并据此进行调整。
- 如果设置了extras_dir
- 调用load_extras(extras_dir),加载extras_dir下的文件放入extra数组并排序。
- 如果没设置
timeout_given
那么调用find_timeout
设置超时时间。 - detect_file_args(argv + optind + 1):检测argv中的 @@ 并替换。
- 如果没设置out_file,调用setup_stdio_file(),在
output/.cur_input
新建一个文件作为输出文件,打开后fd赋值给:static s32 out_fd, /* Persistent fd for out_file */
- check_binary(argv[optind]),检查目标文件有效性:是否是可执行文件,是否是Mach-O还是ELF还是一个生成的shell文件。
- start_time = get_cur_time()获取当前时间
- 如果是qemu_mode
- 调用
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind)
为qemu重写参数。 - 否则直接令:
use_argv = argv + optind
- 如果不是,use_argv = argv + optind
- 调用
- perform_dry_run(use_argv):对所有测试用例执行试运行,以确认该应用程序按照预期正常运行。仅对初始输入执行此操作,并且仅执行一次。
- cull_queue():精简队列
- show_init_stats():向用户输出一些状态信息
- find_start_position()
- 只有在resuming_fuzz时才起作用。
- 如果设置了in_place_resume。
- 打开out_dir/fuzzer_stats
- 否则打开in_dir/../fuzzer_stats
- 读取4095字节到tmp中。
- 查找子串
"cur_path : "
的位置为off。 - 设置
ret = atoi(off + 20);
- 如果
ret >= queued_paths
,将ret归零后return ret。 - 否则直接return ret
- 如果
- write_stats_file(0, 0, 0):更新统计信息文件以进行无人值守的监视。
- save_auto()
- Save automatically generated extras.
- 扫描a_extras[]数组,将对应的a_extras[i].data写入
"%s/queue/.state/auto_extras/auto_%06u", out_dir, i
中,其中i最大是min(a_extras_cnt,USE_AUTO_EXTRAS)
- 如果设置了stop_soon,跳转到stop_fuzzing
- 如果是tty启动(终端),那么先sleep 4 秒,start_time += 4000。
- 再检测如果设置了stop_soon,跳转到stop_fuzzing
- 首先扫描到 -i , 将我们 -i 后面的 input 赋值给 in_dir。如果此时的
in_dir = "-"
,那么设置 -in_place_resume = 1
- 接下来扫描到 -o , 将我们的 -o 的参数output赋值给out_dir,
out_dir = optarg
- 调用load_extras(extras_dir),加载extras_dir下的文件放入extra数组并排序。
- 调用
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind)
为qemu重写参数。 - 否则直接令:
use_argv = argv + optind
- 如果不是,use_argv = argv + optind
- 只有在resuming_fuzz时才起作用。
- 如果设置了in_place_resume。
- 打开out_dir/fuzzer_stats
- 否则打开in_dir/../fuzzer_stats
- 读取4095字节到tmp中。
- 查找子串
"cur_path : "
的位置为off。 - 设置
ret = atoi(off + 20);
- 如果
ret >= queued_paths
,将ret归零后return ret。 - 否则直接return ret
- 如果
- 打开out_dir/fuzzer_stats
- 如果
ret >= queued_paths
,将ret归零后return ret。 - 否则直接return ret
- Save automatically generated extras.
- 扫描a_extras[]数组,将对应的a_extras[i].data写入
"%s/queue/.state/auto_extras/auto_%06u", out_dir, i
中,其中i最大是min(a_extras_cnt,USE_AUTO_EXTRAS)
- 再检测如果设置了stop_soon,跳转到stop_fuzzing
- 进入循环一开始,首先再次简化队列。
cull_queue()
-
queue_cur
指向当前队列中的元素。(entry) - 如果
queue_cur
说明此时整个队列已经扫描了一遍了。-
queue_cycle
计数加一,说明走了一个循环了。 -
current_entry
归零。 -
cur_skipped_paths
归零。 -
queue_cur
重新指向队头。 - 如果
seek_to
不为零。-
queue_cur
顺着队列持续后移。 - 同时
seek_to--
;current_entry++
- 直到
seek_to==0
个人感觉就是把queue_cur
置位到seek_to
的位置。
-
-
show_stats()
展示状态 - 如果不是终端模式即:
not_on_tty==1
- 输出当前是第几个循环
ACTF("Entering queue cycle %llu.", queue_cycle);
- 输出当前是第几个循环
- 如果我们经历了一个完整的扫描周期后都没有新的路径发现,那么尝试调整策略。
- 如果:
queued_paths == prev_queued
相等。- 当设置了
use_splicing
-
cycles_wo_finds
计数加一。
-
- 否则设置
use_splicing
为1。(代表我们要通过splicing进行队列重组。)
- 当设置了
- 否则设置
cycles_wo_finds
为0. - 令prev_queued等于queued_paths
- 如果设置了
sync_id
并且queue_cycle == 1
,并且环境变量中设置了AFL_IMPORT_FIRST
- 调用
sync_fuzzers(use_argv)
- 调用
-
- 调用
fuzz_one(use_argv)
对于我们的样本进行变换后fuzz,返回skipped_fuzz - 若skipped_fuzz为0,并且stop_soon为0,并且设置了sync_id
- 若sync_interval_cnt没有到一个周期(% SYNC_INTERVAL)
- 调用
sync_fuzzers(use_argv)
同步其他fuzzer
- 调用
- 若sync_interval_cnt没有到一个周期(% SYNC_INTERVAL)
- 如果没有设置stop_soon,且 exit_1不为0,那么设置stop_soon=2后break出fuzz主循环。
- 否则准备fuzz队列中下一个样本。
- 主循环结束后,销毁内存空间,关闭描述符,输出/更新一些状态,至此整个afl的fuzz过程就结束了。
fuzz主循环中的关键函数
fuzz_one(char **argv)
从队列中取出当前的一项,然后进行fuzz,返回0如果fuzz成功。返回1如果跳过或者bailed out - 如果设置了
pending_favored
- 查看(当前queue中的这一项是否已经fuzz过了或者不是favored)并且打一个100以内的随机数,如果小于
SKIP_TO_NEW_PROB
(百分之99)- 直接return 1.
- 查看(当前queue中的这一项是否已经fuzz过了或者不是favored)并且打一个100以内的随机数,如果小于
- 如果非dumb_mode,且当前的不是favored,并且queued_paths > 10
- 若queue_cycle > 1,并且当前的queue_cur还没有fuzz过。
- 打一个100以内的随机数,如果小于
SKIP_NFAV_NEW_PROB
,直接return 1.(百分之75)
- 打一个100以内的随机数,如果小于
- 否则打一个100以内的随机数,如果小于
SKIP_NFAV_OLD_PROB
,直接return 1.(百分之95)
- 若queue_cycle > 1,并且当前的queue_cur还没有fuzz过。
- 如果不是tty模式。
- 输出
current_entry, queued_paths, unique_crashes
提示信息,刷新stdout缓冲区
- 输出
- 将当前的test case映射进入内存。
orig_in = in_buf = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)
- 分配len大小的空间,初始化为0,返回给out_buf。
- 设置
subseq_tmouts
为0 - 设置
cur_depth
为queue_cur->depth
-
queue_cycle
计数加一,说明走了一个循环了。 -
current_entry
归零。 -
cur_skipped_paths
归零。 -
queue_cur
重新指向队头。 - 如果
seek_to
不为零。-
queue_cur
顺着队列持续后移。 - 同时
seek_to--
;current_entry++
- 直到
seek_to==0
个人感觉就是把queue_cur
置位到seek_to
的位置。
-
-
show_stats()
展示状态 - 如果不是终端模式即:
not_on_tty==1
- 输出当前是第几个循环
ACTF("Entering queue cycle %llu.", queue_cycle);
- 输出当前是第几个循环
- 如果我们经历了一个完整的扫描周期后都没有新的路径发现,那么尝试调整策略。
- 如果:
queued_paths == prev_queued
相等。- 当设置了
use_splicing
-
cycles_wo_finds
计数加一。
-
- 否则设置
use_splicing
为1。(代表我们要通过splicing进行队列重组。)
- 当设置了
- 否则设置
cycles_wo_finds
为0. - 令prev_queued等于queued_paths
- 如果设置了
sync_id
并且queue_cycle == 1
,并且环境变量中设置了AFL_IMPORT_FIRST
- 调用
sync_fuzzers(use_argv)
- 调用
-
queue_cur
顺着队列持续后移。 - 同时
seek_to--
;current_entry++
- 直到
seek_to==0
个人感觉就是把queue_cur
置位到seek_to
的位置。
- 输出当前是第几个循环
ACTF("Entering queue cycle %llu.", queue_cycle);
- 当设置了
use_splicing
-
cycles_wo_finds
计数加一。
-
- 否则设置
use_splicing
为1。(代表我们要通过splicing进行队列重组。)
-
cycles_wo_finds
计数加一。
- 调用
sync_fuzzers(use_argv)
- 若sync_interval_cnt没有到一个周期(% SYNC_INTERVAL)
- 调用
sync_fuzzers(use_argv)
同步其他fuzzer
- 调用
- 调用
sync_fuzzers(use_argv)
同步其他fuzzer
- 查看(当前queue中的这一项是否已经fuzz过了或者不是favored)并且打一个100以内的随机数,如果小于
SKIP_TO_NEW_PROB
(百分之99)- 直接return 1.
- 直接return 1.
- 若queue_cycle > 1,并且当前的queue_cur还没有fuzz过。
- 打一个100以内的随机数,如果小于
SKIP_NFAV_NEW_PROB
,直接return 1.(百分之75)
- 打一个100以内的随机数,如果小于
- 否则打一个100以内的随机数,如果小于
SKIP_NFAV_OLD_PROB
,直接return 1.(百分之95)
- 打一个100以内的随机数,如果小于
SKIP_NFAV_NEW_PROB
,直接return 1.(百分之75)
- 输出
current_entry, queued_paths, unique_crashes
提示信息,刷新stdout缓冲区
- 如果当前的
queue_cur->cal_failed
不为0(存在校准错误)- 如果校准错误的次数小于三次。
- 重制exec_cksum来告诉
calibrate_case
重新执行testcase来避免对于无效trace_bits的使用。 - 设置
queue_cur->exec_cksum
为0. - 对于queue_cur重新执行
calibrate_case
。res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0)
- 如果返回值为FAULT_ERROR,那么直接abort
- 重制exec_cksum来告诉
- 如果设置了stop_soon,或者res不等于crash_mode。
- cur_skipped_paths计数加一。
- 跳转到
abandon_entry
- 如果校准错误的次数小于三次。
- 如果校准错误的次数小于三次。
- 重制exec_cksum来告诉
calibrate_case
重新执行testcase来避免对于无效trace_bits的使用。 - 设置
queue_cur->exec_cksum
为0. - 对于queue_cur重新执行
calibrate_case
。res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0)
- 如果返回值为FAULT_ERROR,那么直接abort
- 重制exec_cksum来告诉
- 如果设置了stop_soon,或者res不等于crash_mode。
- cur_skipped_paths计数加一。
- 跳转到
abandon_entry
- 重制exec_cksum来告诉
calibrate_case
重新执行testcase来避免对于无效trace_bits的使用。 - 设置
queue_cur->exec_cksum
为0. - 对于queue_cur重新执行
calibrate_case
。res = calibrate_case(argv, queue_cur, in_buf, queue_cycle - 1, 0)
- 如果返回值为FAULT_ERROR,那么直接abort
- cur_skipped_paths计数加一。
- 跳转到
abandon_entry
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)