Persistent Mode

本文最后更新于:2023年3月14日 晚上

Forkserver Persistent Mode

Persistent Mode

AFL 中采用 Forserver 来加快程序运行效率,其原理是利用 Forkserver 机制 fork() 出子程序进行执行,取代了 execve() 的执行模式,避免了大量重复的链接等操作,大大提升了 Fuzz 的速率。

进一步看,由于某些库提供的 API 是无状态的,或能重置其状态,在一次次循环之间。当 API 重置后,一个长期活跃的进程就可以被重复使用,这样可以消除 fork() 函数以及 OS 相关的消耗。

通常,Persistent Mode 下,程序可有 x10 ~ x20 倍的速率提升。

使用 Persistent Mode

使用 Persistent Mode 很简单,只需要修改 target 程序为一个确定的模式即可

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
#include "what_you_need_for_your_target.h"
//引用有目标无状态 API 的库
__AFL_FUZZ_INIT();

main() {

// anything else here, e.g. command line arguments, initialization, etc.

#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif

unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
// and before __AFL_LOOP!

while (__AFL_LOOP(10000)) {

int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a
// call!

if (len < 8) continue; // check for a required/useful minimum input length

/* Setup function call, e.g. struct target *tmp = libtarget_init() */
/* Call function to be fuzzed, e.g.: */
target_function(buf, len);
/* Reset state. e.g. libtarget_free(tmp) */

}

return 0;

}

以下为两个例子
对 capstone 进行 fuzz

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
34
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include "capstone/include/capstone.h" // 引用capstone内无状态库
#include <inttypes.h>


int main(int argc, char** argv) {
csh handle;
cs_insn *insn;
size_t count;
uint8_t buf[128];
ssize_t read_bytes;

while (__AFL_LOOP(1000)) {
// (re-) initialize the library and read new input
read_bytes = -1;
memset(buf, 0, 128);
read_bytes = read(STDIN_FILENO, buf, 128);

if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) == CS_ERR_OK) {
// disassemble the bytes we just read using capstone
count = cs_disasm(handle, buf, read_bytes, 0x1000, 0, &insn);

// Don't leak memory. This is especially important in persistent mode,
// because we reuse the process a significant number of times
cs_free(insn, count);
}
cs_close(&handle);
}
return 0;
}

对某程序进行 fuzz

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <ft2build.h>
#include FT_FREETYPE_H

int main(int argc, char *argv[]){
FT_Library lib = NULL;
FT_Face face = NULL;
FT_Init_FreeType(&lib);

while(__AFL_LOOP(1000)) {
face = NULL;
FT_New_Face(lib, argv[1], 0, &face);
FT_Done_Face(face);
}

return 0;
}

**注意:重置状态时应尽力避免 memory leak **

__AFL_LOOP

_AFL_LOOP 的宏定义:

1
2
3
4
5
6
7
#define __AFL_LOOP(_A)  \
({ \
static volatile char *_B __attribute__((used)); \
_B = (char*)"##SIG_AFL_PERSISTENT##"; \
__attribute__((visibility("default"))) int _L(unsigned int) __asm__("__afl_persistent_loop"); \
_L(_A); \
})

宏定义 __AFL_LOOP内部调用__afl_persistent_loop函数

该函数源代码如下:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/* A simplified persistent mode handler, used as explained in README.llvm. */

int __afl_persistent_loop(unsigned int max_cnt) {

static u8 first_pass = 1;
static u32 cycle_cnt;

if (first_pass) {

/* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
On subsequent calls, the parent will take care of that, but on the first
iteration, it's our job to erase any trace of whatever happened
before the loop. */

if (is_persistent) {
// 重置共享内存
memset(__afl_area_ptr, 0, MAP_SIZE);
__afl_area_ptr[0] = 1;
__afl_prev_loc = 0;
}

cycle_cnt = max_cnt;
first_pass = 0;
return 1;

}

if (is_persistent) {

if (--cycle_cnt) {

raise(SIGSTOP);

__afl_area_ptr[0] = 1;
__afl_prev_loc = 0;

return 1;

} else {

/* When exiting __AFL_LOOP(), make sure that the subsequent code that
follows the loop is not traced. We do that by pivoting back to the
dummy output region. */

__afl_area_ptr = __afl_area_initial;

}

}

return 0;

}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!