AFL++ Frida-Mode Scripting

Scripting

FRIDA当前支持使用Javascript配置的能力。依靠FRIDA的脚本引擎(支持调试符号和导出表),这比之前使用环境变量配置FRIDA更加方便。

在默认情况下,FRIDA模式会在目标文件(要被fuzz的二进制文件)相同目录下寻找afl.js文件,作为FRIDA的配置文件。若为其他文件名,则可使用AFL_FRIDA_JS_SCRIPT环境变量指定。

在该脚本中,除所有标准的frida api函数外,还另外添加了一些功能函数用以与FRIDA mode自身交互。这些额外的函数可以通过全局变量Afl来访问, 例如 Afl.print("Hello world"); 取代 console.log("Hello World");

在使用中若需要调试,则使用环境变量AFL_DEBUG_CHILD=1.

Example

在有符号的二进制中,用户往往更喜欢用符号指定函数地址(例如入口函数或者persistent fuzz的地址)。

下面的例子使用了API
DebugSymbol.fromName().

Module.getExportByName().

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
54
55
56
57
58
59
60
61
62
/* Use Afl.print instead of console.log */
Afl.print('******************');
Afl.print('* AFL FRIDA MODE *');
Afl.print('******************');
Afl.print('');

/* Print some useful diagnostics stuff */
Afl.print(`PID: ${Process.id}`);

new ModuleMap().values().forEach(m => {
Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
});

/*
* Configure entry-point, persistence etc. This will be what most
* people want to do.
*/
const persistent_addr = DebugSymbol.fromName('main');
Afl.print(`persistent_addr: ${persistent_addr.address}`);

if (persistent_addr.address.equals(ptr(0))) {
Afl.error('Cannot find symbol main');
}

const persistent_ret = DebugSymbol.fromName('slow');
Afl.print(`persistent_ret: ${persistent_ret.address}`);

if (persistent_ret.address.equals(ptr(0))) {
Afl.error('Cannot find symbol slow');
}

Afl.setPersistentAddress(persistent_addr.address);
Afl.setPersistentReturn(persistent_ret.address);
Afl.setPersistentCount(1000000);

/* Control instrumentation, you may want to do this too */
Afl.setInstrumentLibraries();
const mod = Process.findModuleByName("libc-2.31.so")
Afl.addExcludedRange(mod.base, mod.size);

/* Some useful options to configure logging */
Afl.setStdOut("/tmp/stdout.txt");
Afl.setStdErr("/tmp/stderr.txt");

/* Show the address layout. Sometimes helpful */
Afl.setDebugMaps();

/*
* If you are using these options, then things aren't going
* very well for you.
*/
Afl.setInstrumentDebugFile("/tmp/instr.log");
Afl.setPrefetchDisable();
Afl.setInstrumentNoOptimize();
Afl.setInstrumentEnableTracing();
Afl.setInstrumentTracingUnique();
Afl.setStatsFile("/tmp/stats.txt");
Afl.setStatsInterval(1);

/* *ALWAYS* call this when you have finished all your configuration */
Afl.done();
Afl.print("done");

Stripped binaries

下面的例子是处理没符号或者导出表的二进制的情况:

1
2
3
4
const module = Process.getModuleByName('target.exe');
/* Hardcoded offset within the target image */
const address = module.base.add(0xdeadface);
Afl.setPersistentAddress(address);

Persistent hook

Persistent hook可以通过使用一个共享链接库的方式(***.so)的方式实现。示例源码(hook并fuzzLLVMFuzzerTestOneInput函数的例子)可以在Frida_mode/hook目录下找到。在该例子中,使用了如下的代码片段实现Persistent Hook。

1
2
3
4
5
const path = Afl.module.path;
const dir = path.substring(0, path.lastIndexOf("/"));
const mod = Module.load(`${dir}/frida_mode/build/hook.so`);
const hook = mod.getExportByName('afl_persistent_hook');
Afl.setPersistentHook(hook);

Persistent Hook也可以通过FRIDA本身对CModule的支持来实现,该功能依赖于TinyCC。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const cm = new CModule(`

#include <string.h>
#include <gum/gumdefs.h>

void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
uint32_t input_buf_len) {

memcpy((void *)regs->rdi, input_buf, input_buf_len);
regs->rsi = input_buf_len;

}
`,
{
memcpy: Module.getExportByName(null, 'memcpy')
});
Afl.setPersistentHook(cm.afl_persistent_hook);

Advanced persistence

以如下的代码作为(被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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void LLVMFuzzerTestOneInput(char *buf, int len) {

if (len < 1) return;
buf[len] = 0;

// we support three input cases
if (buf[0] == '0')
printf("Looks like a zero to me!\n");
else if (buf[0] == '1')
printf("Pretty sure that is a one!\n");
else
printf("Neither one or zero? How quaint!\n");

}

int run(char *file) {

int fd = -1;
off_t len;
char * buf = NULL;
size_t n_read;
int result = -1;

do {

dprintf(STDERR_FILENO, "Running: %s\n", file);

fd = open(file, O_RDONLY);
if (fd < 0) {

perror("open");
break;

}

len = lseek(fd, 0, SEEK_END);
if (len < 0) {

perror("lseek (SEEK_END)");
break;

}

if (lseek(fd, 0, SEEK_SET) != 0) {

perror("lseek (SEEK_SET)");
break;

}

buf = malloc(len);
if (buf == NULL) {

perror("malloc");
break;

}

n_read = read(fd, buf, len);
if (n_read != len) {

perror("read");
break;

}

dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);

LLVMFuzzerTestOneInput(buf, len);
dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);

result = 0;

} while (false);

if (buf != NULL) { free(buf); }

if (fd != -1) { close(fd); }

return result;

}

void slow() {

usleep(100000);

}

int main(int argc, char **argv) {

if (argc != 2) { return 1; }
slow();
return run(argv[1]);

}

使用CModule的实现方法,FRIDA模式支持对任何函数的替换。如下例:

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
const slow = DebugSymbol.fromName('slow').address;
Afl.print(`slow: ${slow}`);

const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);

const cm = new CModule(`

extern unsigned char * __afl_fuzz_ptr;
extern unsigned int * __afl_fuzz_len;
extern void LLVMFuzzerTestOneInput(char *buf, int len);

void slow(void) {

LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
}
`,
{
LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
__afl_fuzz_ptr: Afl.getAflFuzzPtr(),
__afl_fuzz_len: Afl.getAflFuzzLen()
});

Afl.setEntryPoint(cm.slow);
Afl.setPersistentAddress(cm.slow);
Afl.setInMemoryFuzzing();
Interceptor.replace(slow, cm.slow);
Afl.print("done");
Afl.done();

在这个例子里,我们将slow函数替换成我们自己的代码。该代码随后被设定为入口地址、及persistent循环的地址。

Replacing LLVMFuzzerTestOneInput

与其他函数类似,函数LLVMFuzzerTestOneInput同样可以被替换。另外,任何被替换的函数都可以调用它本身。在下面的例子中,我们将LLVMFuzzerTestOneInput函数替换成My_LLVMFuzzerTestOneInput函数,并忽视buflen两个参数;而改为使用__afl_fuzzer_ptr__afl_fuzz_len。这允许我们在不必hook其他函数的情况下使用in-memory fuzzing。需要注意的是:替换函数和被替换的原函数不能使用同一函数名字,否则在CModule中的C代码将会由于符号名冲突而不能编译。

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
const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);

const cm = new CModule(`

extern unsigned char * __afl_fuzz_ptr;
extern unsigned int * __afl_fuzz_len;
extern void LLVMFuzzerTestOneInput(char *buf, int len);

void My_LLVMFuzzerTestOneInput(char *buf, int len) {

LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);

}
`,
{
LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
__afl_fuzz_ptr: Afl.getAflFuzzPtr(),
__afl_fuzz_len: Afl.getAflFuzzLen()
});

Afl.setEntryPoint(cm.My_LLVMFuzzerTestOneInput);
Afl.setPersistentAddress(cm.My_LLVMFuzzerTestOneInput);
Afl.setInMemoryFuzzing();
Interceptor.replace(LLVMFuzzerTestOneInput, cm.My_LLVMFuzzerTestOneInput);

Hooking main

最后,需要注意的是,main函数的Hook是一个特殊情况,这是因为main函数已经被FRIDA引擎自身hook了(至少第一个基本块已经被Stalker编译了)。因此任何如以上例子使用Interceptor.replacemain函数的替换都无效。JS绑定为此提供了setJsMainHook如下例所示:

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
const main = DebugSymbol.fromName('main').address;
Afl.print(`main: ${main}`);

const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);

const cm = new CModule(`

extern unsigned char * __afl_fuzz_ptr;
extern unsigned int * __afl_fuzz_len;
extern void LLVMFuzzerTestOneInput(char *buf, int len);

int main(int argc, char **argv) {

LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);

}
`,
{
LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
__afl_fuzz_ptr: Afl.getAflFuzzPtr(),
__afl_fuzz_len: Afl.getAflFuzzLen()
});

Afl.setEntryPoint(cm.main);
Afl.setPersistentAddress(cm.main);
Afl.setInMemoryFuzzing();
Afl.setJsMainHook(cm.main);

Library Fuzzing

使用FRIDA的Module.loadAPI,可扩展上述例子main函数的能力,使之调用任意函数。从而,当我们需要Fuzz一个动态链接库(而非可执行程序)时,可使用一个代理可执行程序作为入口。

Patching

以如下测试代码为例:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
american fuzzy lop++ - a trivial program to test the build
--------------------------------------------------------
Originally written by Michal Zalewski
Copyright 2014 Google Inc. All rights reserved.
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://www.apache.org/licenses/LICENSE-2.0
*/

#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

const uint32_t crc32_tab[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,

...

0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

uint32_t
crc32(const void *buf, size_t size)
{
const uint8_t *p = buf;
uint32_t crc;
crc = ~0U;
while (size--)
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
return crc ^ ~0U;
}

/*
* Don't you hate those contrived examples which CRC their data. We can use
* FRIDA to patch this function out and always return success. Otherwise, we
* could change it to actually correct the checksum.
*/
int crc32_check (char * buf, int len) {
if (len < sizeof(uint32_t)) { return 0; }
uint32_t expected = *(uint32_t *)&buf[len - sizeof(uint32_t)];
uint32_t calculated = crc32(buf, len - sizeof(uint32_t));
return expected == calculated;
}

/*
* So you've found a really boring bug in an earlier campaign which results in
* a NULL dereference or something like that. That bug can get in the way,
* causing the persistent loop to exit whenever it is triggered, and can also
* cloud your output unnecessarily. Again, we can use FRIDA to patch it out.
*/
void some_boring_bug(char c) {
switch (c) {
case 'A'...'Z':
case 'a'...'z':
__builtin_trap();
break;
}
}

void LLVMFuzzerTestOneInput(char *buf, int len) {

if (!crc32_check(buf, len)) return;

some_boring_bug(buf[0]);

if (buf[0] == '0') {
printf("Looks like a zero to me!\n");
}
else if (buf[0] == '1') {
printf("Pretty sure that is a one!\n");
}
else if (buf[0] == '2') {
if (buf[1] == '3') {
if (buf[2] == '4') {
printf("Oh we, weren't expecting that!");
__builtin_trap();
}
}
}
else
printf("Neither one or zero? How quaint!\n");

}

int main(int argc, char **argv) {

int fd = -1;
off_t len;
char * buf = NULL;
size_t n_read;
int result = -1;

if (argc != 2) { return 1; }

printf("Running: %s\n", argv[1]);

fd = open(argv[1], O_RDONLY);
if (fd < 0) { return 1; }

len = lseek(fd, 0, SEEK_END);
if (len < 0) { return 1; }

if (lseek(fd, 0, SEEK_SET) != 0) { return 1; }

buf = malloc(len);
if (buf == NULL) { return 1; }

n_read = read(fd, buf, len);
if (n_read != len) { return 1; }

printf("Running: %s: (%zd bytes)\n", argv[1], n_read);

LLVMFuzzerTestOneInput(buf, len);
printf("Done: %s: (%zd bytes)\n", argv[1], n_read);

return 0;
}

在以上的测试代码中,有多个函数会成为Fuzz的障碍,在如下的例子中,我们示范如何使用FRIDA的功能修改掉这些障碍。

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
Afl.print('******************');
Afl.print('* AFL FRIDA MODE *');
Afl.print('******************');
Afl.print('');

const main = DebugSymbol.fromName('main').address;
Afl.print(`main: ${main}`);
Afl.setEntryPoint(main);
Afl.setPersistentAddress(main);
Afl.setPersistentCount(10000000);

const crc32_check = DebugSymbol.fromName('crc32_check').address;
const crc32_replacement = new NativeCallback(
(buf, len) => {
Afl.print(`len: ${len}`);
if (len < 4) {
return 0;
}

return 1;
},
'int',
['pointer', 'int']);
Interceptor.replace(crc32_check, crc32_replacement);

const some_boring_bug = DebugSymbol.fromName('some_boring_bug').address
const boring_replacement = new NativeCallback(
(c) => { },
'void',
['char']);
Interceptor.replace(some_boring_bug, boring_replacement);

Afl.done();
Afl.print("done");

Advanced patching

Consider the following code fragment…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extern void some_boring_bug2(char c);

__asm__ (
".text \n"
"some_boring_bug2: \n"
".global some_boring_bug2 \n"
".type some_boring_bug2, @function \n"
"mov %edi, %eax \n"
"cmp $0xb4, %al \n"
"jne ok \n"
"ud2 \n"
"ok: \n"
"ret \n");

void LLVMFuzzerTestOneInput(char *buf, int len) {

...

some_boring_bug2(buf[0]);

...

}

FRIDA除Interceptor.replaceInterceptor.attachAPI之外,还允许使用StalkerAPI对目标程序进行更细粒度的更改。

如下的例子将目标函数中的UD2指令修改为nop指令,从而避免崩溃(UD2指令为崩溃指令)。

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* Modify the instructions */
const some_boring_bug2 = DebugSymbol.fromName('some_boring_bug2').address
const pid = Memory.alloc(4);
pid.writeInt(Process.id);

const cm = new CModule(`
#include <stdio.h>
#include <gum/gumstalker.h>

typedef int pid_t;

#define STDERR_FILENO 2
#define BORING2_LEN 10

extern int dprintf(int fd, const char *format, ...);
extern void some_boring_bug2(char c);
extern pid_t getpid(void);
extern pid_t pid;

gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
gboolean excluded, GumStalkerOutput *output)
{
pid_t my_pid = getpid();
GumX86Writer *cw = output->writer.x86;

if (GUM_ADDRESS(insn->address) < GUM_ADDRESS(some_boring_bug2)) {

return TRUE;

}

if (GUM_ADDRESS(insn->address) >=
GUM_ADDRESS(some_boring_bug2) + BORING2_LEN) {

return TRUE;

}

if (my_pid == pid) {

if (begin) {

dprintf(STDERR_FILENO, "\n> 0x%016lX: %s %s\n", insn->address,
insn->mnemonic, insn->op_str);

} else {

dprintf(STDERR_FILENO, " 0x%016lX: %s %s\n", insn->address,
insn->mnemonic, insn->op_str);

}

}

if (insn->id == X86_INS_UD2) {

gum_x86_writer_put_nop(cw);
return FALSE;

} else {

return TRUE;

}
}
`,
{
dprintf: Module.getExportByName(null, 'dprintf'),
getpid: Module.getExportByName(null, 'getpid'),
some_boring_bug2: some_boring_bug2,
pid: pid
});
Afl.setStalkerCallback(cm.js_stalker_callback)
Afl.setStdErr("/tmp/stderr.txt");

注意你可能更喜欢用如下的代码找到patch地址。

1
2
3
const module = Process.getModuleByName('target.exe');
/* Hardcoded offset within the target image */
const address = module.base.add(0xdeadface);

OR

1
const address = DebugSymbol.fromName("my_function").address.add(0xdeadface);

OR

1
const address = Module.getExportByName(null, "my_function").add(0xdeadface);

若原函数的原始指令没有更更改,则函数js_stalker_callback应该返回TRUE,否则返回FALSE。在上述的例子中,我们可以看到原始指令被替换成NOP指令。

最后注意:应保持forkserver父子进程的代码相同或forkserver每次产生的子进程代码相同,否则会产生难以调试的bug【原文:
note that the same callback will be called when compiling instrumented
code both in the child of the forkserver (as it is executed) and also in the
parent of the forkserver (when prefetching is enabled) so that it can be
inherited by the next forked child. It is VERY important that the same
instructions be generated in both the parent and the child or if prefetching is
disabled that the same instructions are generated every time the block is
compiled. Failure to do so will likely lead to bugs which are incredibly
difficult to diagnose. The code above only prints the instructions when running
in the parent process (the one provided by Process.id when the JS script is
executed).】

OSX

注意OSX上JavaScript的调试符号api使用CoreSymbolicationAPI,因此目标进程需要加载和使用CoreFoundation模块,如下设定:

1
AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation

应该被注意的是CoreSymbolicationAPI在初始化和创建cache的时候较慢,因此,需要增加afl-fuzz-t参数的值,避免超时。

API

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
class Afl {
/**
* This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`,
* it takes as arguments a `NativePointer` and a `number`. It can be
* called multiple times to exclude several ranges.
*/
static addExcludedRange(addressess, size) {
Afl.jsApiAddExcludeRange(addressess, size);
}
/**
* This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`,
* it takes as arguments a `NativePointer` and a `number`. It can be
* called multiple times to include several ranges.
*/
static addIncludedRange(addressess, size) {
Afl.jsApiAddIncludeRange(addressess, size);
}
/**
* This must always be called at the end of your script. This lets
* FRIDA mode know that your configuration is finished and that
* execution has reached the end of your script. Failure to call
* this will result in a fatal error.
*/
static done() {
Afl.jsApiDone();
}
/**
* This function can be called within your script to cause FRIDA
* mode to trigger a fatal error. This is useful if for example you
* discover a problem you weren't expecting and want everything to
* stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
* this error message.
*/
static error(msg) {
const buf = Memory.allocUtf8String(msg);
Afl.jsApiError(buf);
}
/**
* Function used to provide access to `__afl_fuzz_ptr`, which contains the length of
* fuzzing data when using in-memory test case fuzzing.
*/
static getAflFuzzLen() {
return Afl.jsApiGetSymbol("__afl_fuzz_len");
}
/**
* Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing
* data when using in-memory test case fuzzing.
*/
static getAflFuzzPtr() {
return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
}
/**
* Print a message to the STDOUT. This should be preferred to
* FRIDA's `console.log` since FRIDA will queue it's log messages.
* If `console.log` is used in a callback in particular, then there
* may no longer be a thread running to service this queue.
*/
static print(msg) {
const STDOUT_FILENO = 2;
const log = `${msg}\n`;
const buf = Memory.allocUtf8String(log);
Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
}
/**
* See `AFL_FRIDA_STALKER_NO_BACKPATCH`.
*/
static setBackpatchDisable() {
Afl.jsApiSetBackpatchDisable();
}
/**
* See `AFL_FRIDA_DEBUG_MAPS`.
*/
static setDebugMaps() {
Afl.jsApiSetDebugMaps();
}
/**
* This has the same effect as setting `AFL_ENTRYPOINT`, but has the
* convenience of allowing you to use FRIDAs APIs to determine the
* address you would like to configure, rather than having to grep
* the output of `readelf` or something similarly ugly. This
* function should be called with a `NativePointer` as its
* argument.
*/
static setEntryPoint(address) {
Afl.jsApiSetEntryPoint(address);
}
/**
* Function used to enable in-memory test cases for fuzzing.
*/
static setInMemoryFuzzing() {
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
}
/**
* See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
* as an argument.
*/
static setInstrumentCoverageFile(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetInstrumentCoverageFile(buf);
}
/**
* See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
* an argument.
*/
static setInstrumentDebugFile(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetInstrumentDebugFile(buf);
}
/**
* See `AFL_FRIDA_INST_TRACE`.
*/
static setInstrumentEnableTracing() {
Afl.jsApiSetInstrumentTrace();
}
/**
* See `AFL_FRIDA_INST_JIT`.
*/
static setInstrumentJit() {
Afl.jsApiSetInstrumentJit();
}
/**
* See `AFL_INST_LIBS`.
*/
static setInstrumentLibraries() {
Afl.jsApiSetInstrumentLibraries();
}
/**
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/
static setInstrumentNoOptimize() {
Afl.jsApiSetInstrumentNoOptimize();
}
/*
* See `AFL_FRIDA_INST_SEED`
*/
static setInstrumentSeed(seed) {
Afl.jsApiSetInstrumentSeed(seed);
}
/**
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
*/
static setInstrumentTracingUnique() {
Afl.jsApiSetInstrumentTraceUnique();
}
/**
* See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single
* `string` as an argument.
*/
static setInstrumentUnstableCoverageFile(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
}
/*
* Set a callback to be called in place of the usual `main` function. This see
* `Scripting.md` for details.
*/
static setJsMainHook(address) {
Afl.jsApiSetJsMainHook(address);
}
/**
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
* `NativePointer` should be provided as it's argument.
*/
static setPersistentAddress(address) {
Afl.jsApiSetPersistentAddress(address);
}
/**
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
* `number` should be provided as it's argument.
*/
static setPersistentCount(count) {
Afl.jsApiSetPersistentCount(count);
}
/**
* See `AFL_FRIDA_PERSISTENT_DEBUG`.
*/
static setPersistentDebug() {
Afl.jsApiSetPersistentDebug();
}
/**
* See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
* argument. See above for examples of use.
*/
static setPersistentHook(address) {
Afl.jsApiSetPersistentHook(address);
}
/**
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
* `NativePointer` should be provided as it's argument.
*/
static setPersistentReturn(address) {
Afl.jsApiSetPersistentReturn(address);
}
/**
* See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
*/
static setPrefetchBackpatchDisable() {
Afl.jsApiSetPrefetchBackpatchDisable();
}
/**
* See `AFL_FRIDA_INST_NO_PREFETCH`.
*/
static setPrefetchDisable() {
Afl.jsApiSetPrefetchDisable();
}
/**
* See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as
* an argument.
*/
static setSeccompFile(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetSeccompFile(buf);
}
/**
* See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`.
*/
static setStalkerAdjacentBlocks(val) {
Afl.jsApiSetStalkerAdjacentBlocks(val);
}
/*
* Set a function to be called for each instruction which is instrumented
* by AFL FRIDA mode.
*/
static setStalkerCallback(callback) {
Afl.jsApiSetStalkerCallback(callback);
}
/**
* See `AFL_FRIDA_STALKER_IC_ENTRIES`.
*/
static setStalkerIcEntries(val) {
Afl.jsApiSetStalkerIcEntries(val);
}
/**
* See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
* an argument.
*/
static setStatsFile(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetStatsFile(buf);
}
/**
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
* argument
*/
static setStatsInterval(interval) {
Afl.jsApiSetStatsInterval(interval);
}
/**
* See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
* an argument.
*/
static setStdErr(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetStdErr(buf);
}
/**
* See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
* an argument.
*/
static setStdOut(file) {
const buf = Memory.allocUtf8String(file);
Afl.jsApiSetStdOut(buf);
}
/**
* See `AFL_FRIDA_TRACEABLE`.
*/
static setTraceable() {
Afl.jsApiSetTraceable();
}
static jsApiGetFunction(name, retType, argTypes) {
const addr = Afl.module.getExportByName(name);
return new NativeFunction(addr, retType, argTypes);
}
static jsApiGetSymbol(name) {
return Afl.module.getExportByName(name);
}
}