接下来看看源码,今天先看一部分不一定能看完(大概率看不完吧)由于相当大一部分是AFLfuzz的代码,就不重点看这些,主要看看AFLNET做了些什么。
前面都是一些参数配置的检查部分,新增了AFLNET的参数,主要是配置网络信息和选择使用的协议,具体参数可以参考AFLNET的官方文档。
//9132行
//AFLNet - Check for required arguments
if (!use_net) FATAL("Please specify network information of the server under test (e.g., tcp://127.0.0.1/8554)");
if (!protocol_selected) FATAL("Please specify the protocol to be tested using the -P option");
if (netns_name) {
if (check_ep_capability(CAP_SYS_ADMIN, argv[0]) != 0)
FATAL("Could not run the server under test in a \"%s\" network namespace "
"without CAP_SYS_ADMIN capability.\n You can set it by invoking "
"afl-fuzz with sudo or by \"$ setcap cap_sys_admin+ep /path/to/afl-fuzz\".", netns_name);
}
//9142行
进行初始化
//9194行
check_crash_handling();
check_cpu_governor();
setup_post();
setup_shm();
init_count_class16();
setup_ipsm();
setup_dirs_fds();
read_testcases();
load_auto();
pivot_inputs();
if (extras_dir) load_extras(extras_dir);
if (!timeout_given) find_timeout();
detect_file_args(argv + optind + 1);
if (!out_file) setup_stdio_file();
check_binary(argv[optind]);
start_time = get_cur_time();
//9219行
这里初始化时调用了setup_ipsm()函数用于初始化状态机。函数细节等分析完main函数后进行分析。read_testcasese()读取所有测试用例,load_auto()是加载自动生成的额外内容。之后pivot_inputs()在输出目录中为输入的测试用例创建硬链接(/* Create hard links for input test cases in the output directory, choosing good names and pivoting accordingly. */)
perform_dry_run(use_argv);
cull_queue();
show_init_stats();
seek_to = find_start_position();
write_stats_file(0, 0, 0);
save_auto();
perform_dry_run()在AFLfuzz中是先把所有测试用例跑一次确保程序正确运行,只在初始化输入时执行且只执行一次。但是由于AFLNET会在此更新状态机,所以这个函数也需要分析。
cull_queue()没有变化,就是简化队列。
show_init_stats();输入目录处理结束后的一些工作。
seek_to = find_start_position();找到队列起始位置。
if (state_aware_mode) {
if (state_ids_count == 0) {
PFATAL("No server states have been detected. Server responses are likely empty!");
}
while (1) {
u8 skipped_fuzz;
struct queue_entry *selected_seed = NULL;
while(!selected_seed || selected_seed->region_count == 0) {
target_state_id = choose_target_state(state_selection_algo);
/* Update favorites based on the selected state */
cull_queue();
/* Update number of times a state has been selected for targeted fuzzing */
khint_t k = kh_get(hms, khms_states, target_state_id);
if (k != kh_end(khms_states)) {
kh_val(khms_states, k)->selected_times++;
}
selected_seed = choose_seed(target_state_id, seed_selection_algo);
}
/* Seek to the selected seed */
if (selected_seed) {
if (!queue_cur) {
current_entry = 0;
cur_skipped_paths = 0;
queue_cur = queue;
queue_cycle++;
}
while (queue_cur != selected_seed) {
queue_cur = queue_cur->next;
current_entry++;
if (!queue_cur) {
current_entry = 0;
cur_skipped_paths = 0;
queue_cur = queue;
queue_cycle++;
}
}
}
skipped_fuzz = fuzz_one(use_argv);
if (!stop_soon && sync_id && !skipped_fuzz) {
if (!(sync_interval_cnt++ % SYNC_INTERVAL))
sync_fuzzers(use_argv);
}
if (!stop_soon && exit_1) stop_soon = 2;
if (stop_soon) break;
}
} else {
while (1) {
u8 skipped_fuzz;
cull_queue();
if (!queue_cur) {
queue_cycle++;
current_entry = 0;
cur_skipped_paths = 0;
queue_cur = queue;
while (seek_to) {
current_entry++;
seek_to--;
queue_cur = queue_cur->next;
}
show_stats();
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. */
if (queued_paths == prev_queued) {
if (use_splicing) cycles_wo_finds++; else use_splicing = 1;
} else cycles_wo_finds = 0;
prev_queued = queued_paths;
if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST"))
sync_fuzzers(use_argv);
}
skipped_fuzz = fuzz_one(use_argv);
if (!stop_soon && sync_id && !skipped_fuzz) {
if (!(sync_interval_cnt++ % SYNC_INTERVAL))
sync_fuzzers(use_argv);
}
if (!stop_soon && exit_1) stop_soon = 2;
if (stop_soon) break;
queue_cur = queue_cur->next;
current_entry++;
}
}
这部分代码是启用了感知机模式即使用状态机指导fuzzing的代码。
首先如果state_ids_count==0的话表示服务器状态数为0即服务器没有返回响应报文,这时报错退出。这也说明之前的state_ids_count记录的是状态机的状态数。
while(1)部分即进行fuzzing的部分。
while (1) {
u8 skipped_fuzz;
struct queue_entry *selected_seed = NULL;
while(!selected_seed || selected_seed->region_count == 0) {
target_state_id = choose_target_state(state_selection_algo);
/* Update favorites based on the selected state */
cull_queue();
/* Update number of times a state has been selected for targeted fuzzing */
khint_t k = kh_get(hms, khms_states, target_state_id);
if (k != kh_end(khms_states)) {
kh_val(khms_states, k)->selected_times++;
}
selected_seed = choose_seed(target_state_id, seed_selection_algo);
}
/* Seek to the selected seed */
if (selected_seed) {
if (!queue_cur) {
current_entry = 0;
cur_skipped_paths = 0;
queue_cur = queue;
queue_cycle++;
}
while (queue_cur != selected_seed) {
queue_cur = queue_cur->next;
current_entry++;
if (!queue_cur) {
current_entry = 0;
cur_skipped_paths = 0;
queue_cur = queue;
queue_cycle++;
}
}
}
skipped_fuzz = fuzz_one(use_argv);
if (!stop_soon && sync_id && !skipped_fuzz) {
if (!(sync_interval_cnt++ % SYNC_INTERVAL))
sync_fuzzers(use_argv);
}
if (!stop_soon && exit_1) stop_soon = 2;
if (stop_soon) break;
}
当queue entry* selected_seed没有选出来或者selected_seed中的regions数为0时,先通过choose_target_state()函数选出目标状态id,之后调用cull_queue()更新队列,之后更新被选中状态的选中次数。之后通过choose_seed()函数选出selected_seed。 之后调整queue_cur到selected_seed。之后调用fuzz_one()函数进行一次fuzz,再根据fuzz的结果判断是否结束fuzz。
之后是一些结束fuzz清理资源关闭文件的一些操作。
以上是对AFLNET的main函数执行步骤的分析,还需要对setup_ipsm()、perform_dry_run()、fuzz_one()进一步分析。