首先来看下main函数
1 | u8* inst_ratio_str = getenv("AFL_INST_RATIO"); |
先是从环境变量中拿了AFL_INST_RATIO,应该是插入指令的密度
之后一大堆东西略过一下
1 | gettimeofday(&tv, &tz); |
这里是根据时间和pid来随机化seed
再下面是把 inst_ratio_str转为数字
1 | if (inst_ratio_str) { |
之后比较有趣的一部分代码
1 | if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { |
如果使用 ASAN或者MSAN的话,就会把插入指令的密度降低为 1/3,以加快速度
之后就是关键的add_instrumentation函数
1 | if (input_file) { |
首先是打开汇编代码文件
1 | outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600); |
然后打开一个新的文件
然后就是循环读每一行,进行判断
1 | if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok && |
一开始就是判断一堆条件,假如满足这些条件的话,就插入指令
1 | fputs(line, outf); |
之后再输出原来那行
1 | if (line[0] == '\t' && line[1] == '.') { |
这里是找 .text在的那一行,也就是代码段
1 | if (strstr(line, ".code")) { |
这里是判断是32位还是64位的
1 | /* If we're in the right mood for instrumenting, check for function |
这段注释大概就是说,在 main函数,GCC branch label,clang branch label,conditional branches处插入指令
1 | if (line[0] == '\t') { |
这里就是找到 j开头,但是第二个字母不是m的指令,如jne, jbe指令的
后面还有 R(100) < inst_ratio 这个就是根据概率来选择插入或者不插入
1 | if (ins_lines) |
这里还会插入 main_payload
插入的两个汇编有点长,这里就不仔细分析了
1 | if (!(pid = fork())) { |
插入完指令后,会fork子进程,用来执行as,将汇编变成二进制