我感觉我一行一行写注释这种行为本身就很傻。。
关键的地方作者都写过注释了,我就觉得我这一行行写很不合适,下次读源码还是用眼看吧,想记得地方再写文档上吧,这次接上次还是参考的https://bbs.pediy.com/thread-265936.htm,这份博客,发现可能是版本差异,有些源码不同,不过不碍事,其实注释算是硬写上去的,就把作者的英文注释翻译了一下,我真觉得我这一行行写注释行为太不可取了,真的
我就只贴个main吧,全贴发不出来
我发到github上了,向要的可以自己拿,在这里
也可以在我博客上看,这才8000多行csdn就发不上来了
afl-fuzz.c
main
int main(int argc, char** argv) {
//妈的main真长。。。。
s32 opt;
u64 prev_queued = 0;
u32 sync_interval_cnt = 0, seek_to;
u8 *extras_dir = 0;
u8 mem_limit_given = 0;
u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE");
char** use_argv;
struct timeval tv;
struct timezone tz;
SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
//首先调用gettimeofday
gettimeofday(&tv, &tz);
//根据时间与当前进程pid做异或来设定种子,保证了随机性
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
//一个很大很大的循环,通过getopt扫描argv里面的参数,类似如下
// /Users/apple/Desktop/AFL/AFL/cmake-build-debug/afl-fuzz -i input -o output -- ./test
while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Q")) > 0)
//扫描到的字符,对应的操作
switch (opt) {
//如果是-i
case 'i': /* input dir */
if (in_dir) FATAL("Multiple -i options not supported");
//将-i之后的input赋值给in_dir,optarg没找到在哪声明的
in_dir = optarg;
//如果此时的 in_dir = "-",那么设置 - in_place_resume = 1
if (!strcmp(in_dir, "-")) in_place_resume = 1;
break;
//扫到-o
case 'o': /* output dir */
if (out_dir) FATAL("Multiple -o options not supported");
//将-o的参数output赋值给out_dir
out_dir = optarg;
break;
//剩下的以下这些case以后再分析
case 'M': { /* master sync ID */
u8* c;
if (sync_id) FATAL("Multiple -S or -M options not supported");
sync_id = ck_strdup(optarg);
if ((c = strchr(sync_id, ':'))) {
*c = 0;
if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 ||
!master_id || !master_max || master_id > master_max ||
master_max > 1000000) FATAL("Bogus master ID passed to -M");
}
force_deterministic = 1;
}
break;
case 'S':
if (sync_id) FATAL("Multiple -S or -M options not supported");
sync_id = ck_strdup(optarg);
break;
case 'f': /* target file */
if (out_file) FATAL("Multiple -f options not supported");
out_file = optarg;
break;
case 'x': /* dictionary */
if (extras_dir) FATAL("Multiple -x options not supported");
extras_dir = optarg;
break;
case 't': { /* timeout */
u8 suffix = 0;
if (timeout_given) FATAL("Multiple -t options not supported");
if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 ||
optarg[0] == '-') FATAL("Bad syntax used for -t");
if (exec_tmout < 5) FATAL("Dangerously low value of -t");
if (suffix == '+') timeout_given = 2; else timeout_given = 1;
break;
}
case 'm': { /* mem limit */
u8 suffix = 'M';
if (mem_limit_given) FATAL("Multiple -m options not supported");
mem_limit_given = 1;
if (!strcmp(optarg, "none")) {
mem_limit = 0;
break;
}
if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 ||
optarg[0] == '-') FATAL("Bad syntax used for -m");
switch (suffix) {
case 'T': mem_limit *= 1024 * 1024; break;
case 'G': mem_limit *= 1024; break;
case 'k': mem_limit /= 1024; break;
case 'M': break;
default: FATAL("Unsupported suffix or bad syntax for -m");
}
if (mem_limit < 5) FATAL("Dangerously low value of -m");
if (sizeof(rlim_t) == 4 && mem_limit > 2000)
FATAL("Value of -m out of range on 32-bit systems");
}
break;
case 'd': /* skip deterministic */
if (skip_deterministic) FATAL("Multiple -d options not supported");
skip_deterministic = 1;
use_splicing = 1;
break;
case 'B': /* load bitmap */
/* This is a secret undocumented option! It is useful if you find
an interesting test case during a normal fuzzing process, and want
to mutate it without rediscovering any of the test cases already
found during an earlier run.
To use this mode, you need to point -B to the fuzz_bitmap produced
by an earlier run for the exact same binary... and that's it.
I only used this once or twice to get variants of a particular
file, so I'm not making this an official setting. */
if (in_bitmap) FATAL("Multiple -B options not supported");
in_bitmap = optarg;
read_bitmap(in_bitmap);
break;
case 'C': /* crash mode */
if (crash_mode) FATAL("Multiple -C options not supported");
crash_mode = FAULT_CRASH;
break;
case 'n': /* dumb mode */
if (dumb_mode) FATAL("Multiple -n options not supported");
if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1;
break;
case 'T': /* banner */
if (use_banner) FATAL("Multiple -T options not supported");
use_banner = optarg;
break;
case 'Q': /* QEMU mode */
if (qemu_mode) FATAL("Multiple -Q options not supported");
qemu_mode = 1;
if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU;
break;
default:
usage(argv[0]);
}
if (optind == argc || !in_dir || !out_dir) usage(argv[0]);
//参数处理完毕后,调用setup_siginal_handlers设置信号处理句柄
setup_signal_handlers();
//调用check_asan_opts见哈asan设置是否正确
check_asan_opts();
//上面这俩函数的实现都比较短
//如果设置了sync_id,那么在fix_up_sync中检测是否有互斥的参数
if (sync_id) fix_up_sync();
//检查是否一样
if (!strcmp(in_dir, out_dir))
FATAL("Input and output directories can't be the same");
//如果设置了dumb_mode,那么不能设置crash_mode或qemu_mode,因为互斥
if (dumb_mode) {
if (crash_mode) FATAL("-C and -n are mutually exclusive");
if (qemu_mode) FATAL("-Q and -n are mutually exclusive");
}
//获取几个环境变量,如果有就设置对应值
if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1;
if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1;
if (getenv("AFL_NO_ARITH")) no_arith = 1;
if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1;
if (getenv("AFL_FAST_CAL")) fast_cal = 1;
//通过AFL_HANG_TMOUT设置对应的hang_out时间
if (getenv("AFL_HANG_TMOUT")) {
//ascii转int
hang_tmout = atoi(getenv("AFL_HANG_TMOUT"));
if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT");
}
//dumb_mode == 2 与 no_forkserver互斥
if (dumb_mode == 2 && no_forkserver)
FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive");
//设置LD_PRELOAD和DYLD_INSERT_LIBRARIES为AFL_PRELOAD
if (getenv("AFL_PRELOAD")) {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
}
if (getenv("AFL_LD_PRELOAD"))
FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD");
//save_cmdline 保存argv到局部变量buf中,代码很短自己看一下
save_cmdline(argc, argv);
//又是检查,比较短,不想看了
fix_up_banner(argv[optind]);
//检查!!检查是否是终端环境
check_if_tty();
//获取cpu数量,还分平台
get_core_count();
//然后根据亲缘性设置绑定CPU
//这玩意我不懂。。,回头再分析吧
#ifdef HAVE_AFFINITY
bind_to_free_cpu();
#endif /* HAVE_AFFINITY */
//检查core dump设置是否正确
check_crash_handling();
//字面意思
check_cpu_governor();
//加载进来一个动态链接库,获取里面的后处理程序
setup_post();
//设置共享内存和virgin_bits
setup_shm();
//初始化count_class_lookup16数组,为分支路径的规整做准备
init_count_class16();
//创建一些相关文件夹,写入一些信息
//准备输出到的文件以及fds
setup_dirs_fds();
//读取测试用例并入队,在启动时调用
read_testcases();
//加载automatically generated extras
load_auto();
//在输出目录中为输入测试用例创建硬链接,选择好名字,并据此进行调整
pivot_inputs();
//如果设置了extras_dir
//就调用函数加载extras_dir下的文件放入extras数组并排序
if (extras_dir) load_extras(extras_dir);
//如果没设置timeout_given那么调用find_timeout设置超时时间
if (!timeout_given) find_timeout();
//检测sargs中的'@@'并替换
detect_file_args(argv + optind + 1);
//如果没设置out_file,调用函数
//在output/.cur_input新建一个文件作为输出
if (!out_file) setup_stdio_file();
//检查目标文件的有效性,是否是可执行文件,是否式Mach-O还是ELF还是一个生成的shell文件
check_binary(argv[optind]);
//获取当前时间
start_time = get_cur_time();
//如果是qemu_mode
if (qemu_mode)
//为qemu重写参数
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
else
//否则简单粗暴
use_argv = argv + optind;
//对所有测试用例执行试运行,以确认该应用程序按照预期正常运行。
//仅对初始输入执行此操作,并且仅执行一次
//好些我都是抄的注释(狗头
perform_dry_run(use_argv);
//精简队列
cull_queue();
//向用户展示初始化状态信息
show_init_stats();
//进去
seek_to = find_start_position();
//更新统计信息文件以进行无人值守的监视
write_stats_file(0, 0, 0);
//前面调用过一次
save_auto();
//如果设置了stop_soon,就跳转到stop_fuzzing
if (stop_soon) goto stop_fuzzing;
/* Woop woop woop */
//如果是tty启动(终端),那么先sleep 4 秒,start_time += 4000
if (!not_on_tty) {
sleep(4);
start_time += 4000;
//再检测如果设置了stop_soon,跳转到stop_fuzzing
if (stop_soon) goto stop_fuzzing;
}
//至此,整个启动前的初始化完成,准备进入fuzz主循环
while (1) {
u8 skipped_fuzz;
//简化队列
cull_queue();
//queue_cur指向目前队列中的元素
//初始化是空的,不为空说明扫过一次了
if (!queue_cur) {
//循环计数器加1
queue_cycle++;
//归零
current_entry = 0;
//归零
cur_skipped_paths = 0;
//重新指向队头,afl就是这么可怕
queue_cur = queue;
//如果seek_to不为0,没注释的局部变量
while (seek_to) {
current_entry++;
//直达seek_to==0才会停,似乎是用seek_to记录上次队列位置,这次从那开始
seek_to--;
//queue_cur后移
queue_cur = queue_cur->next;
}
//展示状态
show_stats();
//如果不是终端模式即:not_on_tty==1
if (not_on_tty) {
//输出当前是第几个循环
ACTF("Entering queue cycle %llu.", queue_cycle);
//清输出缓存
fflush(stdout);
}
/* If we had a full queue cycle with no new finds, try
recombination strategies next. */
//如果我们经历了一个完整的扫描周期后都没有新的路径发现,那么尝试调整策略
//如果queue_path==prev_queued
if (queued_paths == prev_queued) {
//当设置了use_splicing,cycles_wo_finds计数加1,否则use_splicing为1
if (use_splicing) cycles_wo_finds++; else use_splicing = 1;
//否则设置cycles_wo_finds为0
} else cycles_wo_finds = 0;
//令prev_queued等于queued_paths
prev_queued = queued_paths;
//如果设置了syn_id并且queue_cycle==1并且环境变量中有这个
if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))
//调用syn_fuzzers,同步其他fuzzer
sync_fuzzers(use_argv);
}
//调用fuzz_one(use_argv)对于我们的样本进行变换后fuzz,返回skipped_fuzz
skipped_fuzz = fuzz_one(use_argv);
//如果skipped_fuzz为且这样这样
if (!stop_soon && sync_id && !skipped_fuzz) {
//如果sync_interval_cnt没有到一个周期
if (!(sync_interval_cnt++ % SYNC_INTERVAL))
//调用同步其他fuzzer
sync_fuzzers(use_argv);
}
//如果没有设置stop_soon,且 exit_1不为0
//那么设置stop_soon=2后break处fuzz主循环
if (!stop_soon && exit_1) stop_soon = 2;
if (stop_soon) break;
//否则准备下一个样本
queue_cur = queue_cur->next;
current_entry++;
}
//主循环结束了,输出/更新一些状态
if (queue_cur) show_stats();
write_bitmap();
write_stats_file(0, 0, 0);
save_auto();
stop_fuzzing:
SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST,
stop_soon == 2 ? "programmatically" : "by user");
/* Running for more than 30 minutes but still doing first cycle? */
if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) {
SAYF("\n" cYEL "[!] " cRST
"Stopped during the first cycle, results may be incomplete.\n"
" (For info on resuming, see %s/README.)\n", doc_path);
}
//主循环结束后,销毁内存空间
//关闭描述符,至此整个afl的fuzz过程就结束了
fclose(plot_file);
destroy_queue();
destroy_extras();
ck_free(target_path);
ck_free(sync_id);
alloc_report();
OKF("We're done here. Have a nice day!\n");
exit(0);
}
#endif /* !AFL_LIB */