[AFL IV] afl-showmap
This tool displays raw tuple data captured by AFL instrumentation.
afl-showmap这个工具用来打印 一次程序执行过程中 被命中的执行路径
从这个工具中能看出, 执行路径是以什么方式存储的
举个🌰
还是以统计输入前缀中的‘a’个数为例子
┌──(root㉿Destroyer)-[/home/dustball/testafl]
└─# cat test.c
#include <stdio.h>
#include <unistd.h>
int main(void) {
char s[50];
read(0, s, 100);
int len = -1;
do {
len++;
} while(s[len] == 'a');
printf("%d\n", len);
return 0;
}
afl-showmap的 打印效果 是<路径哈希值:命中次数桶>元组
注意这里打印的是命中次数桶,不是命中次数
044520这条路径命中8,9,…,15次都会打印为5
命中4,5,6,7次会打印4
🧄法分析
afl系列工具的老套路, afl-showmap先处理命令行参数, 然后建立共享内存, 然后执行一次程序, 然后就应该打印命中的执行路径了
在write_results中
//u8* trace_bits = shmat(shm_id, NULL, 0); 就是共享内存, 用于记录执行路径
for (i = 0; i < MAP_SIZE; i++) { //64K
if (!trace_bits[i]) continue; //trace_bits[i] = 0 则表明: 哈希值为 i 的执行路径 没有被命中过
ret++;
if (cmin_mode) {
if (child_timed_out) break;
if (!caa && child_crashed != cco) break;
fprintf(f, "%u%u\n", trace_bits[i], i);
} else fprintf(f, "%06u:%u\n", i, trace_bits[i]); //哈希值为i的执行路径, 被命中次数为trace_bits[i]
}
trace_bits中的值是谁填写的呢?showmap中显然没有写操作.

可以这个trace_bits由afl-showmap创建, 之后afl-showmap对trace_bits只有读操作, 至于如何记录, 这是目标程序中的afl桩要干的事情
//桩干的事情:
current_path = _afl_prev_loc ^ _afl_current_loc;
_afl_prev_loc = _afl_current_loc;
_afl_prev_loc = _afl_prev_loc >> 1;
++trace_bits[current_path];
在目标程序执行完之后, afl-showmap会遍历一遍trace_bits, 进行一个按桶分类
static void classify_counts(u8* mem, const u8* map) {
u32 i = MAP_SIZE; //从后往前遍历
if (edges_only) { //只记录有没有命中, 不统计命中次数
while (i--) {
if (*mem) *mem = 1;
mem++;
}
} else {
while (i--) {
*mem = map[*mem]; //将实际的命中次数*mem经过map映射一下
mem++;
}
}
}
这个桶实际上就是一个映射:
static const u8 count_class_human[256] = {
[0] = 0,
[1] = 1,
[2] = 2,
[3] = 3,
[4 ... 7] = 4,
[8 ... 15] = 5,
[16 ... 31] = 6,
[32 ... 127] = 7,
[128 ... 255] = 8 //总共有8种桶, 这个映射也就是将命中次数转化为桶编号
};
static const u8 count_class_binary[256] = {
[0] = 0,
[1] = 1,
[2] = 2,
[3] = 4,
[4 ... 7] = 8,
[8 ... 15] = 16,
[16 ... 31] = 32,
[32 ... 127] = 64,
[128 ... 255] = 128 //总共有8个桶,这个映射也就是将命中次数映射为一个代表命中数字
};
因此经过count_class_human的映射后, 假设某条路径命中次数为7, 那么实际得到的trace_bits值就是4, 意思是落到了第4个桶里
至于为甚么只记录8个桶子, 命中次数最大值是255
这是因为trace_bits以字节为单元, 一个字节代表一个执行路径元组. 一个字节最多就能表示[0,255],再命中一次就会整数上溢, 回绕到0
但是开发者认为可以为了性能牺牲这个准确性, 凑活着用吧