关于 AFL 与 AFLNet 中对代码功能的一些理解,未完。
AFL
run_target
函数功能
首先将tarce_bits
置0,用于记录本次运行的命中的路径及其数量,然后进行pipe设置执行程序,在之后对tarce_bits
进行规整,搜集信息,判断返回状态并返回。
top_rated[]
结构体数组,top_rated[i]
是一个结构体,包含了关于第 i 个测试用例的覆盖率和评分情况。
这里的i表示的是每一条路径,top_rated[i]的意思是对于当前这条路径 i,top_rated[i]位置指向的测试用例是最优的一个
判断条件的理解:
MAP_SIZE
大小为 65535 .表示路径数。MAP_SIZE>>3
表示按8个路径为一组进行划分,每 1 bit代表一条路径,所以需要MAP_SIZE>>3
个byte来存储这些路径,每 1 byte存储8条路径。
(i&7)
相当于是 i
对 7 取模,相当于 i 除以 8 的余数,写成位来看就比较好理解了。
1 | 假设i从0到8 |
因为 MAP_SIZE>>3
后,每 1 bit来表示一条路径,所以在对每个字节的检查中,需要检查的是各个位的位置,范围是0到7之间。
1<<(i&7)
也就是,1分别左移0,1,2,3,···,7位,同样,换成位来看:
1 | i = 0: 1<<0 00000001 |
这样就是依次检查该字节的各个位(bit)的情况
calibrate_case
函数中调用到的关键函数和主要流程:
1 | calibrate_case(){ |
run_target
函数关键流程:
1 | run_target(){ |
common_fuzz_stuff
函数中调用的关键函数和主要流程:
1 | common_fuzz_stuff(){ |
save_if_interesting
函数中调用的关键函数和主要流程:
1 | save_if_interesting(){ |
下面是次一级重要的函数:
trim_case
函数:
1 |
疑问
1.关于forkserver插桩的代码部分:在第一次执行桩代码时的,__afl_fork_wait_loop这个函数内,会不断执行fork?还是只执行一次,这个循环是什么?
对于每一个测试用例,或重新fork一次?还是重新execvp一次?
答:对于每一个测试用例会重新fork一次,生成一个新的子进程,如果未设置forkserver则会重新execvp()
2.forkserver,父子进程进行通信,通信的内容是什么?
写入管道内的__afl_temp 4字节大小的信息是什么?
3.如何给进程传递testcase中的输入?
答:在试运行过程中,通过read函数读取文件内容到use_mem变量中,然后作为参数传递给calibrate_case函数
追问:在calibrate_case函数内,通过write_to_testcase函数将文件内容use_mem写入到out_file(.cur_input)文件,但是fork得到的进程如何获取输入,在源码哪里体现出来?
4.resume模式,多次提到的resuming_fuzz变量
5.跟踪字节引用计数,tc_ref变量的作用
- extras的作用
7.timeout_given,超时时间是谁的超时时间?
答:target程序运行的超时时间
8.cur_depth,max_depth,和q->depth,这些变量里的depth指的是什么?如何理解?
9.路径数,是不是应该叫测试用例数更符合,这里的path理解成路径的话,这个路径是指的什么路径?
10:q->handicap这个变量的作用?
几个模块:反馈收集、种子筛选、种子变异、种子调度。
AFLNET
源码理解
AFLNET主要在AFL的基础上实现了:
- socket 通信发送测试用例
- 一套与代码覆盖率并行的状态机引导机制
- 增加了消息序列级别的变异策略
klib库
khint_t
在 klib 库中,khint_t
是一个无符号整数类型,用于表示哈希表中的键、值和哈希函数的返回值等。
khint_t
的实现可能会因不同的编译器和平台而异,通常会被定义为 size_t
或 unsigned int
。在 klib 库中,khint_t
通常用于表示哈希表中键值对的索引或哈希值,它是哈希表中的关键类型之一。
例如,在使用 klib 库中的哈希表时,可以使用 khint_t
类型来表示键、值和哈希函数的返回值。在向哈希表中添加、删除、查找或更新键值对时,需要使用 khint_t
类型来指定相应的操作。khint_t
可以保证足够大,以便在大多数哈希表中存储索引或哈希值。
kh_get()
kh_get
是 klib 库中哈希表操作的一个函数之一,用于获取指定键的值。
该函数的原型为:
1 | khint_t kh_get(kh_t, khkey_t key); |
其中,kh_t
表示哈希表的类型,khkey_t
表示键的类型,khint_t
表示哈希表索引的类型。该函数的返回值为哈希表中与指定键相关联的索引,如果指定键不存在,则返回哈希表的结束位置。
kh_get
函数的作用是从哈希表中查找指定键的值,并返回该键对应的索引。在哈希表中,每个键都会被哈希成一个索引,而该索引通常用于查找存储在哈希表中的值。kh_get
函数通过查找指定键的索引来获取该键对应的值。
kh_end()
kh_end
是 klib 库中哈希表操作的一个函数之一,用于获取哈希表的结束位置。
该函数的原型为:
1 | khint_t kh_end(kh_t); |
其中,kh_t
表示哈希表的类型,khint_t
表示哈希表索引的类型。该函数的返回值为哈希表的结束位置,即哈希表中最后一个键值对的后一个位置。
kh_end
函数的作用是获取哈希表中最后一个键值对的后一个位置。在 klib 库中,哈希表中的键值对是按照哈希值存储的,而哈希表的大小通常是事先确定的。因此,可以通过 kh_end
函数来获取哈希表的结束位置,并遍历哈希表中的所有键值对。
kh_val()
kh_val
是 klib 库中哈希表操作的一个函数之一,用于获取与指定索引相关联的值。
该函数的原型为:
1 | khval_t kh_val(kh_t, khint_t); |
其中,kh_t
表示哈希表的类型,khint_t
表示哈希表索引的类型,khval_t
表示值的类型。该函数的返回值为与指定索引相关联的值。
kh_val
函数的作用是获取与指定索引相关联的值。在哈希表中,每个键都会被哈希成一个索引,而该索引通常用于查找存储在哈希表中的值。kh_val
函数通过指定索引来获取该索引对应的值。
疑问
Target State Selector 目标状态选择器,什么是state?初始状态从哪里获取?
下一个状态?如何选择下一个状态?如何根据hash表存储状态并索引
send_over_network函数中最后HANDLE_REPONSES:为什么要 while(1)if {has_new_bits==2} break;
种子的region是什么?
答:先看一下add_to_queue函数时,写入到文件保存下来的内容:
在save_regions_to_file函数中,每一个region格式为:Region %d - Start: %d, End: %d\n
我们再来看下生成的文件的内容:
可以看到,rtsp_requests_aac.raw对应的regions的数量是3,即有三个region,
第一个region从0开始,在157(0x9D处结束)。
我们看一下rtsp_requests_aac.raw内容:
红色竖线刚好是0x9D的位置,我们可以看到,region的起止范围正好是第一条请求的全部内容
所以,regions就是该种子中请求的数量,一个region对应一个请求
was_fuzzed_map二维数组是如何标记states和qentries的
qentries又指的是states里的什么?
这里的states的seed指的是什么,kl_message