首页
社区
课程
招聘
[原创]AFL源码阅读笔记之gcc与fuzz部分
发表于: 2021-2-6 21:12 27538

[原创]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

 
 
 
 
 
 
 
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的字节),小于100return
- 扫描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的字节),小于100return
- 扫描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的字节),小于100return
- 扫描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          */
};
  • 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
  • 接下来我们进入一个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_MSANAFL_HARDEN 是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加 -U_FORTIFY_SOURCE-fsanitize=address 到编译选项中。
  • 如果设置了 AFL_USE_MSAN
    • 继续检测 AFL_USE_ASANAFL_HARDEN 是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加 -U_FORTIFY_SOURCE-fsanitize=memory 到编译选项中。
  • 继续检测 AFL_USE_MSANAFL_HARDEN 是否设置。如果设置则 abort,因为他们是互斥的;如果没有设置的话,添加 -U_FORTIFY_SOURCE-fsanitize=address 到编译选项中。
  • 继续检测 AFL_USE_ASANAFL_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
  • 参数准备完毕后,调用 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
  • 如果没有设置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.
  • 如果非dumb_mode,且当前的不是favored,并且queued_paths > 10
    • 若queue_cycle > 1,并且当前的queue_cur还没有fuzz过。
      • 打一个100以内的随机数,如果小于SKIP_NFAV_NEW_PROB,直接return 1.(百分之75)
    • 否则打一个100以内的随机数,如果小于SKIP_NFAV_OLD_PROB,直接return 1.(百分之95)
  • 如果不是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_depthqueue_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以内的随机数,如果小于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
    • 如果设置了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
  • 如果设置了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期)

最后于 2021-2-6 21:39 被Roland_编辑 ,原因:
收藏
免费 13
支持
分享
最新回复 (3)
雪    币: 14517
活跃值: (17538)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2021-2-6 22:52
0
雪    币: 15187
活跃值: (16852)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
3
精品
2021-2-7 09:45
0
雪    币: 8175
活跃值: (6399)
能力值: ( LV12,RANK:207 )
在线值:
发帖
回帖
粉丝
4

read_testcases处:

如果没有fuzz过,设置passed_det = 1。

如果fuzz过了,保持passed_det = 0。

这里好像写反了

2022-1-17 16:40
0
游客
登录 | 注册 方可回帖
返回
//