共享内存中的bitmap结构 && forkserver机制
/* --- AFL MAIN PAYLOAD (32-BIT) --- */
.text
.att_syntax
.code32
.align 8
__afl_maybe_log:
lahf
seto %al //溢出置位
/* Check if SHM region is already mapped. */
movl __afl_area_ptr, %edx //检查共享内存有没有进行设置,判断是否为NULL
testl %edx, %edx //如果为空,跳转到__afl__setup进行设置
je __afl_setup
__afl_store:
/* Calculate and store hit for the code location specified in ecx. There
is a double-XOR way of doing this without tainting another register,
and we use it on 64-bit systems; but it's slower for 32-bit ones. */
#ifndef COVERAGE_ONLY
movl __afl_prev_loc, %edi ;edi = prev_location ecx = cur_location
xorl %ecx, %edi ;cur_location ^ prev_location edi= cur_location ^ prev_location
shrl $1, %ecx ;prev_location = cur_location >> 1;
movl %ecx, __afl_prev_loc
#else
movl %ecx, %edi
#endif /* ^!COVERAGE_ONLY */
#ifdef SKIP_COUNTS
orb $1, (%edx, %edi, 1)
#else
;把共享内存单元中的计数数值加一, 也就是所谓的命中数
;shared_mem[cur_location ^ prev_location]++;
incb (%edx, %edi, 1) ;edx = shared_mem edi= cur_location ^ prev_location
#endif /* ^SKIP_COUNTS */
__afl_return:
addb $127, %al
sahf
ret ;难道说, 在运行完一次之后, 这个插桩的汇编程序又重新回到了__afl__maybe__log中了????
;也不是说运行完一次就回到最开始了,而是运行第一个桩之后就到了第二个桩呗?
.align 8
__afl_setup: ;初始化__afl__area__ptr,且只在运行到第一个桩时进行本次初始化
/* Do not retry setup if we had previous failures. */
cmpb $0, __afl_setup_failure
jne __afl_return
/* Map SHM, jumping to __afl_setup_abort if something goes wrong.
We do not save FPU/MMX/SSE registers here, but hopefully, nobody
will notice this early in the game. */
pushl %eax
pushl %ecx
pushl $.AFL_SHM_ENV
call getenv
addl $4, %esp
testl %eax, %eax
je __afl_setup_abort
pushl %eax
call atoi
addl $4, %esp
pushl $0 /* shmat flags */
pushl $0 /* requested addr */
pushl %eax /* SHM ID */
call shmat
addl $12, %esp
cmpl $-1, %eax
je __afl_setup_abort
/* Store the address of the SHM region. */
movl %eax, __afl_area_ptr
movl %eax, %edx
popl %ecx
popl %eax
__afl_forkserver:
/* Enter the fork server mode to avoid the overhead of execve() calls. */
pushl %eax
pushl %ecx
pushl %edx
/* Phone home and tell the parent that we're OK. (Note that signals with
no SA_RESTART will mess it up). If this fails, assume that the fd is
closed because we were execve()d from an instrumented binary, or because
the parent doesn't want to use the fork server. */
pushl $4 /* length */
pushl $__afl_temp /* data */
pushl $ STRINGIFY((FORKSRV_FD + 1)) /* file desc */
call write
addl $12, %esp
cmpl $4, %eax
jne __afl_fork_resume
__afl_fork_wait_loop:
/* Wait for parent by reading from the pipe. Abort if read fails. */
pushl $4 /* length */
pushl $__afl_temp /* data */
pushl $ STRINGIFY(FORKSRV_FD) /* file desc */
call read
addl $12, %esp
cmpl $4, %eax
jne __afl_die
/* Once woken up, create a clone of our process. This is an excellent use
case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly
caches getpid() results and offers no way to update the value, breaking
abort(), raise(), and a bunch of other things :-( */
call fork
cmpl $0, %eax
jl __afl_die ;创建子进程失败后,就退出程序
je __afl_fork_resume ;如果是子进程的话, 进入afl_fork_resume中
/* In parent process: write PID to pipe, then wait for child. */
movl %eax, __afl_fork_pid ;此时 eax中保存的是产生的子进程的pid, 将子进程pid保存到__afl_fork_pid中
pushl $4 /* length */
pushl $__afl_fork_pid /* data */
pushl $ STRINGIFY((FORKSRV_FD + 1)) /* file desc */
call write
addl $12, %esp
pushl $0 /* no flags */ ;等待子进程结束
pushl $__afl_temp /* status */
pushl __afl_fork_pid /* PID */
call waitpid ;成功:返回清理掉的子进程ID
addl $12, %esp
cmpl $0, %eax
jle __afl_die ;如果子进程不是正常退出的话, 就结束掉整个进程
/* Relay wait status to pipe, then loop back. */
pushl $4 /* length */
pushl $__afl_temp /* data */
pushl $ STRINGIFY((FORKSRV_FD + 1)) /* file desc */
call write
addl $12, %esp
jmp __afl_fork_wait_loop
__afl_fork_resume:
/* In child process: close fds, resume execution. */
pushl $ STRINGIFY(FORKSRV_FD) ;为什么要关闭fds呢? 不想让子进程干扰到父进程在管道中 写/读 数据
call close
pushl $ STRINGIFY((FORKSRV_FD + 1))
call close
addl $8, %esp
popl %edx
popl %ecx
popl %eax
jmp __afl_store
__afl_die:
xorl %eax, %eax
call _exit
__afl_setup_abort:
/* Record setup failure so that we don't keep calling
shmget() / shmat() over and over again. */
incb __afl_setup_failure
popl %ecx
popl %eax
jmp __afl_return
.AFL_VARS: //
;.comm 声明未初始化的数据的通用内存区域
.comm __afl_area_ptr, 4, 32 //共享内存地址
.comm __afl_setup_failure, 1, 32 //标志位,如果标志置位,退出程序
#ifndef COVERAGE_ONLY
.comm __afl_prev_loc, 4, 32 //上一个插桩位置(id为R(100)的随机数)
#endif /* !COVERAGE_ONLY */
.comm __afl_fork_pid, 4, 32 //由fork产生的子进程的pid
.comm __afl_temp, 4, 32 //缓冲区
.AFL_SHM_ENV:
.asciz \ SHM_ENV_VAR \
/* --- END --- */
;