AFL源码分析(二)
本文最后更新于:2023年10月6日 晚上
AFL源码分析(二)
afl-as.c
main
1 |
|
为了方便查看as_params的值,我们追加如下代码:
1 |
|
当我们执行afl-as -o myafl.o myafl.s
时,输出如下:
1 |
|
可以看到,最终执行的是as。
由此可见,afl-as也是对as进行的再封装。
edit_params
解析并修改传递给as的变量。
1 |
|
add_instrumentation
处理输入文件,对汇编文件基本块进行插桩。
1 |
|
插桩代码分析
插桩过程中,我们插入了trampoline_fmt_64
与main_payload_64
。
接下来,对插入的内容进行分析
1 |
|
这个汇编格式为AT&T格式,在IDA中反编译插桩过的源码,可得到intel格式的汇编。
1 |
|
主要含义为:
- 为变量分配栈空间
- 保存rdx,rcx,rax的值
- 将rcx的值设置为R(MAP_SIZE),作为桩代码标志
- 调用__afl_maybe_log
- 恢复rdx,rcx,rax的值
重点调用__afl_maybe_log
函数,对该函数进行分析。
笔者自知实力不足,以下分析主要引用深信服千里目安全实验室。
首先,对上述引用的变量进行解释。(参考main_payload_64
)
1 |
|
定义了以下变量:
__afl_area_ptr: 共享内存地址
__afl_prev_loc: 上一个插桩位置
__afl_fork_pid: fork server产生的子进程的pid
__afl_global_area_ptr: 临时变量
__afl_setup_failure: 失败标识位
__afl_global_area_ptr: 全局指针
这里主要借IDA中intel格式汇编进行分析。
__afl_maybe_log
1 |
|
首先,使用 lahf
指令(加载状态标志位到AH
)将EFLAGS寄存器的低八位复制到 AH
,被复制的标志位包括:符号标志位(SF)、零标志位(ZF)、辅助进位标志位(AF)、奇偶标志位(PF)和进位标志位(CF),使用该指令可以方便地将标志位副本保存在变量中;
然后,使用 seto
指令溢出置位;
最后,判断__afl_area_ptr
是否为空。若为空跳转到__afl_setup
,否则继续执行。
__afl_setup
1 |
|
首先,判断__afl_setup_failure
是否为空,即判断是否出错。若出错直接返回,否则跳转到__afl_return
。
其次,判断__afl_global_area_ptr
是否为空。若为空,则跳转到__afl_setup_first
,否则将__afl_global_area_ptr
赋值给__afl_area_ptr
,调用__afl_store
。
__afl_setup_first
1 |
|
首先,保存寄存器的值,将rsp进行16字节对齐
然后,调用getenv
获取共享内存的id(共享内存id在afl-fuzz中设置)。若获取成功,则继续向下执行;若获取失败,则跳转到__afl_setup_abort
。
其次,调用atoi
将共享id转换为int型,调用shmat
启动共享内存。若成功,则继续向下执行;否则,跳转到__afl_setup_abort
。
最后,把共享内存地址赋值给__afl_area_ptr
与__afl_global_area_ptr
__afl_forkserver
1 |
|
向FORKSRV_FD + 1
(管道)写入__afl_temp
,告诉父程序,fork server启动成功。
__afl_fork_wait_loop
1 |
|
- 从
FORKSRV_FD
,即控制管道中获取指令,读入4bytes到__afl_temp
。若读取成功,继续执行;若读取失败,跳转到__afl_die
。 - 调用
fork
生成子进程,子进程调用__afl_fork_resume
- 父进程调用
write
将子进程的pid写入到FORKSRV_FD + 1
中,并调用waitpid
等待子进程结束,并将字进程状态信息写入到FORKSRV_FD + 1
中
__afl_fork_resume
1 |
|
由于父子进程共享文件描述符,子进程关闭FORKSRV_FD
与FORKSRV_FD + 1
,恢复寄存器的值,调用__afl_store
。
__afl_store
1 |
|
这里直接查看反编译源码:
1 |
|
其中a4为插入的随机值,也即rcx, 而 _afl_prev_loc
其实是上一个桩的随机id。
经过两次异或之后,再将 _afl_prev_loc
右移一位作为新的 _afl_prev_loc
,避免诸如此类路径(A -> A, B -> B, A -> B, B -> A)混淆。最后在共享内存中存储当前插桩位置的地方计数加一。
参考链接
https://eternalsakura13.com/2020/08/23/afl/
https://hollk.blog.csdn.net/category_11470526.html
https://paper.seebug.org/1732/
https://www.z1r0.top/2023/03/23/AFL-fuzz%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/