afl-fuzz学习笔记

AFL阅读文档

AFL在github上下载链接:https://github.com/mirrorer/afl (然后编译安装make+make install)
AFL工具大致流程
1)将用户提供的初始测试案例加载到队列中
2)从队列中取出下一个输入文件
3)尝试最小程度的修改测试案例而不改变程序标准的行为
4)使用平衡、研究充分的传统模糊测试策略反复改变文件
5)如果生成的变化导致程序的状态改变被仪器记录,将改变后的输出作为队列的新输入
6)跳到2

简单的使用

当有源代码时,可以用自带的工具来标准的编译第三方代码,替代gcc或者clang

1
2
3
4
5
6
$ CC=/path/to/afl/afl-gcc ./configure
$ make clean all
//对于c++项目
$ CXX=/path/to/afl/afl-g++
//当使用clang时类似
afl-clang和afl-clang++

静态构建(绑定.so文件等,可以通过设置LD_LIBRARTY_PATH)

1
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared

make时,设置AFL_HERDEN=1检测内存错误能力更强
Libdislocator可以帮助发现堆相关问题
检测可执行程序可以使用QEMU形式

1
2
3
4
$cd qemu_mode
./build_qemu_support.sh//比编译时,检测慢2-5倍,可能还有其他问题
//当安装时,如果缺少库使用sudo apt-get install libtool以及sudo apt-get install libtool-bin
//还有sudo apt-get install automake和sudo apt-get install bison补全库

testcases/子目录里面有很多例子,当数据很多时,可以使用AFL-cmin,用来识别功能
不同的文件
模糊测试文件由afl-fuzz程序执行
语法

1
2
3
4
5
6
$ ./afl-fuzz -i 测试案例集 -o 结果 文件路径 [...参数...]
//从文件中获取输入使用"@@"标记
//-f可以将特别的数据写入特定的文件(当程序需要特定的文件扩展名时,很有用)
//-Q参数在qemu模式下测试,传统的盲模糊测试使用-n
//-t和-m跳过超时以及内存限制(编译器或者视频解码器等)
//需要快速的结果可以加上-d

fuzz输出文件解释

fuzz完成后会在fuzz_out文件夹中生成三个文件夹,分别为queue、crashes、hangs
queue文件夹中存放的文件为测试案例,可以使用afl-cmin将其缩小
crashes文件夹中存放了可以导致程序崩溃的样例(当测试程序收到某些致命错误时,如SIGSEGV、SIGILL、SIGABRT)
hangs中存放了导致测试程序超时的案例(1s和-t参数中的较大值),可以通过修改AFL_HANG_TMOUT调整
当无法重现afl-fuzz发生的崩溃时,可能的原因是没有设置与该工具使用的相同的内存限制。
解决方法

1
2
3
4
5
6
7
8
$ LIMIT_MB=50
$ ( ulimit -Sv $[LIMIT_MB << 10]; /path/to/tested_binary ... )
//更改 LIMIT_MB 以匹配传递给 afl-fuzz 的 -m 参数。
//在 OpenBSD 上,还将 -Sv 更改为 -Sd。
//使用现有的目录恢复过程
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
//使用xxd命令查看
$ xxd id:000001,sig:11,src:000000,op:havoc,rep:128

fuzz界面解释

① Process timing:Fuzzer运行时长、以及距离最近发现的路径、崩溃和挂起经过了多长时间。
② Overall results:Fuzzer当前状态的概述。
③ Cycle progress:我们输入队列的距离。
④ Map coverage:目标二进制文件中的插桩代码所观察到覆盖范围的细节。
⑤ Stage progress:Fuzzer现在正在执行的文件变异策略、执行次数和执行速度。
⑥ Findings in depth:有关我们找到的执行路径,异常和挂起数量的信息。
⑦ Fuzzing strategy yields:关于突变策略产生的最新行为和结果的详细信息。
⑧ Path geometry:有关Fuzzer找到的执行路径的信息。
⑨ CPU load:CPU利用率

afl及相关工具安装

afl-fuzz

afl-fuzzing项目地址:

1
https://github.com/mirrorer/afl

安装及配置

1
2
3
4
5
6
apt-get update
apt-get install make git curl cargo vim wget
git clone https://github.com/mirrorer/afl
cd $HOME/afl
make
sudo make install

afl-fuzz++安装

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo apt-get update

sudo apt-get install -y build-essential python3-dev automake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools

sudo apt-get install -y lld-11 llvm-11 llvm-11-dev clang-11 || sudo apt-get install -y lld llvm llvm-dev clang

sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/.* //'|sed 's/\..*//')-dev
cd $HOME
git clone https://github.com/AFLplusplus/AFLplusplus && cd AFLplusplus
export LLVM_CONFIG="llvm-config-11"

make -j$(nproc) source-only # 'make distrib' for both binary-only and source code fuzzing
sudo make install

crash exploration mode

afl-fuzz自带的运行模式,用于分析crashes利用的可行性,可以根据生成的crashes文件,产生不同的crashes

1
afl-fuzz -m none -C -i poc -o peruvian-were-rabbit_out -- ~/src/LuPng/a.out @@ out.png

afl-cmin和afl-tmin

afl-cmin作用是移除执行相同代码的输入文件,在测试的时候有可能会用到多个测试案例,此时就可以使用afl-cmin精简输入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Required parameters:
-i dir - input directory with starting corpus
-o dir - output directory for minimized files

Execution control settings:
-f file - location read by the fuzzed program (stdin)
-m megs - memory limit for child process ( MB)
-t msec - run time limit for child process (none)
-O - use binary-only instrumentation (FRIDA mode)
-Q - use binary-only instrumentation (QEMU mode)
-U - use unicorn-based instrumentation (unicorn mode)

Minimization settings:
-C - keep crashing inputs, reject everything else
-e - solve for edge coverage only, ignore hit counts

afl-tmin作用是减小单个文件的大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
equired parameters:
-i file - input test case to be shrunk by the tool
-o file - final output location for the minimized data

Execution control settings:
-f file - input file read by the tested program (stdin)
-t msec - timeout for each run (1000 ms)
-m megs - memory limit for child process (0 MB)
-O - use binary-only instrumentation (FRIDA mode)
-Q - use binary-only instrumentation (QEMU mode)
-U - use unicorn-based instrumentation (Unicorn mode)
-W - use qemu-based instrumentation with Wine (Wine mode)
(Not necessary, here for consistency with other afl-* tools)

Minimization settings:
-e - solve for edge coverage only, ignore hit counts
-x - treat non-zero exit codes as crashes

-H - minimize a hang (hang mode)

fuzz测试案例

fuzz需要有测试案例,通过案例的变异,可以使程序crash,然后进行相关操作,一般使用项目自带的案例,或者是afl中的案例,除此
之外还有开源的语料库可供使用,如下
afl generated image test sets
fuzzer-test-suite
samples-libav
fuzzdata
moonlight

afl-gcc和afl-g++构建项目

在fuzz过程中,一般需要构建项目两次,第一次是使用afl自带的afl-gcc或者afl-g++以及afl-clang等,来源码中插桩,并测试
程序的crashes,第二次为出现crashes时,使用gdb进行调试
编译器等设置如下

1
2
3
4
5
./configure CC="afl-gcc" CXX="afl-g++" 
./configure CC=$HOME/AFLplusplus/afl-clang-fast CXX=$HOME/AFLplusplus/afl-clang-fast++
#上面的模式速度比较慢,还有LLVM模式
export LLVM_CONFIG="llvm-config-11"
./configure CC="afl-clang-fast" CXX="afl-clang-fast++"

下载项目后,此时得到的只是源代码、及相关文件部分,还需要编译等操作(对应项目目录)
流程

1
2
3
4
5
git clone https://github.com/program-examples
cd $HOME/program-examples
./configure CC="afl-gcc" CXX="afl-g++" --prefix="$HOME/fuzzing_test/install/"#--prefix是目标路径
make
make install

当需要重新编译时,需要删除原来的文件,操作

1
2
3
rm -r $HOME/fuzzing_test/install
cd %HOME/program-examples
make clean

afl-showmap

使用afl-showmap可以在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
afl-showmap [ options ] -- /path/to/target_app [ ... ]

Required parameters:
-o file - file to write the trace data to

Execution control settings:
-t msec - timeout for each run (none)
-m megs - memory limit for child process (0 MB)
-O - use binary-only instrumentation (FRIDA mode)
-Q - use binary-only instrumentation (QEMU mode)
-U - use Unicorn-based instrumentation (Unicorn mode)
-W - use qemu-based instrumentation with Wine (Wine mode)
(Not necessary, here for consistency with other afl-* tools)

Other settings:
-i dir - process all files below this directory, must be combined with -o.
With -C, -o is a file, without -C it must be a directory
and each bitmap will be written there individually.
-C - collect coverage, writes all edges to -o and gives a summary
Must be combined with -i.
-q - sink program's output and don't show messages
-e - show edge coverage only, ignore hit counts
-r - show real tuple values instead of AFL filter values
-s - do not classify the map
-c - allow core dumps

This tool displays raw tuple data captured by AFL instrumentation.
For additional help, consult /usr/local/share/doc/afl/README.md.

Environment variables used:
LD_BIND_LAZY: do not set LD_BIND_NOW env var for target
AFL_CMIN_CRASHES_ONLY: (cmin_mode) only write tuples for crashing inputs
AFL_CMIN_ALLOW_ANY: (cmin_mode) write tuples for crashing inputs also
AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash
AFL_DEBUG: enable extra developer output
AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)
AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)
AFL_MAP_SIZE: the shared memory size for that target. must be >= the size the target was compiled for
AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target
AFL_PRINT_FILENAMES: If set, the filename currently processed will be printed to stdout
AFL_QUIET: do not print extra informational output
AFL_NO_FORKSRV: run target via execve instead of using the forkserver

使用例子

1
afl-showmap -m none -o /dev/null -- $HOME/fuzzing_xpdf/install/bin/pdftotext $HOME/fuzzing_xpdf/out/default/crashes/test.pdf

结果
not image

crasheswalk

分析crashes原因的工具
安装方式

1
2
3
4
5
6
7
8
$ apt-get install gdb golang
$ mkdir tools
$ cd tools
$ git clone https://github.com/jfoote/exploitable.git
$ mkdir go
$ export GOPATH=~/tools/go
$ export CW_EXPLOITABLE=~/tools/exploitable/exploitable/exploitable.py
$ go get -u github.com/bnagy/crashwalk/cmd/...

当go get这一步装不上时见blog.csdn.net

screen

使用screen命令可以暂停或者恢复fuzzing工作
用法如下

1
2
3
screen afl-fuzz ......
screen -ls
screen -r + port

具体的用法参考:https://www.runoob.com/linux/linux-comm-screen.html

相关参数

如下

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
afl-fuzz [ options ] -- /path/to/fuzzed_app [ ... ]

需要的参数:
-i dir - 测试案例的目录
-o dir - fuzzer发现的输出目录

执行参数设置:
-p schedule - 功率计划计算种子的性能分数:fast(default), explore, exploit, seek, rare, mmopt, coe, lin
-f file - 模糊程序读取的位置(默认值:stdin 或@@)
-t msec - 每次运行的超时时间(自动缩放,默认 1000 毫秒)。 添加一个“+”自动计算超时,该值为最大值。

-m megs - 子进程的内存限制(0 MB,0 = 无限制 [默认])
-O - 使用仅二进制检测(FRIDA 模式)
-Q - 使用仅二进制检测(QEMU 模式)
-U - 使用基于Unicorn的仪器(Unicorn模式)
-W - 在 Wine 中使用基于 qemu 的仪器(Wine 模式)

Mutator settings:
-D - 启用确定性模糊测试(每个队列条目一次)
-L minutes - 使用 MOpt(imize) 模式并设置进入的时间限制
起搏器模式(无新路径的分钟数)。 0 = 立即,
-1 = 立即并与正常突变一起。
-c program - enable CmpLog by specifying a binary compiled for it.
if using QEMU, just use -c 0.
-l cmplog_opts - CmpLog configuration values (e.g. "2AT"):
1=small files, 2=larger files (default), 3=all files,
A=arithmetic solving, T=transformational solving.

Fuzzing behavior settings:
-Z - sequential queue selection instead of weighted random
-N - do not unlink the fuzzing input file (for devices etc.)
-n - fuzz without instrumentation (non-instrumented mode)
-x dict_file - fuzzer dictionary (see README.md, specify up to 4 times)

Test settings:
-s seed - use a fixed seed for the RNG
-V seconds - fuzz for a specified time then terminate
-E execs - fuzz for an approx. no. of total executions then terminate
Note: not precise and can have several more executions.

Other stuff:
-M/-S id - distributed mode (see docs/parallel_fuzzing.md)
-M auto-sets -D, -Z (use -d to disable -D) and no trimming
-F path - sync to a foreign fuzzer queue directory (requires -M, can
be specified up to 32 times)
-T text - text banner to show on the screen
-I command - execute this command/script when a new crash is found
-C - crash exploration mode (the peruvian rabbit thing)
-b cpu_id - bind the fuzzing process to the specified CPU core (0-...)
-e ext - file extension for the fuzz test input file (if needed)

To view also the supported environment variables of afl-fuzz please use "-hh".

Compiled with Python 3.6.9 module support, see docs/custom_mutator.md
Compiled without AFL_PERSISTENT_RECORD support.
Compiled with shmat support.
For additional help please consult /usr/local/share/doc/afl/README.md :)

afl-fuzz相关原理

afl主要的用途是对与c/c++程序进行模糊测试,可以对存在源码和不存在源码的程序(qemu_mode)进行分析,白盒测试效率远远高于黑盒。
afl测试程序流程,如图
avatar
1.从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage);
2.选择一些输入文件,作为初始测试集加入输入队列(queue);
3.将队列中的文件按一定的策略进行”突变”;
4.如果经过变异文件更新了覆盖范围,则将其保留添加到队列中;
5.上述过程会一直循环进行,期间触发了crash的文件会被记录下来。

插桩的原理

在编译过程中,使用afl-gcc等afl工具编译项目,会对源码(.s)进行插桩,插桩的规则位,在main函数入口,函数入口,条件跳转等处插桩,直接插入一段汇编代码
这一段汇编代码如下
trampoline_fmt_64如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static const u8* trampoline_fmt_64 =

"\n"
"/* --- AFL TRAMPOLINE (64-BIT) --- */\n"
"\n"
".align 4\n"
"\n"
"leaq -(128+24)(%%rsp), %%rsp\n"//开辟152bytes空间
"movq %%rdx, 0(%%rsp)\n"
"movq %%rcx, 8(%%rsp)\n"
"movq %%rax, 16(%%rsp)\n"//保存变量
"movq $0x%08x, %%rcx\n"
"call __afl_maybe_log\n"//调用afl_maybe_log
"movq 16(%%rsp), %%rax\n"
"movq 8(%%rsp), %%rcx\n"
"movq 0(%%rsp), %%rdx\n"
"leaq (128+24)(%%rsp), %%rsp\n"
"\n"
"/* --- END --- */\n"
"\n";

在main函数入口处插入的汇编代码段为

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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
static const u8* main_payload_64 = 

"\n"
"/* --- AFL MAIN PAYLOAD (64-BIT) --- */\n"
"\n"
".text\n"
".att_syntax\n"
".code64\n"
".align 8\n"
"\n"
"__afl_maybe_log:\n"
"\n"
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
" .byte 0x9f /* lahf */\n"
#else
" lahf\n"
#endif /* ^__OpenBSD__, etc */
" seto %al\n"
"\n"
" /* Check if SHM region is already mapped. */\n"
"\n"
" movq __afl_area_ptr(%rip), %rdx\n"
" testq %rdx, %rdx\n"
" je __afl_setup\n"
"\n"
"__afl_store:\n"
"\n"
" /* Calculate and store hit for the code location specified in rcx. */\n"
"\n"
#ifndef COVERAGE_ONLY
" xorq __afl_prev_loc(%rip), %rcx\n"
" xorq %rcx, __afl_prev_loc(%rip)\n"
" shrq $1, __afl_prev_loc(%rip)\n"
#endif /* ^!COVERAGE_ONLY */
"\n"
#ifdef SKIP_COUNTS
" orb $1, (%rdx, %rcx, 1)\n"
#else
" incb (%rdx, %rcx, 1)\n"
#endif /* ^SKIP_COUNTS */
"\n"
"__afl_return:\n"
"\n"
" addb $127, %al\n"
#if defined(__OpenBSD__) || (defined(__FreeBSD__) && (__FreeBSD__ < 9))
" .byte 0x9e /* sahf */\n"
#else
" sahf\n"
#endif /* ^__OpenBSD__, etc */
" ret\n"
"\n"
".align 8\n"
"\n"
"__afl_setup:\n"
"\n"
" /* Do not retry setup if we had previous failures. */\n"
"\n"
" cmpb $0, __afl_setup_failure(%rip)\n"
" jne __afl_return\n"
"\n"
" /* Check out if we have a global pointer on file. */\n"
"\n"
#ifndef __APPLE__
" movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n"
" movq (%rdx), %rdx\n"
#else
" movq __afl_global_area_ptr(%rip), %rdx\n"
#endif /* !^__APPLE__ */
" testq %rdx, %rdx\n"
" je __afl_setup_first\n"
"\n"
" movq %rdx, __afl_area_ptr(%rip)\n"
" jmp __afl_store\n"
"\n"
"__afl_setup_first:\n"
"\n"
" /* Save everything that is not yet saved and that may be touched by\n"
" getenv() and several other libcalls we'll be relying on. */\n"
"\n"
" leaq -352(%rsp), %rsp\n"
"\n"
" movq %rax, 0(%rsp)\n"
" movq %rcx, 8(%rsp)\n"
" movq %rdi, 16(%rsp)\n"
" movq %rsi, 32(%rsp)\n"
" movq %r8, 40(%rsp)\n"
" movq %r9, 48(%rsp)\n"
" movq %r10, 56(%rsp)\n"
" movq %r11, 64(%rsp)\n"
"\n"
" movq %xmm0, 96(%rsp)\n"
" movq %xmm1, 112(%rsp)\n"
" movq %xmm2, 128(%rsp)\n"
" movq %xmm3, 144(%rsp)\n"
" movq %xmm4, 160(%rsp)\n"
" movq %xmm5, 176(%rsp)\n"
" movq %xmm6, 192(%rsp)\n"
" movq %xmm7, 208(%rsp)\n"
" movq %xmm8, 224(%rsp)\n"
" movq %xmm9, 240(%rsp)\n"
" movq %xmm10, 256(%rsp)\n"
" movq %xmm11, 272(%rsp)\n"
" movq %xmm12, 288(%rsp)\n"
" movq %xmm13, 304(%rsp)\n"
" movq %xmm14, 320(%rsp)\n"
" movq %xmm15, 336(%rsp)\n"
"\n"
" /* Map SHM, jumping to __afl_setup_abort if something goes wrong. */\n"
"\n"
" /* The 64-bit ABI requires 16-byte stack alignment. We'll keep the\n"
" original stack ptr in the callee-saved r12. */\n"
"\n"
" pushq %r12\n"
" movq %rsp, %r12\n"
" subq $16, %rsp\n"
" andq $0xfffffffffffffff0, %rsp\n"
"\n"
" leaq .AFL_SHM_ENV(%rip), %rdi\n"
CALL_L64("getenv")
"\n"
" testq %rax, %rax\n"
" je __afl_setup_abort\n"
"\n"
" movq %rax, %rdi\n"
CALL_L64("atoi")
"\n"
" xorq %rdx, %rdx /* shmat flags */\n"
" xorq %rsi, %rsi /* requested addr */\n"
" movq %rax, %rdi /* SHM ID */\n"
CALL_L64("shmat")
"\n"
" cmpq $-1, %rax\n"
" je __afl_setup_abort\n"
"\n"
" /* Store the address of the SHM region. */\n"
"\n"
" movq %rax, %rdx\n"
" movq %rax, __afl_area_ptr(%rip)\n"
"\n"
#ifdef __APPLE__
" movq %rax, __afl_global_area_ptr(%rip)\n"
#else
" movq __afl_global_area_ptr@GOTPCREL(%rip), %rdx\n"
" movq %rax, (%rdx)\n"
#endif /* ^__APPLE__ */
" movq %rax, %rdx\n"
"\n"
"__afl_forkserver:\n"
"\n"
" /* Enter the fork server mode to avoid the overhead of execve() calls. We\n"
" push rdx (area ptr) twice to keep stack alignment neat. */\n"
"\n"
" pushq %rdx\n"
" pushq %rdx\n"
"\n"
" /* Phone home and tell the parent that we're OK. (Note that signals with\n"
" no SA_RESTART will mess it up). If this fails, assume that the fd is\n"
" closed because we were execve()d from an instrumented binary, or because\n"
" the parent doesn't want to use the fork server. */\n"
"\n"
" movq $4, %rdx /* length */\n"
" leaq __afl_temp(%rip), %rsi /* data */\n"
" movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n"
CALL_L64("write")
"\n"
" cmpq $4, %rax\n"
" jne __afl_fork_resume\n"
"\n"
"__afl_fork_wait_loop:\n"
"\n"
" /* Wait for parent by reading from the pipe. Abort if read fails. */\n"
"\n"
" movq $4, %rdx /* length */\n"
" leaq __afl_temp(%rip), %rsi /* data */\n"
" movq $" STRINGIFY(FORKSRV_FD) ", %rdi /* file desc */\n"
CALL_L64("read")
" cmpq $4, %rax\n"
" jne __afl_die\n"
"\n"
" /* Once woken up, create a clone of our process. This is an excellent use\n"
" case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n"
" caches getpid() results and offers no way to update the value, breaking\n"
" abort(), raise(), and a bunch of other things :-( */\n"
"\n"
CALL_L64("fork")
" cmpq $0, %rax\n"
" jl __afl_die\n"
" je __afl_fork_resume\n"
"\n"
" /* In parent process: write PID to pipe, then wait for child. */\n"
"\n"
" movl %eax, __afl_fork_pid(%rip)\n"
"\n"
" movq $4, %rdx /* length */\n"
" leaq __afl_fork_pid(%rip), %rsi /* data */\n"
" movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n"
CALL_L64("write")
"\n"
" movq $0, %rdx /* no flags */\n"
" leaq __afl_temp(%rip), %rsi /* status */\n"
" movq __afl_fork_pid(%rip), %rdi /* PID */\n"
CALL_L64("waitpid")
" cmpq $0, %rax\n"
" jle __afl_die\n"
"\n"
" /* Relay wait status to pipe, then loop back. */\n"
"\n"
" movq $4, %rdx /* length */\n"
" leaq __afl_temp(%rip), %rsi /* data */\n"
" movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi /* file desc */\n"
CALL_L64("write")
"\n"
" jmp __afl_fork_wait_loop\n"
"\n"
"__afl_fork_resume:\n"
"\n"
" /* In child process: close fds, resume execution. */\n"
"\n"
" movq $" STRINGIFY(FORKSRV_FD) ", %rdi\n"
CALL_L64("close")
"\n"
" movq $" STRINGIFY((FORKSRV_FD + 1)) ", %rdi\n"
CALL_L64("close")
"\n"
" popq %rdx\n"
" popq %rdx\n"
"\n"
" movq %r12, %rsp\n"
" popq %r12\n"
"\n"
" movq 0(%rsp), %rax\n"
" movq 8(%rsp), %rcx\n"
" movq 16(%rsp), %rdi\n"
" movq 32(%rsp), %rsi\n"
" movq 40(%rsp), %r8\n"
" movq 48(%rsp), %r9\n"
" movq 56(%rsp), %r10\n"
" movq 64(%rsp), %r11\n"
"\n"
" movq 96(%rsp), %xmm0\n"
" movq 112(%rsp), %xmm1\n"
" movq 128(%rsp), %xmm2\n"
" movq 144(%rsp), %xmm3\n"
" movq 160(%rsp), %xmm4\n"
" movq 176(%rsp), %xmm5\n"
" movq 192(%rsp), %xmm6\n"
" movq 208(%rsp), %xmm7\n"
" movq 224(%rsp), %xmm8\n"
" movq 240(%rsp), %xmm9\n"
" movq 256(%rsp), %xmm10\n"
" movq 272(%rsp), %xmm11\n"
" movq 288(%rsp), %xmm12\n"
" movq 304(%rsp), %xmm13\n"
" movq 320(%rsp), %xmm14\n"
" movq 336(%rsp), %xmm15\n"
"\n"
" leaq 352(%rsp), %rsp\n"
"\n"
" jmp __afl_store\n"
"\n"
"__afl_die:\n"
"\n"
" xorq %rax, %rax\n"
CALL_L64("_exit")
"\n"
"__afl_setup_abort:\n"
"\n"
" /* Record setup failure so that we don't keep calling\n"
" shmget() / shmat() over and over again. */\n"
"\n"
" incb __afl_setup_failure(%rip)\n"
"\n"
" movq %r12, %rsp\n"
" popq %r12\n"
"\n"
" movq 0(%rsp), %rax\n"
" movq 8(%rsp), %rcx\n"
" movq 16(%rsp), %rdi\n"
" movq 32(%rsp), %rsi\n"
" movq 40(%rsp), %r8\n"
" movq 48(%rsp), %r9\n"
" movq 56(%rsp), %r10\n"
" movq 64(%rsp), %r11\n"
"\n"
" movq 96(%rsp), %xmm0\n"
" movq 112(%rsp), %xmm1\n"
" movq 128(%rsp), %xmm2\n"
" movq 144(%rsp), %xmm3\n"
" movq 160(%rsp), %xmm4\n"
" movq 176(%rsp), %xmm5\n"
" movq 192(%rsp), %xmm6\n"
" movq 208(%rsp), %xmm7\n"
" movq 224(%rsp), %xmm8\n"
" movq 240(%rsp), %xmm9\n"
" movq 256(%rsp), %xmm10\n"
" movq 272(%rsp), %xmm11\n"
" movq 288(%rsp), %xmm12\n"
" movq 304(%rsp), %xmm13\n"
" movq 320(%rsp), %xmm14\n"
" movq 336(%rsp), %xmm15\n"
"\n"
" leaq 352(%rsp), %rsp\n"
"\n"
" jmp __afl_return\n"
"\n"
".AFL_VARS:\n"
"\n"

#ifdef __APPLE__

" .comm __afl_area_ptr, 8\n"
#ifndef COVERAGE_ONLY
" .comm __afl_prev_loc, 8\n"
#endif /* !COVERAGE_ONLY */
" .comm __afl_fork_pid, 4\n"
" .comm __afl_temp, 4\n"
" .comm __afl_setup_failure, 1\n"

#else

" .lcomm __afl_area_ptr, 8\n"
#ifndef COVERAGE_ONLY
" .lcomm __afl_prev_loc, 8\n"
#endif /* !COVERAGE_ONLY */
" .lcomm __afl_fork_pid, 4\n"
" .lcomm __afl_temp, 4\n"
" .lcomm __afl_setup_failure, 1\n"

#endif /* ^__APPLE__ */

" .comm __afl_global_area_ptr, 8, 8\n"
"\n"
".AFL_SHM_ENV:\n"
" .asciz \"" SHM_ENV_VAR "\"\n"
"\n"
"/* --- END --- */\n"
"\n";

#endif /* !_HAVE_AFL_AS_H */

main_payload_64中插入了很多afl类的函数,方便调用

有源码的情况

将源码编译生成.s文件,然后对.s文件插桩,最后生成插桩后的文件modifed_file
插桩的位置有如下情况
1.条件跳转指令(jne\je\jle等)
2.main函数入口
3.function入口
条件:
1.只在代码段插桩
2.只对AT&T汇编插桩(intel跳过)