winafl-net
条评论winAFL 的网络测试模式
在winAFL的目录下,有个 custom_net_fuzzer.dll 和 custom_winafl_server.dll
根据winafl的说明, custom_net_fuzzer.dll 是用来辅助网络连接测试的。
具体原理:
当 winAFL 启动时,设置 -l 参数,附加 custom_net_fuzzer.dll 。
custom_net_fuzzer.dll 在运行时,要获取几个环境变量
1
2
3
4
5
6-a IP address - ip 地址,数据往这个地址发
-U - 使用udp协议,默认是 tcp
-p port - 端口,数据往这个端口发
-w msec - 实际开始fuzz之前的毫秒延迟
实际命令如下:
set AFL_CUSTOM_DLL_ARGS=-U -p 7714 -a 127.0.0.1 -w 1000 && afl-fuzz.exe -l custom_net_fuzzer.dll -i in -o out -D E:\Fuzz\DynamoRIO-Windows-8.0.18971\bin32 -t 20000 -- -target_module test_netmode.exe -target_method recv_func -coverage_module test_netmode.exe -fuzz_iterations 5200 -nargs 1 -- test_netmode.exe然后,afl-fuzz 负责变异数据,然后将数据放到一个 buf 中,源码如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// afl-fuzz.c
/* This function is used to call user-defined server routine to send data back into sample */
static int process_test_case_into_dll(int fuzz_iterations)
{
int result;
long fsize;
char *buf = get_test_case(&fsize);
ACTF("buf= %s\n",buf);
result = dll_run_ptr(buf, fsize, fuzz_iterations); /* caller should copy the buffer */
free(buf);
if (result == 0)
FATAL("Unable to process test case, the user-defined DLL returned 0");
return 1;
}再将 buf 传递给 custom_net_fuzzer.dll 中的 dll_run 函数,如下。
1
2
3
4
5
6
7
8
9// custom_net_fuzzer.c
CUSTOM_SERVER_API int APIENTRY dll_run(char *data, long size, int fuzz_iterations) {
if (is_TCP)
send_data_tcp(data, size, fuzz_iterations);
else
send_data_udp(data, size, fuzz_iterations);
return 1;
}
// fuzz_iterations 模糊迭代次数dll_run 函数负责最后一步的数据发送,根据是 udp 还是 tcp ,建立 socket 发送数据到目标地址的目标端口上。
其中 tcp 发送数据之后,需要关闭 socket;udp 只需要建立socket ,sendto 即可。
覆盖率
在 afl-fuzz.c 中有个关键函数:create_target_process ,这个函数的调用在 run_target 中。
1 | static u8 run_target(char** argv, u32 timeout) { |
create_target_process 函数用来创建进程并且使用 dynamorio 监控。create_target_process 函数比较复杂,具体功能有:
创建命名管道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15pipe_name = (char *)alloc_printf("\\\\.\\pipe\\afl_pipe_%s", fuzzer_id);
pipe_handle = CreateNamedPipe(
pipe_name, // pipe name
PIPE_ACCESS_DUPLEX | // read/write access
FILE_FLAG_OVERLAPPED, // overlapped mode
0,
1, // max. instances
512, // output buffer size
512, // input buffer size
20000, // client time-out
&sa);
if (pipe_handle == INVALID_HANDLE_VALUE) {
FATAL("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
}fuzzer_id 是一个随机值。管道的作用,是用来与 drrun 通信。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19else {
if (drattach) {
drattachpid = find_attach_pid(drattach_identifier);
cmd = alloc_printf(
"%s\\drrun.exe -attach %ld -no_follow_children %s %s -fuzzer_id %s",
dynamorio_dir, drattachpid, client_invocation, client_params, fuzzer_id);
} else {
pidfile = alloc_printf("childpid_%s.txt", fuzzer_id); // fuzzer_id 随机值,把 pid 写入到这个 txt 文件
if (persist_dr_cache) {
cmd = alloc_printf(
"%s\\drrun.exe -pidfile %s -no_follow_children -persist -persist_dir \"%s\\drcache\" %s %s -fuzzer_id %s -drpersist -- %s",
dynamorio_dir, pidfile, out_dir, client_invocation, client_params, fuzzer_id, target_cmd);
} else {
cmd = alloc_printf(
"%s\\drrun.exe -pidfile %s -no_follow_children %s %s -fuzzer_id %s -- %s",
dynamorio_dir, pidfile, client_invocation, client_params, fuzzer_id, target_cmd);
}
}
}pidfile 存放 PID 值,然后等待管道连接,并通过读取上述txt文件以获取目标进程id,主要用来后面超时中断进程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33// 根据cmd,来创建进程
if(!CreateProcess(NULL, cmd, NULL, NULL, inherit_handles, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
FATAL("CreateProcess failed, GLE=%d.\n", GetLastError());
}
child_handle = pi.hProcess;
child_thread_handle = pi.hThread;
......
// 减少线程的挂起计数。当挂起计数减为零时,恢复线程的执行。
// child_thread_handle 是要重新启动的线程的句柄。
ResumeThread(child_thread_handle);
watchdog_timeout_time = get_cur_time() + exec_tmout;
watchdog_enabled = 1;
// OverlappedConnectNamedPipe 负责初始化,并连接前面创建的管道
if(!OverlappedConnectNamedPipe(pipe_handle, &pipe_overlapped)) {
FATAL("ConnectNamedPipe failed, GLE=%d.\n", GetLastError());
}
watchdog_enabled = 0;
else if (drioless == 0) {
// 打开 pidfile 文件,读取内容
fp = fopen(pidfile, "rb");
if(!fp) {
FATAL("Error opening pidfile.txt");
}
.......
ck_free(pidfile);
}
else {
child_pid = pi.dwProcessId;
}
之后的操作交给winafl.dll,在 winafl.c 中会先打开前面创建的命名管道。
1 | static void |
然后,访问共享内存。
1 | static void |
winafl_data.afl_area 就是用来存覆盖率信息的。
循环调用的关键函数 pre_fuzz_handler ,
pre_fuzz_handler
函数,通过管道写入 P 命令,代表开始进入目标函数,afl-fuzz.exe 进程收到命令后,会向目标进程写入管道命令 F,并监测超时时间和循环调用次数。
1 | static void |
当执行完了被 fuzz 的函数,就会调用 post_fuzz_handler 来恢复调用 fuzz 函数之前的状态。以此来实现循环调用。
1 | static void |
instrument_bb_coverage , instrument_edge_coverage 函数,用来记录覆盖率的情况。
1 | afl_map = winafl_data.afl_area; |