记录一下所遇到的和AFL覆盖率相关的参数
trace_bits
65536字节
在二进制文件中的插桩会直接修改trace_bits。
如何修改? 参考这篇博客https://blog.csdn.net/weixin_45100742/article/details/135426994
写的很清楚,trace_bits记录的是分支覆盖率,并且分支每被执行一次,就会+1
virgin_bits
这个参数在calibrate_case函数中被使用。
首先,这个参数是什么意思? fuzzing目前没有涉及到的区域
问bing:具体来说,virgin_bits数组的每个元素对应于trace_bits数组的一个字节,它表示该字节是否被置位过,即是否覆盖了一个新的执行路径。如果是,那么virgin_bits数组的相应元素就会被清零,否则就保持为255。
看了下这篇博客:https://blog.csdn.net/weixin_50972562/article/details/124327119,virgin_bits 记录总的路径信息
大概意思是,virgin_bits记录了所有已经被发现的路径?
那么,是记录当前testcase覆盖的路径,还是所有的?
先去看看calibrate_case中的这个函数 hnb = has_new_bits(virgin_bits);看看能否获得答案(删减部分内容)
/* 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. */
static inline u8 has_new_bits(u8* virgin_map) {
// 跟run_target里面处理trace_bits类似,都是以8字节为单位进行处理的。i=8192
u64* current = (u64*)trace_bits;
u64* virgin = (u64*)virgin_map;
u32 i = (MAP_SIZE >> 3);
u8 ret = 0;
while (i--) {
/* Optimize for (*current & *virgin) == 0 - i.e., no bits in current bitmap
that have not been already cleared from the virgin map - since this will
almost always be the case. */
// trace_bits中包含该位(被覆盖),
if (unlikely(*current) && unlikely(*current & *virgin)) {
if (likely(ret < 2)) {
u8* cur = (u8*)current;
u8* vir = (u8*)virgin;
/* Looks like we have not found any new bytes yet; see if any non-zero
bytes in current[] are pristine in virgin[]. */
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)) ret = 2;
else ret = 1;
}
// 把覆盖到的位置0
*virgin &= ~*current;
}
current++;
virgin++;
}
if (ret && virgin_map == virgin_bits) bitmap_changed = 1;
return ret;
}
这个函数的作用是检查当前执行路径是否发现了新的覆盖点,即是否触发了新的分支或边。它会更新virgin_map参数,这是一个数组,用于保存后续模糊测试中覆盖的路径。函数的返回值有三种可能:
0:表示当前执行路径没有发现任何新的覆盖点,即与之前的测试用例没有区别。
1:表示当前执行路径只发现了新的hit-count,即某个分支或边的执行次数发生了变化,但没有发现新的分支或边。
2:表示当前执行路径发现了新的分支或边,即增加了覆盖率。
在网上搜索了下这个函数,仅有的几个中文博客写的都比较模糊,再大概解释下:
最前面那段注释,说的是ret(返回值),如果返回2,说明覆盖了新路径;如果返回1,说明路径的执行次数发生了变化;如果返回0,说明覆盖率没有发生变化。
*virgin &= ~*current;这句代码用来更新virgin_bits。
现在也理解为什么virgin_bits的注释是Regions yet untouched by fuzzing,virgin_bits的初始值全是1,如果某些路径被覆盖到了,就置0。(置1=Regions yet untouched=未被覆盖到)
而virgin翻译为:处女,童男;无经验的人,新手,这就合理了。
现在可以回答问题了,virgin_bits记录所有的testcase覆盖的路径。
到目前为止,把calibrate_case函数看了个差不多,明天继续往下看。
在分析fuzz_one havoc_stage的处理时,有这么一段代码:
simplify_trace((u64*)trace_bits);
// 如果没有触发新的覆盖,那么就返回函数
if (!has_new_bits(virgin_tmout)) return keeping;
simplify_trace把trace按字节进行了处理,被覆盖到的转换为0x80,没被覆盖到的转换为0x01,现在想要搞明白一个事情,simplify_trace的处理结果,会不会对has_new_bits的结果产生影响。
个人感觉这里逻辑有点难懂,simplify_trace处理后,trace_bits的每一位,要么是0x01,要么是0x80。
第一次调用has_new_bits(virgin_tmout),*current一定为真,*current & *virgin也一定为真,virgin_tmout的第1位,或者第8位会被置0。
如果第1位置0,下次输入为0x01,current & *virgin为0,不处理。
如果第1位置0,下次输入为0x80,current & *virgin为1,处理,返回1,第1,8位全置0,之后永远不处理。
如果第8位置0,下次输入为0x01,current & *virgin为1,处理,返回1,第1,8位全置0,之后永远不处理。
如果第8位置0,下次输入为0x80,current & *virgin为1,不处理。
第1位置0,下次输入为0x80,意味着本来没被覆盖,现在被覆盖了,返回1是合理的。
第8位置0,下次输入为0x01,意味着本来被覆盖,现在没被覆盖,返回1不合理。因为并没有带来新的覆盖率。
这个地方还是搞不太清楚,留着以后解决。