这个头文件里面放着的是汇编器编译汇编到二进制时插入的汇编代码。
这里只分析64位的
trampoline_fmt_64
1 | static const u8* trampoline_fmt_64 = |
首先在栈上开辟一段空间,然后将rdx,rcx,rax这三个寄存器的值保存到栈上,将rcx的值赋值为一个随机数,这个随机数是插入这段汇编的时候动态传进来的。然后调用_afl_maybe_log
,调用完之后,把栈上保存的值恢复回去,再恢复栈。
main_payload_64
__afl_maybe_log
1 | /*lahf |
1 | " /* Check if SHM region is already mapped.(检查共享内存是否已经加载, |
__afl_store
1 | "\n" |
__afl_return
1 | /*先将al+0x7f,然后再把标志寄存器FLAGS的值从AH中恢复回去*/ |
__afl_setup
1 | /*首先判断之前有没有错误,如果有,直接返回*/ |
1 | " /* Check out if we have a global pointer on file. |
__afl_setup_first
1 | /*将剩下所有会被libc库函数影响的寄存器保存到栈上面*/ |
1 | /*这里先保存r12,然后将栈指针保存到r12里面,再开一段栈空间,进行对齐*/ |
1 | /*这里就是调用getenv去拿存在环境变量中的共享内存标志符,拿不到的话,就会跳到__afl_setup_abort*/ |
1 | /*这里调用atoi将字符串转为数字,然后调用shmat拿到共享内存,然后判断一下shamat |
1 | /*这里是把共享内存的地址存到afl_area_ptr和afl_global_area_ptr指向的内存*/ |
__afl_forkserver
接下来就是fork server的逻辑了
1 | /*先是push两次rdx来使得栈整齐一点 |
__afl_fork_wait_loop
1 | /*这里是不断地从管道中读取内容,假如读取到的字节数不为4就会跳到__afl_die*/ |
1 | /*读取成功:这里先fork了,然后判断 fork是否成功,如果成功就会跳到__afl_fork_resume |
1 | /*这里是父进程等待子进程,如果 waitpid返回的结果小于等于0,就会跳afl_die,waitpid |
1 | /*然后把子进程的状态通过管道写回到fuzzer中,跳回到__afl_fork_wait_loop,继续 |
__afl_fork_resume
1 | /*这里把两个管道给关掉*/ |
1 | /*然后把各种寄存器恢复,跳到__afl_store*/ |
__afl_die
1 | /*这里就是简单的exit*/ |
__afl_setup_abort
1 | /*这里就是设置__afl_setup_failure为1,然后恢复下寄存器,直接返回*/ |
总结:
首先afl-fuzz这个程序会创建两个管道,然后利用afl-gcc或者afl-clang编译的程序,就会被执行,
之前在afl-as.c中也分析到了,main函数肯定会被插桩的,也就是肯定会调用__afl_maybe_log
而对于第一次运行的进程,就会作为fork-server,后面的由fork-server fork出来的才是真正被fuzz的程序
然后fork-server不断地等待fuzzer的指令去fork子进程,用waitpid去拿到子进程的结束状态,写回给fuzzer