afl-as粗析
afl-as粗析
Ivoripuionafl-as.c粗析
查看main
函数,关键的就一句:
1 | if (!just_version) add_instrumentation(); |
即在汇编文件中添加插桩代码。
该函数中关键部分:
1 | if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok && |
即通过汇编判断当前是不是一个分支语句或者函数,然后根据环境是x86还是x86-64添加汇编代码trampoline_fmt_32
抑或是trampoline_fmt_64
。
这里分析trampoline_fmt_32
,trampoline_fmt_32
定义在afl-as.h中:
1 | static const u8* trampoline_fmt_32 = |
内容大致就是首先保存上下文环境,将ecx
的值设置为fprintf()
所要打印的变量内容,然后调用__afl_maybe_log
函数,调用完毕恢复上下文环境。
__afl_maybe_log
首先检查__afl_area_ptr
是否为0,是0则跳转至__afl_setup
:
1 | movl __afl_area_ptr, %edx |
__afl_setup
1 | movl __afl_area_ptr, %edx |
__afl_area_ptr
存储的内容为共享内存(当前tuple),若共享内存未设置就跳转至__afl_setup
进行共享内存的设置:
1 | cmpb $0, __afl_setup_failure |
进行一些检查,然后获取.AFL_SHM_ENV
,然后使用atoi
转化为int
型,即为SHM ID
,共享内存的标识符。
然后使用shmat
设置共享内存:
1 | pushl $0 |
最后将共享内存地址保存在__afl_area_ptr
:
1 | movl %eax, __afl_area_ptr |
__afl_setup
结束就会进入__afl_forkserver
部分。
__afl_forkserver
这部分的内容与lcamtuf的文章《Fuzzing random programs without execve()》大致差不多,作用就是为了避免过早的调用execve()
函数。
译文:
这部分执行完成会进入__afl_store
:
1 | popl %edx |
该部分用于保存分支信息。
__afl_store
这里__afl_prev_loc
保存的是前一个运行到的位置,ecx
保存的是当前运行到的位置,edx
保存的是共享内存的地址:
1 | movl __afl_prev_loc, %edi |
首先把前一个运行到的位置保存到edi
中,然后与ecx
进行抑或操作,将ecx
右移1位,把ecx
存储到__afl_prev_loc
中,最后将edx[edi]
加1。
在trampoline_fmt_32
定义中,标识处了format
参数为ecx:
1 | "movl $0x%08x, %%ecx\n" |
结合输出函数:
1 | fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, |
可知:ecx
内容即为R(MAP_SIZE)
,是一个(0-2^16)
的随机数。
所以上述的AT&T汇编伪代码对应的就是afl白皮书里关于记录分支信息的伪代码:
1 | cur_location = <COMPILE_TIME_RANDOM>;//当前位置信息,一个随机数 |
即在每个插桩的地方,afl-as设置一个随机数标识当前分支信息,然后将前一个随机数和当前随机数抑或后存放到共享内存中,计数器加1累加分支语句执行的次数。