AFL学习笔记 下篇前言:

上一篇 已经将前三部分写完了,但是留有一些疑问点,所以下篇将分三部分:对上篇遗留问题的解决关键函数实现原理介绍main函数注释(逐行贴出代码块,因为main是整个alf的核心流程,所以需要一行行的详解),这一篇可能对代码的分析偏多一点,因为以后我的论文应该也是要在afl的基础上进行改进,所以就需要对代码多进行分析。其实像mopt-afl对代码的改进就主要是在fuzz_one函数上也就是mutation阶段。
最后是附录,写有我看过的一些实用的相关文章、博客、帖子之类的,多是实用总结型的。


接上篇遗留的问题

先来说说上一篇未解决的问题,这几个问题主要是在代码细节的理解上,有错误之处还望斧正):

1. effector map

关于effector map,在看源码数据变异这一部分的时候,一定会注意的是在 bitflip 8/8 的时候遇到一个叫eff_map的数组,这个数组的大小是EFF_ALEN(len),这个数是多大?

当时没想出来这么写是个什么意思,后来转过弯来,其实那不是变量也不是普通的函数,EFF_ALEN是一个宏定义的函数:

1

2

3

4

5

6

#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)

//为eff_map分配大小为EFF_ALEN(len)的内存

eff_map = ck_alloc(EFF_ALEN(len));

如果把这一块的宏定义拆开,变成函数的形式就是这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

/*此部分我写了下伪代码,宏定义虽然写代码的时候好用,但是不得不说全是大写字母有点难理解,所以我又给拆分成了通俗的代码,

这部分跟上下文关系不大,看懂这部分的代码,其他类似的也很好懂了。*/

type(_p) EFF_APOS(_p){

    /* >> 是位移操作,把参数的二进制形式进行右移,每移动一位就减小2倍,所以这个函数的意思就是:

    返回传入参数_p除以(2的EFF_MAP_SCALE2次方)。

    同理另一个方向就是左移,是放大2的幂的倍数。*/

    return (_p / 8);

    // EFF_MAP_SCALE2 在文件config.h中出现,值为3,所以这里就是除以8的意思。

}

type(_x) EFF_REM(_x){

    //这里 & 是按位与,所以求的是 _x 与 ((1 << EFF_MAP_SCALE2) - 1)进行按位与,实际上就是跟 7 以二进制形式按位与

    return (_x & 7)

}

宏函数本质知道了,但是分别代表的什么意思呢?(代码中给出的注释如下)

EFF_APOS - position of a particular file offset in the map. 在map中的特定文件偏移位置。
EFF_ALEN - length of a map with a particular number of bytes. 根据特定数量的字节数,计算得到的文件长度。
EFF_SPAN_ALEN - map span for a sequence of bytes. 跳过一块bytes

1

2

3

4

5

6

7

8

9

10

type(_l) EFF_ALEN(_l){

    /*这里的 !! 是一个两次否,目的是归一化(又是一个骚操作,这个作者写代码真的是净整些这种,主要还是自己菜,菜是原罪)

    比如 r = !!a,如果a是整数0,则r=0,如果a是整数非0,则r=1

    在a不是整数的情况下一般不这么用,但这里都是默认_l为整数的,毕竟字符型转成ascii码那不也是整数吗。*/

    return (EFF_APOS(_l) + !!EFF_REM(_l))

}

type(_p) EFF_SPAN_ALEN(_p, _l){

    return (EFF_APOS((_p) + (_l) - 1- EFF_APOS(_p) + 1)

}

现在重新看eff_map = ck_alloc(EFF_ALEN(len));,len来自于队列当前结点queue_cur的成员len,是input length(输入长度),所以这里分配给eff_map的大小是 (文件大小/8) 向下取整,这里的 8 = 2^EFF_MAP_SCALE2。比如文件17bytes,那么这里的EFF_ALEN(_l)就是3。

2. ARITHMETIC

如果加减某数之后效果与之前某bitflip效果相同,认为此次变异在上一阶段已经执行过,此次不再执行?

这里当时标记出来是因为没有理解透彻,没怎么搞懂作者是怎么实现的,后来经过仔细分析几个变量的关系,才缕清楚,这里以 ARITHMETIC 的8/8变异这一段为例(16和32的变异大同小异),把重要部分做解释:

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

stage_name  = "arith 8/8";//当前进行的状态,这个在fuzz的时候用来在状态栏展示

stage_short = "arith8";//同上,是简短的状态名

stage_cur   = 0;

stage_max   = 2 * len * ARITH_MAX;

/*ARITH_MAX就是加减变异的最大值限制35

文件大小len bytes,

然后进行 +/- 操作乘以2

每个byte要进行的 +/- 操作各35次,

所以这个stage_max意思就是将要进行多少次变异,但是之后要是没有进行有效变异就要给减去*/

stage_val_type = STAGE_VAL_LE;

orig_hit_cnt = new_hit_cnt;//暂存用于最后的计算

for (i = 0; i < len; i++) {//循环len

  u8 orig = out_buf[i];

  /* Let's consult the effector map... */

  //如果当前i byte在eff_map对应位置是0,就跳过此次循环,进入for循环的下一次

  //并且此byte对应的变异无效,所以要减 2*ARITH_MAX

  if (!eff_map[EFF_APOS(i)]) {

    stage_max -= 2 * ARITH_MAX;

    continue;

  }

  stage_cur_byte = i;//当前byte

  for (j = 1; j <= ARITH_MAX; j++) {

    //分别进行 +/- 操作 j = 1~35

    u8 r = orig ^ (orig + j);

    /* Do arithmetic operations only if the result couldn't be a product of a bitflip. */

    //只有当arithmetic变异跟bitflip变异不重合时才会进行

    if (!could_be_bitflip(r)) {//判断函数就是对是否重合进行判断的

      stage_cur_val = j;

      out_buf[i] = orig + j;

      if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;

      stage_cur++;

    else stage_max--;//如果没有进行变异,stage_max减一,因为这里属于无效操作

    =  orig ^ (orig - j);

    if (!could_be_bitflip(r)) {

      stage_cur_val = -j;

      out_buf[i] = orig - j;

      if (common_fuzz_stuff(argv, out_buf, len)) goto abandon_entry;

      stage_cur++;

    else stage_max--;

    out_buf[i] = orig;

  }

}

new_hit_cnt = queued_paths + unique_crashes;//如果8/8期间有新crash的话会加到这里

stage_finds[STAGE_ARITH8]  += new_hit_cnt - orig_hit_cnt;//这期间增加了的

stage_cycles[STAGE_ARITH8] += stage_max;//如果之前没有有效变异的话stage_max这里就已经变成0

3. 文件拼接

为什么不是当前的尾拼接随机文件的头

这个其实想想当时挺蠢的,一堆文件,拿出一个一分为二,把当前文件的头和其他文件的尾拼接,这是原来的策略,这样多次之后,自然会有之前的那个拿了尾的文件,用这个的头跟其他的尾拼接了。所以没必要此次拼接的时候,非得生成两个文件,因为这样的话后面还会出现,就有点重复了。

4. fuzz流程

一次变异到底是变一个比特还是从一比特到32比特都结束了才算事

这个看理解,我跟同学总结的说法是:一次变异是指一个比特的改变也算变异,根据编译策略,经过很多次变异之后,fuzz_one结束叫做一轮变异。


Ⅳ、关键函数实现原理

这部分详提一下三个重要函数 fuzz_one、init_forkserver、show_stats

1. fuzz_one函数

这个函数的主要功能就是变异,每一阶段变异的原理,在上一篇已经分析过,这里着重说一下作者加的 eff_map 这个变量,看代码的话,在变异fuzz_one阶段,这是个贯穿始终的数组,刚开始第一遍看代码其实不是理解的很深刻,现在理解的比较多了,也能理解作者为什么要加这个数组了:
fuzz_one函数将近两千行,每一个变异阶段都有自己的功能,怎么把上一阶段的信息用于下一阶段,需要一个在函数内通用的局部变量,可以看到fuzz_one一开始的局部变量有很多,有很多类型:

1

2

3

4

5

6

7

s32 len, fd, temp_len, i, j;

u8  *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0;

u64 havoc_queued,  orig_hit_cnt, new_hit_cnt;

u32 splice_cycle = 0, perf_score = 100, orig_perf, prev_cksum, eff_cnt = 1;

u8  ret_val = 1, doing_det = 0;

u8  a_collect[MAX_AUTO_EXTRA];

u32 a_len = 0;

eff_map的类型是u8(上篇解释过这种看不懂的,可以去types.h里找,这是一个uint8_t = unsigned char 形,8比特(0~255)),并且u8类型只有这一个是map命名形式的,在后面的注释中如果出现Effector map,说的就是这个变量。主要的作用就是标记,在初始化数组的地方有这么一段注释Initialize effector map for the next step (see comments below). Always flag first and last byte as doing something.把第一个和最后一个字节单独标记出来用作其他用途,这里其实就说明了,这个map是标记作用,那是标记什么呢,标记当前byte对应的map块是否需要进行阶段变异。如果是0意味着不需要变异,非0(比如1)就需要变异,比如一开始分析的 arithmetic 8/8 阶段就用过这个eff_map,此时已经它在bitflip阶段经过了改变了。

2. init_forkserver函数

用于fork server进行的初始化,这部分跟插桩息息相关,只有插桩模式才会运行这部分,可以配合afl-as.h文件一起看。
这里的整体流程是这样的:

  1. 局部变量,用于设置管道记录信息,以及读取进程记录状态
  2. fork一个进程
  3. execv执行fork severe
  4. 关闭不需要的通道
  5. 读取第一步设置的两个通道的状态
  6. 从状态通道读取4个字节,并且监测是否成功,如果成功会输出“All right - fork server is up.”

3. show_stats函数

这个函数技术上没什么好说的,就是用来展示那个fuzz界面的,每次有数据更新的时候就会调用这个函数刷新页面,如果说在AFL运行的界面,看到哪个不懂的变量,或者想单独看那个参数是怎么来的,就从这个函数入手就对了,对应着界面的字符串定位到函数的位置,然后从变量再定位到相应位置,就可以很轻松的理解参数的含义和求法。


Ⅴ、main函数

main函数逐块分析,这部分可能会有点长,大家随意看看就好,算是我自己看代码的碎碎念吧,因为不少在前面已经解释过了,这里算是总结了,有很多总结文章对源代码的解读比我更好,形式也比我的好看,我这算是献丑了233333)main函数代码太长,我放在这里:afl-fuzz.c文件里的main函数代码注释版了,可以点进去看。


Ⅵ、附录

AFL教程类

  1. 持续更新的AFL相关小细节

    这个是我从去年年底一开始接触AFL一直到现在记录的平时遇到的一些小细节,一般情况下就很机录在这个博客里,王婆卖瓜自卖自夸哈哈哈哈哈

  2. 利用AFL对upx从头开始模糊测试

    这是一篇比较简单的实践类,非常适合刚开始的新手对AFL进行初探。但是我最早的时候跟着这个帖子进行测试,发现并没有达到作者的效果,所以大家就跟着练练手就好啦,只要跑出来的界面就行。

  3. 从头开始的fuzzing流程

    很详细的教程,配合2.可以加快对afl工作原理的理解,我是这么觉得的。如果对英文吃力,可以看这篇翻译

  4. afl-fuzz.c源码讲解

    这位前辈写的源码分析,对我而言收获颇多,希望能帮到大家。

  5. AFL整体结构

    版主写的一篇对AFL从整体结构上进行剖析的文章,写的也很好,很有收获。

  6. 256多线程运行AFL

    一个对AFL进行多线程操作的文章,算是从工程上进行性能改进的类型,有机会可以实现一下。

  7. Awesome AFL

    一个搜集的项目,作者收集了很多 不同的AFL变种,或者AFL启发的超级魔改,这个项目的好处是从没停更,一直整的挺好的。

  8. AFL生态圈

    这位师傅写的翻译,对很多AFL类,以及其他fuzzer工具的介绍,比较仔细全面

  9. AFL插桩讲解

    可以看到上下篇只对afl-fuzz.c进行讲解,插桩部分并没有讲解,因为我也还没仔细研究。有这方面需要的可以看一下这篇文章,对插桩进行了详细解释

Fuzz相关有用收藏

  1. Fuzzer tools

    一些fuzzer工具集合,内容是前几年比较经典的一些fuzzer,不包括最近顶会的那些fuzzer

  2. fuzz总结性优秀文章分类

    这是看雪上的一篇质量挺高的总结性的文章

  3. 顶会fuzz论文收集与总结

    作者收集的一些文章是比较经典的顶会文章,而且做了思维导图,可以说是很用心了,~~ 但是貌似停更很久了,不知道还会不会继续更新。 ~~ 跟作者聊过,现在是不更新了,不过还有一个项目在维护

  4. 顶会fuzzing论文分类

    这是 3. 的作者写的另一个一直在维护的论文分类project,后面我跟同学也会参与其中一起维护,后面对论文的理解相关的东西也会同步更新到这里。我跟郭老板的想法是把近十年的论文abstract都过一遍然后进行分类,希望能从中找到启发。

  5. 大手子rk700

    写的关于fuzz的文章都挺好的,最早的几篇对afl的理解也很有帮助。他的这篇分析写的很好

写在最后

下篇本来是跟着上篇写的,但是中间被安排了一些杂七杂八的事情,就一直拖到现在,还好现在已经进入正轨了,期间看雪上也有发私信一起讨论的,如果大家也想一起讨论,或者对文章内容有异议欢迎留言,或者发邮件 wayne-tao(at)Outlook.com,我也是上学期末对时候下定决心搞这方面的,再加上本科不是搞安全的,所以进步很慢,大家共同进步。
文章和注释源码我放在git上了地址: https://github.com/WayneDevMaze/Chinese_noted_AFL 欢迎大家star,除了上下两篇对 afl.c 文件对分析,还有两篇对cmin和tmin的分析,这两个因为没多少技术含量就不放看雪了,感兴趣可以看一下我对博客(对两个工具对分析和修改tmin、cmin),目前一方面搞fuzz,一方面也在搞web安全(毕竟新入门的水平比较菜,这个好入门),后面也会深入一下二进制,因为经常是发现一些崩溃情况,但是不知道怎么入手分析就很烦,感觉入门还早,继续努力把吧。

AFL学习笔记(下)相关推荐

  1. Python课程学习笔记 下

    笔记目录 Python 学习笔记 上 面向对象和异常 面向对象 基本理论 定义类 根据类实例化对象 属性相关 方法相关 类的补充 属性补充 方法相关补充 描述器 python对象的生命周期 内存管理机 ...

  2. AFL学习笔记(续)

    前言 本篇是之前写的<AFL笔记>(上.下)的后续,之前断断续续学习模糊测试写了两篇侧重分析Fuzz代码的笔记,这篇主要写一下这半年(很水的半年)的Fuzz过程总结的一些经验,用Git上的 ...

  3. 模糊测试-AFL学习笔记之C/C++

    目录 简介 文档 QuickStartGuide README perf_tips.txt status_screen INSTALL 安装 下载 编译 检查 例子 有源码-标准输入 源代码 编译 f ...

  4. 【操作系统-哈工大李治军】---学习笔记(下)---操作系统管理内存

    # 操作系统-内存 ----------------操作系统如何管理CPU------>操作系统如何管理内存----------------------------------- 1 内存使用 ...

  5. 计算机网络基础学习笔记(下)

    学习视频 随看随记 ABCD类型地址的分类 根据地址左起第-个十进制数的值,可以判断出网络类别(小于127的为A类, 128 ~ 191 的为B类,192 ~ 223的为C类) ; 根据网络类别,就可 ...

  6. h5学习笔记 下拉菜单

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 鼠标经过 ...

  7. jQuery基础学习笔记(下)

    8.jQuery的扩展与noConflict 1.jQuery扩展 <script src="../../jquery-2.1.3.min.js"></scrip ...

  8. Oracle PL/SQL语句基础学习笔记(下)

    游标 游标: 游标(cursor)可以被看作指向结果集(a set of rows)中一行的指针(pointer).在oracle数据库中可以使用显示或隐式两种游标. 1.隐式游标 在执行一个sql语 ...

  9. Elasticsearch7学习笔记(中)

    Elasticsearch是实时全文搜索和分析引擎,提供搜集.分析.存储数据三大功能:是一套开放REST和JAVA API等结构提供高效搜索功能,可扩展的分布式系统.它构建于Apache Lucene ...

最新文章

  1. ue4 开发动作游戏_两年时间独自用UE4制作的动作游戏,传说一般的制作人离忧先生...
  2. Css:背景色透明,内容不透明之终极方法!兼容所有浏览器
  3. tomcat jsvc 调优及JMX监控
  4. 应用程序 mysql 连接_学生信息管理系统之四:实现应用程序与数据库的连接
  5. python for in循环_Python傻瓜教程:跟我学for循环
  6. python下面的代码_解析一下下面的python代码?
  7. Google 地图 API 参考
  8. html无框架,HTML框架技术详例
  9. android刷新蓝牙缓存,如何使用GattServer以编程方式清除蓝牙缓存
  10. PDG转PDF全攻略
  11. (C#.net)CAD二次开发 polyline多段线/line直线/曲线 break打断的方法
  12. 如何模拟地震、噪音、颠簸路面激励下的不确定性振动行为?
  13. whl 文件怎么安装
  14. python 输入一个整数,将该整数逆向输出
  15. Invalid bound statement (not found): org.seckill.dao.Suc
  16. 家用路由器及NAT协议
  17. android 应用广告业,安卓应用商店APP推广:CPD广告详解
  18. 软件集成测试采用,集成测试的组成以及流程
  19. C语言Windows命令行编程
  20. 嗨!亲爱的朋友们,欢迎您光临我的BLOG

热门文章

  1. Xilinx FPGA提供DDR4内存接口解决方案
  2. flask-migrate(flask_script与flask2.0不再兼容)
  3. Android SDK AndroidStudio 国内可用的镜像/代理地址
  4. graphpad如何检测方差齐_GraphPad prism --方差世界之析因分析详细步骤解析
  5. virtualbox安装失败报严重错误
  6. js中window。location.search的用法和作用。
  7. 《世界上最远的距离》 泰戈尔
  8. 惠普与戴尔模仿IBM转型难 消化整合周期长
  9. 常吃四类食物能够对抗二手烟
  10. win10系统下配置maven环境