mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-24 22:53:24 +00:00
Compare commits
516 Commits
Author | SHA1 | Date | |
---|---|---|---|
af8c68a774 | |||
bf2727b763 | |||
e71d422b3c | |||
88603a2c2e | |||
a4b9272416 | |||
f6471dd256 | |||
26cbc1e993 | |||
f0ccca123a | |||
62bacf4fc8 | |||
7c84331dc5 | |||
ee2cab73ac | |||
2f6b54e441 | |||
234d55ccd5 | |||
993d0c267d | |||
281f6c1ea1 | |||
9585f5cdfe | |||
abc26a932a | |||
28fd971608 | |||
f9b72b6f2f | |||
b644e48f36 | |||
2b500ce97e | |||
9324f3f628 | |||
63a7a816e7 | |||
06e1c64745 | |||
7870ece6dc | |||
e596c9856b | |||
ed73c632a5 | |||
ad8f7d6eb3 | |||
074b5ba54d | |||
b08e6bf8c6 | |||
c7ced56066 | |||
287128a196 | |||
c9dfc279c7 | |||
c323e0dc63 | |||
b10a091408 | |||
eeed38c5f8 | |||
501226c992 | |||
8e1df8e53d | |||
8985524d3a | |||
b81e0fece6 | |||
22837b5ad2 | |||
dd736126dc | |||
d5e3223f03 | |||
029e039cbc | |||
1416fea160 | |||
d4085314c1 | |||
9a6c0ec0c0 | |||
53a869b757 | |||
eec2c38a68 | |||
401d7617ef | |||
abd6eace9d | |||
f664eb58c5 | |||
3e3adb4d37 | |||
1d0694df86 | |||
dfdc6fd12c | |||
49997e60cb | |||
1ad63a6a32 | |||
6d23df2c7c | |||
c4b1566ba3 | |||
d91f8fa655 | |||
7f636dbfc2 | |||
93c821aaa3 | |||
a752b15921 | |||
3a98d7af18 | |||
eaf59d5a19 | |||
70da0c2e40 | |||
c97caa6e10 | |||
c092892488 | |||
001d9d3d20 | |||
2c421d48fa | |||
f585f26266 | |||
396157deda | |||
f516926f00 | |||
a7b7f3cde9 | |||
22db79aefa | |||
2cd07abca9 | |||
fcab3ec990 | |||
9065d4ba86 | |||
ed96f9b209 | |||
f567a89dae | |||
00c86b7cb1 | |||
74be9ab5ce | |||
5813a4319c | |||
e956f23a77 | |||
41b0fe7280 | |||
6cad585bdc | |||
6172bc7312 | |||
a2daef29f9 | |||
e983e2e9cf | |||
a25439cfa1 | |||
3e84d6a2ae | |||
7ca1b85c5e | |||
b18bc7b98f | |||
432671449f | |||
96848398d4 | |||
21865c6224 | |||
b96ba509d0 | |||
f94a7e8890 | |||
2e23418a09 | |||
f3dc56f59a | |||
d822181467 | |||
bc969f78f6 | |||
7b877e2c1d | |||
c0ecf7cf61 | |||
7b33148b75 | |||
b66d7f99a7 | |||
7c3c0b26d1 | |||
46237c3332 | |||
8c228b0d23 | |||
531380d6ab | |||
dbb3171624 | |||
6bd48a48cb | |||
c5e5a17d67 | |||
599b4631a3 | |||
228e9527cb | |||
53ff09969c | |||
d7e6f8cb38 | |||
e99d4ba976 | |||
de717cd225 | |||
779a72ef8c | |||
dae5f94bce | |||
c49d346e37 | |||
7a8d0a10ce | |||
369ec31f0e | |||
0a297ed9ef | |||
30495e6bfe | |||
7101ffa1ae | |||
4e5f42cab6 | |||
9ab902402c | |||
529a51c160 | |||
e55b5c5408 | |||
450dbae8cd | |||
a3bc8d3440 | |||
70e3095864 | |||
02b9e583f2 | |||
a326c23210 | |||
7f734c96d1 | |||
4d29e484b7 | |||
56f7e3aa08 | |||
87b9dc4ba0 | |||
d0b86bf055 | |||
0a699d885b | |||
61aeb44863 | |||
d213071e13 | |||
6d4234b305 | |||
059d470e8d | |||
47833bcf9e | |||
2f6242d3f8 | |||
120d009e7d | |||
e12acaa203 | |||
8f6d9d66ef | |||
2b81d2d63f | |||
50678ed369 | |||
9764483693 | |||
0c4f0dd4c4 | |||
b5f7f42cd0 | |||
c34c3e2f5f | |||
4f6ec6cb08 | |||
a96cdc649f | |||
e2fedce6ec | |||
afc47868ee | |||
eefd98f374 | |||
2adf5aac0f | |||
f756734ad2 | |||
6cc8d607fb | |||
824385f52c | |||
d304f4e4f1 | |||
4f2d9eeaaa | |||
743ae50775 | |||
a5a122a533 | |||
1589e17213 | |||
9e3e1a5512 | |||
a0818c4fce | |||
0782ed3841 | |||
0911525194 | |||
0eace0212e | |||
c1af004451 | |||
abd6b06fa4 | |||
35151cefe8 | |||
75d7a09469 | |||
f9851dbfbb | |||
d67ee17778 | |||
dba93705a7 | |||
36127fb197 | |||
5fea071ae9 | |||
a01138e1c8 | |||
319b2e8e6f | |||
e46e0bce44 | |||
4d02d8e43d | |||
be96253f52 | |||
2d0d1e267e | |||
a061e1304d | |||
dbdf2d79f2 | |||
48816417ee | |||
661b626c87 | |||
214e24fff0 | |||
67e8c4f100 | |||
5d9c1bc3a3 | |||
c594a58583 | |||
01236f47bc | |||
b1bfc1ae76 | |||
0faa323f12 | |||
2d3c5cc6d8 | |||
7101192865 | |||
19f9612910 | |||
d955409178 | |||
9bc5abc4ec | |||
64fd01d46c | |||
b2f9802f9f | |||
e24ae96361 | |||
0f62e0b167 | |||
478f0bbc1e | |||
3d7bad99b6 | |||
5c5b73df82 | |||
24503fba5f | |||
d80cedcf02 | |||
bd3855fe5d | |||
84274f2e5d | |||
988028bb3b | |||
ac4dd1605e | |||
7ab689ef0e | |||
e32e825372 | |||
c3cf918bcb | |||
af77dab666 | |||
1d4f779d4d | |||
4c7c78d926 | |||
aabfe781fd | |||
7a650e4866 | |||
30483919eb | |||
5221938945 | |||
dc7ef967d8 | |||
1385c24a7d | |||
cd57641705 | |||
e0866f51c7 | |||
aa125f8246 | |||
b571e88bd3 | |||
2f128e0dbd | |||
e5f8c7a612 | |||
e6a05382b8 | |||
cb8296bdb0 | |||
6b75fe1831 | |||
a30664c563 | |||
7034348c57 | |||
e9e440d7f3 | |||
5e7f8a51e0 | |||
403d95d2d2 | |||
07cf27cddc | |||
bc61c90fb6 | |||
6af195916c | |||
7c07437941 | |||
85fa17451d | |||
12262d3a63 | |||
2ff0ff7a90 | |||
d8ba0caab3 | |||
86bf009792 | |||
ea14f3fd40 | |||
2b5c9954f6 | |||
65d4d10762 | |||
2bea77e28a | |||
add2eb42c0 | |||
ffdb5ec9b1 | |||
eb5c1ee4f3 | |||
eeccb2da69 | |||
0c0a6c3bfa | |||
a293281b9b | |||
8a8e350f34 | |||
c33f8751e3 | |||
6f4b5ae083 | |||
91b7f1c9f2 | |||
b786558dea | |||
ebaac23a51 | |||
9da3a2ed45 | |||
1faf6f6731 | |||
7f2bafbb8b | |||
ae94499503 | |||
04356ecbbe | |||
2090f17a9b | |||
668f5e1fa9 | |||
a7c43484e1 | |||
b352e3d1cc | |||
8bc3fa1df2 | |||
80eabd6e8a | |||
5a0100c6ee | |||
54fa78d32c | |||
61439859ce | |||
240f6421d8 | |||
7eaef449a1 | |||
3881ccd0b7 | |||
6030df2f56 | |||
141c324eb9 | |||
bac8d25bc2 | |||
d3cdeabf92 | |||
673a0a3866 | |||
f2be73186e | |||
025f617d6a | |||
c86d06849b | |||
05b1189a55 | |||
846e910e0c | |||
ab26356bf7 | |||
03e6d33a40 | |||
6596284cc4 | |||
dbfa23b40a | |||
1a9f96858b | |||
24e36212d5 | |||
ca2e8a1bf6 | |||
ca063c92d2 | |||
8a2547073c | |||
e612028255 | |||
f99656e22b | |||
90f61552f7 | |||
ec87abda93 | |||
e1434bcfcd | |||
53c19a807c | |||
3a6dea420f | |||
df9ef84f5e | |||
25b4b32627 | |||
4946e9cc3a | |||
f01bf77604 | |||
91ccbf3f68 | |||
686382c328 | |||
0d55feb11d | |||
31727f36a8 | |||
b5d8d4c866 | |||
b81bc8eb6f | |||
47f35d29ac | |||
4a54555a1a | |||
3e8a691a81 | |||
33eba1fc56 | |||
4ec26fc7cb | |||
5db7be5ee2 | |||
614265897c | |||
f4a13585a1 | |||
e332d37d4e | |||
90a259d523 | |||
322e5e2fb6 | |||
1b4e1d75b3 | |||
c52a0a15c8 | |||
c5a84a124c | |||
16f71bfa24 | |||
b0898de377 | |||
0251b9bfd8 | |||
5837322310 | |||
4063a3eb4c | |||
8bc7b3cf26 | |||
afd2ea90df | |||
bd2cb4cd1c | |||
67cfe4f6d4 | |||
628b4b6002 | |||
86d3c65559 | |||
afff6f642c | |||
eeca3a0b29 | |||
151a8facae | |||
45567791c6 | |||
17752465e6 | |||
14d8eb9e40 | |||
8fe5e29104 | |||
22f757a169 | |||
0db662db7b | |||
70f4b456fa | |||
a41fd5cc5c | |||
efe57c9368 | |||
33f41e3974 | |||
4b915207c4 | |||
8cc1c6c54e | |||
10b82c7277 | |||
35801bed7a | |||
5163a49350 | |||
7abbc8d740 | |||
9548af52b2 | |||
e3dadbfe0f | |||
75fb918a37 | |||
c3d7612c97 | |||
e2acba57bf | |||
e4b7c4e6c9 | |||
e4ff0ebd56 | |||
3b6fcd911a | |||
a3b56e7280 | |||
57e7408774 | |||
462e55da0c | |||
a8b6365a90 | |||
aa39921e49 | |||
885a6fc106 | |||
db9fc49ef8 | |||
94d1740390 | |||
1c91d8ca79 | |||
081c480e36 | |||
8d9620eca2 | |||
35f09e11a4 | |||
026404c452 | |||
ffe89e8f2d | |||
31d4dc8a38 | |||
e847b9948d | |||
1bcc9bfa91 | |||
8817da8ae4 | |||
1149b13185 | |||
fd27b2c9be | |||
5670c847bd | |||
99c67defb4 | |||
209527907f | |||
489f2d4d97 | |||
0710e4f17c | |||
c090abb00d | |||
107ebb7d49 | |||
4ff37da709 | |||
86ec1b9d71 | |||
c0c985a278 | |||
f28f6adbce | |||
51e0707d4d | |||
3188cac1d0 | |||
b189640a92 | |||
3d031f93a6 | |||
3a134edd88 | |||
6e5c08b653 | |||
67ae1d5839 | |||
b4000dda8d | |||
0062a14aa3 | |||
4626435dbf | |||
a0eee2bd92 | |||
6fe38b2138 | |||
342081d5ee | |||
65729a2637 | |||
401811a97d | |||
60dc37a8cf | |||
0165ca8c6c | |||
96f05c7f69 | |||
195bf87f18 | |||
e30c20cd28 | |||
e26c173041 | |||
85b44bb730 | |||
42e6f98005 | |||
e02753fd7d | |||
bf1617d354 | |||
149366507d | |||
9239ab01df | |||
0885dda767 | |||
6dd9764cf6 | |||
b5f2a17235 | |||
32a331ab43 | |||
28af7cb9bd | |||
9734d0b3c0 | |||
4124a272d8 | |||
a16726039f | |||
6655d66a9b | |||
2c39c51263 | |||
ef0921d858 | |||
b7c87350cf | |||
f667279b70 | |||
26a5bd625c | |||
d7e788a3c0 | |||
170e8122ae | |||
ad6a4cf1c2 | |||
ba788591dc | |||
8f9726d4a9 | |||
3d07f0ab79 | |||
4b7126c46c | |||
903b5607bc | |||
8cdc48f73a | |||
e5c725c4e0 | |||
1cc2029179 | |||
46beedadd7 | |||
3cc5019fe4 | |||
57257ce656 | |||
e2ebebce83 | |||
4a7cd53f64 | |||
7f7cbe9623 | |||
189255d3f4 | |||
bb81fb784e | |||
c5f8869778 | |||
b2f12c3712 | |||
2323952d62 | |||
dfc6d0fbf7 | |||
c24939e812 | |||
b868758cd7 | |||
513a6ce7b6 | |||
c1562a7cde | |||
0496390526 | |||
d65cf10bad | |||
21372473a1 | |||
a8c1ba0d59 | |||
1bc7cf759d | |||
c058ce37bb | |||
9dab653563 | |||
6553e24b22 | |||
2d640558a0 | |||
ec19a9b068 | |||
c0eaf6f47a | |||
0b6007a49c | |||
2da6b6bf42 | |||
15a26d3303 | |||
340647c5f1 | |||
a6a26d8153 | |||
a075cddef6 | |||
05e0825d66 | |||
02502c1a54 | |||
2cbe49c6eb | |||
102b749c07 | |||
7512316b46 | |||
f84ea69660 | |||
e9ecfed81d | |||
0e908d5b1e | |||
5ccf389414 | |||
96ee3fb899 | |||
ca9854a924 | |||
9a77a6fa92 | |||
23580e2cb6 | |||
089e773d1e | |||
26d4771678 | |||
d1e1bbc713 | |||
77ebab64d3 | |||
939575de4b | |||
7461c52278 | |||
fac6491ad0 | |||
4bb4d6ebfd | |||
23e477caa7 | |||
182013a26f | |||
bd27adf9af |
@ -3,10 +3,10 @@
|
||||
# american fuzzy lop++ - custom code formatter
|
||||
# --------------------------------------------
|
||||
#
|
||||
# Written and maintaned by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
# Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
#
|
||||
# Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||
# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
# Copyright 2019-2023 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.
|
||||
@ -18,24 +18,57 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
# import re # TODO: for future use
|
||||
import shutil
|
||||
import importlib.metadata
|
||||
|
||||
# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # TODO: for future use
|
||||
|
||||
CURRENT_LLVM = os.getenv('LLVM_VERSION', 15)
|
||||
CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "")
|
||||
|
||||
|
||||
def check_clang_format_pip_version():
|
||||
"""
|
||||
Check if the correct version of clang-format is installed via pip.
|
||||
|
||||
Returns:
|
||||
bool: True if the correct version of clang-format is installed,
|
||||
False otherwise.
|
||||
"""
|
||||
# Check if clang-format is installed
|
||||
if importlib.util.find_spec('clang_format'):
|
||||
# Check if the installed version is the expected LLVM version
|
||||
if importlib.metadata.version('clang-format')\
|
||||
.startswith(str(CURRENT_LLVM)+'.'):
|
||||
return True
|
||||
else:
|
||||
# Return False, because the clang-format version does not match
|
||||
return False
|
||||
else:
|
||||
# If the 'clang_format' package isn't installed, return False
|
||||
return False
|
||||
|
||||
# string_re = re.compile('(\\"(\\\\.|[^"\\\\])*\\")') # future use
|
||||
|
||||
with open(".clang-format") as f:
|
||||
fmt = f.read()
|
||||
|
||||
CURRENT_LLVM = os.getenv('LLVM_VERSION', 14)
|
||||
CLANG_FORMAT_BIN = os.getenv("CLANG_FORMAT_BIN", "")
|
||||
|
||||
CLANG_FORMAT_PIP = check_clang_format_pip_version()
|
||||
|
||||
if shutil.which(CLANG_FORMAT_BIN) is None:
|
||||
CLANG_FORMAT_BIN = f"clang-format-{CURRENT_LLVM}"
|
||||
|
||||
if shutil.which(CLANG_FORMAT_BIN) is None:
|
||||
if shutil.which(CLANG_FORMAT_BIN) is None \
|
||||
and CLANG_FORMAT_PIP is False:
|
||||
print(f"[!] clang-format-{CURRENT_LLVM} is needed. Aborted.")
|
||||
print(f"Run `pip3 install \"clang-format=={CURRENT_LLVM}.*\"` \
|
||||
to install via pip.")
|
||||
exit(1)
|
||||
|
||||
if CLANG_FORMAT_PIP:
|
||||
CLANG_FORMAT_BIN = shutil.which("clang-format")
|
||||
|
||||
COLUMN_LIMIT = 80
|
||||
for line in fmt.split("\n"):
|
||||
line = line.split(":")
|
||||
@ -54,43 +87,43 @@ def custom_format(filename):
|
||||
|
||||
for line in src.split("\n"):
|
||||
if line.lstrip().startswith("#"):
|
||||
if line[line.find("#") + 1 :].lstrip().startswith("define"):
|
||||
if line[line.find("#") + 1:].lstrip().startswith("define"):
|
||||
in_define = True
|
||||
|
||||
if (
|
||||
"/*" in line
|
||||
and not line.strip().startswith("/*")
|
||||
and line.endswith("*/")
|
||||
and len(line) < (COLUMN_LIMIT - 2)
|
||||
"/*" in line
|
||||
and not line.strip().startswith("/*")
|
||||
and line.endswith("*/")
|
||||
and len(line) < (COLUMN_LIMIT - 2)
|
||||
):
|
||||
cmt_start = line.rfind("/*")
|
||||
line = (
|
||||
line[:cmt_start]
|
||||
+ " " * (COLUMN_LIMIT - 2 - len(line))
|
||||
+ line[cmt_start:]
|
||||
line[:cmt_start]
|
||||
+ " " * (COLUMN_LIMIT - 2 - len(line))
|
||||
+ line[cmt_start:]
|
||||
)
|
||||
|
||||
define_padding = 0
|
||||
if last_line is not None and in_define and last_line.endswith("\\"):
|
||||
last_line = last_line[:-1]
|
||||
define_padding = max(0, len(last_line[last_line.rfind("\n") + 1 :]))
|
||||
define_padding = max(0, len(last_line[last_line.rfind("\n") + 1:]))
|
||||
|
||||
if (
|
||||
last_line is not None
|
||||
and last_line.strip().endswith("{")
|
||||
and line.strip() != ""
|
||||
last_line is not None
|
||||
and last_line.strip().endswith("{")
|
||||
and line.strip() != ""
|
||||
):
|
||||
line = (" " * define_padding + "\\" if in_define else "") + "\n" + line
|
||||
elif (
|
||||
last_line is not None
|
||||
and last_line.strip().startswith("}")
|
||||
and line.strip() != ""
|
||||
last_line is not None
|
||||
and last_line.strip().startswith("}")
|
||||
and line.strip() != ""
|
||||
):
|
||||
line = (" " * define_padding + "\\" if in_define else "") + "\n" + line
|
||||
elif (
|
||||
line.strip().startswith("}")
|
||||
and last_line is not None
|
||||
and last_line.strip() != ""
|
||||
line.strip().startswith("}")
|
||||
and last_line is not None
|
||||
and last_line.strip() != ""
|
||||
):
|
||||
line = (" " * define_padding + "\\" if in_define else "") + "\n" + line
|
||||
|
||||
|
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: "${{ matrix.os }}"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
|
||||
os: [ubuntu-22.04, ubuntu-20.04]
|
||||
env:
|
||||
AFL_SKIP_CPUFREQ: 1
|
||||
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||
@ -23,34 +23,36 @@ jobs:
|
||||
- name: debug
|
||||
run: apt-cache search plugin-dev | grep gcc-; echo; apt-cache search clang-format- | grep clang-format-
|
||||
- name: update
|
||||
run: sudo apt-get update && sudo apt-get upgrade -y
|
||||
run: sudo apt-get update
|
||||
# && sudo apt-get upgrade -y
|
||||
- name: install packages
|
||||
run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build
|
||||
#run: sudo apt-get install -y -m -f --install-suggests build-essential git libtool libtool-bin automake bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build python3-pip
|
||||
run: sudo apt-get install -y -m -f build-essential git libtool libtool-bin automake flex bison libglib2.0-0 clang llvm-dev libc++-dev findutils libcmocka-dev python3-dev python3-setuptools ninja-build python3-pip
|
||||
- name: compiler installed
|
||||
run: gcc -v; echo; clang -v
|
||||
- name: install gcc plugin
|
||||
run: sudo apt-get install -y -m -f --install-suggests $(readlink /usr/bin/gcc)-plugin-dev
|
||||
- name: build afl++
|
||||
run: make distrib ASAN_BUILD=1
|
||||
run: make distrib ASAN_BUILD=1 NO_NYX=1
|
||||
- name: run tests
|
||||
run: sudo -E ./afl-system-config; make tests
|
||||
macos:
|
||||
runs-on: macOS-latest
|
||||
env:
|
||||
AFL_MAP_SIZE: 65536
|
||||
AFL_SKIP_CPUFREQ: 1
|
||||
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install
|
||||
run: brew install make gcc llvm
|
||||
- name: fix install
|
||||
run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v
|
||||
- name: build
|
||||
run: export PATH=/usr/local/Cellar/llvm/*/":$PATH"; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; sudo -E ./afl-system-config; gmake ASAN_BUILD=1
|
||||
- name: frida
|
||||
run: export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; cd frida_mode; gmake
|
||||
- name: run tests
|
||||
run: sudo -E ./afl-system-config; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export PATH=/usr/local/Cellar/llvm/*/":/usr/local/bin:$PATH"; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; gmake tests
|
||||
- name: force frida test for MacOS
|
||||
run: export AFL_PATH=`pwd`; /usr/local/bin/gcc -o test-instr test-instr.c; mkdir in; echo > in/in; AFL_NO_UI=1 ./afl-fuzz -O -i in -o out -V 5 -- ./test-instr
|
||||
# macos:
|
||||
# runs-on: macOS-latest
|
||||
# env:
|
||||
# AFL_MAP_SIZE: 65536
|
||||
# AFL_SKIP_CPUFREQ: 1
|
||||
# AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: 1
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - name: install
|
||||
# run: brew install make gcc llvm
|
||||
# - name: fix install
|
||||
# run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v
|
||||
# - name: build
|
||||
# run: export PATH=/usr/local/Cellar/llvm/*/":$PATH"; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; sudo -E ./afl-system-config; gmake ASAN_BUILD=1
|
||||
# - name: frida
|
||||
# run: export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; cd frida_mode; gmake
|
||||
# - name: run tests
|
||||
# run: sudo -E ./afl-system-config; export CC=/usr/local/Cellar/llvm/*/bin/clang; export CXX="$CC"++; export PATH=/usr/local/Cellar/llvm/*/":/usr/local/bin:$PATH"; export LLVM_CONFIG=/usr/local/Cellar/llvm/*/bin/llvm-config; gmake tests
|
||||
# - name: force frida test for MacOS
|
||||
# run: export AFL_PATH=`pwd`; /usr/local/bin/gcc -o test-instr test-instr.c; mkdir in; echo > in/in; AFL_NO_UI=1 ./afl-fuzz -O -i in -o out -V 5 -- ./test-instr
|
||||
|
133
.gitignore
vendored
133
.gitignore
vendored
@ -1,100 +1,107 @@
|
||||
.test
|
||||
.test2
|
||||
.sync_tmp
|
||||
.vscode
|
||||
!coresight_mode
|
||||
!coresight_mode/coresight-trace
|
||||
*.dSYM
|
||||
*.o
|
||||
*.o.tmp
|
||||
*.pyc
|
||||
*.so
|
||||
*.swp
|
||||
*.pyc
|
||||
*.dSYM
|
||||
as
|
||||
a.out
|
||||
ld
|
||||
in
|
||||
out
|
||||
core*
|
||||
compile_commands.json
|
||||
.sync_tmp
|
||||
.test
|
||||
.test2
|
||||
.vscode
|
||||
afl-analyze
|
||||
afl-analyze.8
|
||||
afl-as
|
||||
afl-as.8
|
||||
afl-c++
|
||||
afl-c++.8
|
||||
afl-cc
|
||||
afl-cc.8
|
||||
afl-clang
|
||||
afl-clang++
|
||||
afl-clang-fast
|
||||
afl-clang-fast++
|
||||
afl-clang-lto
|
||||
afl-clang-lto++
|
||||
afl-fuzz
|
||||
afl-g++
|
||||
afl-gcc
|
||||
afl-gcc-fast
|
||||
afl-g++-fast
|
||||
afl-gotcpu
|
||||
afl-ld
|
||||
afl-ld-lto
|
||||
afl-cs-proxy
|
||||
afl-qemu-trace
|
||||
afl-showmap
|
||||
afl-tmin
|
||||
afl-analyze.8
|
||||
afl-as.8
|
||||
afl-clang-fast++.8
|
||||
afl-clang-fast.8
|
||||
afl-clang-lto.8
|
||||
afl-clang-lto
|
||||
afl-clang-lto++
|
||||
afl-clang-lto++.8
|
||||
afl-clang-lto.8
|
||||
afl-cmin.8
|
||||
afl-cmin.bash.8
|
||||
afl-cs-proxy
|
||||
afl-frida-trace.so
|
||||
afl-fuzz
|
||||
afl-fuzz.8
|
||||
afl-c++.8
|
||||
afl-cc.8
|
||||
afl-gcc.8
|
||||
afl-g++
|
||||
afl-g++.8
|
||||
afl-gcc
|
||||
afl-gcc.8
|
||||
afl-gcc-fast
|
||||
afl-gcc-fast.8
|
||||
afl-g++-fast
|
||||
afl-g++-fast.8
|
||||
afl-gotcpu
|
||||
afl-gotcpu.8
|
||||
afl-plot.8
|
||||
afl-showmap.8
|
||||
afl-system-config.8
|
||||
afl-tmin.8
|
||||
afl-whatsup.8
|
||||
afl-persistent-config.8
|
||||
afl-c++
|
||||
afl-cc
|
||||
afl-ld
|
||||
afl-ld-lto
|
||||
afl-lto
|
||||
afl-lto++
|
||||
afl-lto++.8
|
||||
afl-lto.8
|
||||
afl-persistent-config.8
|
||||
afl-plot.8
|
||||
afl-qemu-trace
|
||||
afl-showmap
|
||||
afl-showmap.8
|
||||
afl-system-config.8
|
||||
afl-tmin
|
||||
afl-tmin.8
|
||||
afl-whatsup.8
|
||||
a.out
|
||||
as
|
||||
compile_commands.json
|
||||
core*
|
||||
examples/afl_frida/afl-frida
|
||||
examples/afl_frida/frida-gum-example.c
|
||||
examples/afl_frida/frida-gum.h
|
||||
examples/afl_frida/libtestinstr.so
|
||||
examples/afl_network_proxy/afl-network-client
|
||||
examples/afl_network_proxy/afl-network-server
|
||||
examples/aflpp_driver/libAFLDriver.a
|
||||
examples/aflpp_driver/libAFLQemuDriver.a
|
||||
gmon.out
|
||||
in
|
||||
ld
|
||||
libAFLDriver.a
|
||||
libAFLQemuDriver.a
|
||||
out
|
||||
qemu_mode/libcompcov/compcovtest
|
||||
qemu_mode/qemu-*
|
||||
qemu_mode/qemuafl
|
||||
unicorn_mode/samples/*/\.test-*
|
||||
unicorn_mode/samples/*/output/
|
||||
test/.afl_performance
|
||||
test-instr
|
||||
test/output
|
||||
test/test-c
|
||||
test/test-cmplog
|
||||
test/test-compcov
|
||||
test/test-instr.ts
|
||||
test/test-persistent
|
||||
test/unittests/unit_hash
|
||||
test/unittests/unit_list
|
||||
test/unittests/unit_maybe_alloc
|
||||
test/unittests/unit_preallocable
|
||||
test/unittests/unit_list
|
||||
test/unittests/unit_rand
|
||||
test/unittests/unit_hash
|
||||
examples/afl_network_proxy/afl-network-server
|
||||
examples/afl_network_proxy/afl-network-client
|
||||
examples/afl_frida/afl-frida
|
||||
examples/afl_frida/libtestinstr.so
|
||||
examples/afl_frida/frida-gum-example.c
|
||||
examples/afl_frida/frida-gum.h
|
||||
examples/aflpp_driver/libAFLDriver.a
|
||||
examples/aflpp_driver/libAFLQemuDriver.a
|
||||
libAFLDriver.a
|
||||
libAFLQemuDriver.a
|
||||
test/.afl_performance
|
||||
gmon.out
|
||||
afl-frida-trace.so
|
||||
unicorn_mode/samples/*/output/
|
||||
unicorn_mode/samples/*/\.test-*
|
||||
utils/afl_network_proxy/afl-network-client
|
||||
utils/afl_network_proxy/afl-network-server
|
||||
utils/plot_ui/afl-plot-ui
|
||||
*.o.tmp
|
||||
utils/afl_proxy/afl-proxy
|
||||
utils/optimin/build
|
||||
utils/optimin/optimin
|
||||
utils/persistent_mode/persistent_demo
|
||||
utils/persistent_mode/persistent_demo_new
|
||||
utils/persistent_mode/test-instr
|
||||
!coresight_mode
|
||||
!coresight_mode/coresight-trace
|
||||
vuln_prog
|
||||
utils/plot_ui/afl-plot-ui
|
||||
vuln_prog
|
||||
|
10
Dockerfile
10
Dockerfile
@ -6,7 +6,7 @@
|
||||
#
|
||||
|
||||
FROM ubuntu:22.04 AS aflplusplus
|
||||
LABEL "maintainer"="afl++ team <afl@aflplus.plus>"
|
||||
LABEL "maintainer"="AFL++ team <afl@aflplus.plus>"
|
||||
LABEL "about"="AFLplusplus container image"
|
||||
|
||||
### Comment out to enable these features
|
||||
@ -39,7 +39,7 @@ RUN apt-get update && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
make cmake automake meson ninja-build bison flex \
|
||||
git xz-utils bzip2 wget jupp nano bash-completion less vim joe ssh psmisc \
|
||||
python3 python3-dev python3-setuptools python-is-python3 \
|
||||
python3 python3-dev python3-pip python-is-python3 \
|
||||
libtool libtool-bin libglib2.0-dev \
|
||||
apt-transport-https gnupg dialog \
|
||||
gnuplot-nox libpixman-1-dev \
|
||||
@ -47,7 +47,7 @@ RUN apt-get update && \
|
||||
clang-${LLVM_VERSION} clang-tools-${LLVM_VERSION} libc++1-${LLVM_VERSION} \
|
||||
libc++-${LLVM_VERSION}-dev libc++abi1-${LLVM_VERSION} libc++abi-${LLVM_VERSION}-dev \
|
||||
libclang1-${LLVM_VERSION} libclang-${LLVM_VERSION}-dev \
|
||||
libclang-common-${LLVM_VERSION}-dev libclang-cpp${LLVM_VERSION} \
|
||||
libclang-common-${LLVM_VERSION}-dev libclang-rt-${LLVM_VERSION}-dev libclang-cpp${LLVM_VERSION} \
|
||||
libclang-cpp${LLVM_VERSION}-dev liblld-${LLVM_VERSION} \
|
||||
liblld-${LLVM_VERSION}-dev liblldb-${LLVM_VERSION} liblldb-${LLVM_VERSION}-dev \
|
||||
libllvm${LLVM_VERSION} libomp-${LLVM_VERSION}-dev libomp5-${LLVM_VERSION} \
|
||||
@ -67,6 +67,8 @@ RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_VERSION} 0
|
||||
RUN wget -qO- https://sh.rustup.rs | CARGO_HOME=/etc/cargo sh -s -- -y -q --no-modify-path
|
||||
ENV PATH=$PATH:/etc/cargo/bin
|
||||
|
||||
RUN apt clean -y
|
||||
|
||||
ENV LLVM_CONFIG=llvm-config-${LLVM_VERSION}
|
||||
ENV AFL_SKIP_CPUFREQ=1
|
||||
ENV AFL_TRY_AFFINITY=1
|
||||
@ -92,4 +94,4 @@ RUN sed -i.bak 's/^ -/ /g' GNUmakefile && \
|
||||
RUN echo "set encoding=utf-8" > /root/.vimrc && \
|
||||
echo ". /etc/bash_completion" >> ~/.bashrc && \
|
||||
echo 'alias joe="joe --wordwrap --joe_state -nobackup"' >> ~/.bashrc && \
|
||||
echo "export PS1='"'[afl++ \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc
|
||||
echo "export PS1='"'[AFL++ \h] \w \$ '"'" >> ~/.bashrc
|
||||
|
107
GNUmakefile
107
GNUmakefile
@ -39,7 +39,7 @@ ASAN_OPTIONS=detect_leaks=0
|
||||
SYS = $(shell uname -s)
|
||||
ARCH = $(shell uname -m)
|
||||
|
||||
$(info [*] Compiling afl++ for OS $(SYS) on ARCH $(ARCH))
|
||||
$(info [*] Compiling AFL++ for OS $(SYS) on ARCH $(ARCH))
|
||||
|
||||
ifdef NO_SPLICING
|
||||
override CFLAGS_OPT += -DNO_SPLICING
|
||||
@ -91,9 +91,8 @@ ifneq "$(SYS)" "Darwin"
|
||||
#ifeq "$(HAVE_MARCHNATIVE)" "1"
|
||||
# SPECIAL_PERFORMANCE += -march=native
|
||||
#endif
|
||||
# OS X does not like _FORTIFY_SOURCE=2
|
||||
ifndef DEBUG
|
||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=2
|
||||
CFLAGS_OPT += -D_FORTIFY_SOURCE=1
|
||||
endif
|
||||
else
|
||||
# On some odd MacOS system configurations, the Xcode sdk path is not set correctly
|
||||
@ -101,9 +100,14 @@ else
|
||||
LDFLAGS += $(SDK_LD)
|
||||
endif
|
||||
|
||||
COMPILER_TYPE=$(shell $(CC) --version|grep "Free Software Foundation")
|
||||
ifneq "$(COMPILER_TYPE)" ""
|
||||
#$(info gcc is being used)
|
||||
CFLAGS_OPT += -Wno-error=format-truncation -Wno-format-truncation
|
||||
endif
|
||||
|
||||
ifeq "$(SYS)" "SunOS"
|
||||
CFLAGS_OPT += -Wno-format-truncation
|
||||
LDFLAGS = -lkstat -lrt
|
||||
LDFLAGS = -lkstat -lrt -lsocket -lnsl
|
||||
endif
|
||||
|
||||
ifdef STATIC
|
||||
@ -146,7 +150,7 @@ else
|
||||
endif
|
||||
|
||||
override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wno-pointer-arith \
|
||||
-fPIC -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \
|
||||
-fPIC -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \
|
||||
-DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\"
|
||||
# -fstack-protector
|
||||
|
||||
@ -181,13 +185,13 @@ AFL_FUZZ_FILES = $(wildcard src/afl-fuzz*.c)
|
||||
|
||||
ifneq "$(shell command -v python3m 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3m-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3m-config --includes)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3m --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python3m-config --includes)
|
||||
PYTHON_VERSION := $(strip $(shell python3m --version 2>&1))
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag.
|
||||
ifeq "$(shell python3m-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
|
||||
PYTHON_LIB ?= $(shell python3m-config --libs --embed --ldflags)
|
||||
PYTHON_LIB := $(shell python3m-config --libs --embed --ldflags)
|
||||
else
|
||||
PYTHON_LIB ?= $(shell python3m-config --ldflags)
|
||||
PYTHON_LIB := $(shell python3m-config --ldflags)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -195,13 +199,13 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python3 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3-config --includes)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3 --version 2>&1))
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earier versions didn't know this flag.
|
||||
PYTHON_INCLUDE := $(shell python3-config --includes)
|
||||
PYTHON_VERSION := $(strip $(shell python3 --version 2>&1))
|
||||
# Starting with python3.8, we need to pass the `embed` flag. Earlier versions didn't know this flag.
|
||||
ifeq "$(shell python3-config --embed --libs 2>/dev/null | grep -q lpython && echo 1 )" "1"
|
||||
PYTHON_LIB ?= $(shell python3-config --libs --embed --ldflags)
|
||||
PYTHON_LIB := $(shell python3-config --libs --embed --ldflags)
|
||||
else
|
||||
PYTHON_LIB ?= $(shell python3-config --ldflags)
|
||||
PYTHON_LIB := $(shell python3-config --ldflags)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -210,9 +214,9 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python-config --includes)
|
||||
PYTHON_LIB ?= $(shell python-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python-config --includes)
|
||||
PYTHON_LIB := $(shell python-config --ldflags)
|
||||
PYTHON_VERSION := $(strip $(shell python --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -221,9 +225,9 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python3.7 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python3.7-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python3.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python3.7-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python3.7 --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python3.7-config --includes)
|
||||
PYTHON_LIB := $(shell python3.7-config --ldflags)
|
||||
PYTHON_VERSION := $(strip $(shell python3.7 --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -232,9 +236,9 @@ endif
|
||||
ifeq "$(PYTHON_INCLUDE)" ""
|
||||
ifneq "$(shell command -v python2.7 2>/dev/null)" ""
|
||||
ifneq "$(shell command -v python2.7-config 2>/dev/null)" ""
|
||||
PYTHON_INCLUDE ?= $(shell python2.7-config --includes)
|
||||
PYTHON_LIB ?= $(shell python2.7-config --ldflags)
|
||||
PYTHON_VERSION ?= $(strip $(shell python2.7 --version 2>&1))
|
||||
PYTHON_INCLUDE := $(shell python2.7-config --includes)
|
||||
PYTHON_LIB := $(shell python2.7-config --ldflags)
|
||||
PYTHON_VERSION := $(strip $(shell python2.7 --version 2>&1))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -313,9 +317,9 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu
|
||||
@echo
|
||||
@echo Build Summary:
|
||||
@test -e afl-fuzz && echo "[+] afl-fuzz and supporting tools successfully built" || echo "[-] afl-fuzz could not be built, please set CC to a working compiler"
|
||||
@test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be build, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it"
|
||||
@test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be built, it is optional, if you want it, please install LLVM and LLD 11+. More information at instrumentation/README.lto.md on how to build it"
|
||||
ifneq "$(SYS)" "Darwin"
|
||||
@test -e afl-gcc-pass.so && echo "[+] gcc_mode successfully built" || echo "[-] gcc_mode could not be built, it is optional, install gcc-VERSION-plugin-dev to enable this"
|
||||
endif
|
||||
@ -358,7 +362,7 @@ performance-test: source-only
|
||||
help:
|
||||
@echo "HELP --- the following make targets exist:"
|
||||
@echo "=========================================="
|
||||
@echo "all: the main afl++ binaries and llvm/gcc instrumentation"
|
||||
@echo "all: the main AFL++ binaries and llvm/gcc instrumentation"
|
||||
@echo "binary-only: everything for binary-only fuzzing: frida_mode, nyx_mode, qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator, libtokencap"
|
||||
@echo "source-only: everything for source code fuzzing: nyx_mode, libdislocator, libtokencap"
|
||||
@echo "distrib: everything (for both binary-only and source code fuzzing)"
|
||||
@ -366,7 +370,7 @@ help:
|
||||
@echo "install: installs everything you have compiled with the build option above"
|
||||
@echo "clean: cleans everything compiled (not downloads when on a checkout)"
|
||||
@echo "deepclean: cleans everything including downloads"
|
||||
@echo "uninstall: uninstall afl++ from the system"
|
||||
@echo "uninstall: uninstall AFL++ from the system"
|
||||
@echo "code-format: format the code, do this before you commit and send a PR please!"
|
||||
@echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem"
|
||||
@echo "unit: perform unit tests (based on cmocka and GNU linker)"
|
||||
@ -378,9 +382,11 @@ help:
|
||||
@echo Known build environment options:
|
||||
@echo "=========================================="
|
||||
@echo STATIC - compile AFL++ static
|
||||
@echo "CODE_COVERAGE - compile the target for code coverage (see docs/instrumentation/README.llvm.md)"
|
||||
@echo ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes
|
||||
@echo UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes
|
||||
@echo DEBUG - no optimization, -ggdb3, all warnings and -Werror
|
||||
@echo LLVM_DEBUG - shows llvm deprecation warnings
|
||||
@echo PROFILING - compile afl-fuzz with profiling information
|
||||
@echo INTROSPECTION - compile afl-fuzz with mutation introspection
|
||||
@echo NO_PYTHON - disable python support
|
||||
@ -388,10 +394,11 @@ help:
|
||||
@echo NO_NYX - disable building nyx mode dependencies
|
||||
@echo "NO_CORESIGHT - disable building coresight (arm64 only)"
|
||||
@echo NO_UNICORN_ARM64 - disable building unicorn on arm64
|
||||
@echo "WAFL_MODE - enable for WASM fuzzing with https://github.com/fgsect/WAFL"
|
||||
@echo AFL_NO_X86 - if compiling on non-intel/amd platforms
|
||||
@echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)"
|
||||
@echo "=========================================="
|
||||
@echo e.g.: make ASAN_BUILD=1
|
||||
@echo e.g.: make LLVM_CONFIG=llvm-config-16
|
||||
|
||||
.PHONY: test_x86
|
||||
ifndef AFL_NO_X86
|
||||
@ -425,12 +432,12 @@ test_python:
|
||||
@echo "[+] $(PYTHON_VERSION) support seems to be working."
|
||||
else
|
||||
test_python:
|
||||
@echo "[-] You seem to need to install the package python3-dev, python2-dev or python-dev (and perhaps python[23]-apt), but it is optional so we continue"
|
||||
@echo "[-] You seem to need to install the package python3-dev or python-dev (and perhaps python[3]-apt), but it is optional so we continue"
|
||||
endif
|
||||
|
||||
.PHONY: ready
|
||||
ready:
|
||||
@echo "[+] Everything seems to be working, ready to compile."
|
||||
@echo "[+] Everything seems to be working, ready to compile. ($(shell $(CC) --version 2>&1|head -n 1))"
|
||||
|
||||
afl-as: src/afl-as.c include/afl-as.h $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) src/$@.c -o $@ $(LDFLAGS)
|
||||
@ -452,7 +459,7 @@ afl-fuzz: $(COMM_HDR) include/afl-fuzz.h $(AFL_FUZZ_FILES) src/afl-common.o src/
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) $(AFL_FUZZ_FILES) src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS) -lm
|
||||
|
||||
afl-showmap: src/afl-showmap.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-fuzz-mutators.c src/afl-fuzz-python.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(PYFLAGS) $(LDFLAGS)
|
||||
|
||||
afl-tmin: src/afl-tmin.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o $(COMM_HDR) | test_x86
|
||||
$(CC) $(CFLAGS) $(COMPILE_STATIC) $(CFLAGS_FLTO) src/$@.c src/afl-common.o src/afl-sharedmem.o src/afl-forkserver.o src/afl-performance.o -o $@ $(LDFLAGS)
|
||||
@ -546,8 +553,8 @@ ifndef AFL_NO_X86
|
||||
test_build: afl-cc afl-gcc afl-as afl-showmap
|
||||
@echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..."
|
||||
@unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_LSAN AFL_USE_ASAN AFL_USE_MSAN; ASAN_OPTIONS=detect_leaks=0 AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c $(LDFLAGS) -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 )
|
||||
ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null
|
||||
echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||
-ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -q -m none -o .test-instr0 ./test-instr < /dev/null
|
||||
-echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||
@rm -f test-instr
|
||||
@cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-cc does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi
|
||||
@echo
|
||||
@ -628,25 +635,25 @@ distrib: all
|
||||
-$(MAKE) -j$(nproc) -f GNUmakefile.llvm
|
||||
ifneq "$(SYS)" "Darwin"
|
||||
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||
endif
|
||||
-$(MAKE) -C utils/libdislocator
|
||||
-$(MAKE) -C utils/libtokencap
|
||||
endif
|
||||
-$(MAKE) -C utils/afl_network_proxy
|
||||
-$(MAKE) -C utils/socket_fuzzing
|
||||
-$(MAKE) -C utils/argv_fuzzing
|
||||
# -$(MAKE) -C utils/plot_ui
|
||||
-$(MAKE) -C frida_mode
|
||||
ifneq "$(SYS)" "Darwin"
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ifndef NO_CORESIGHT
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ifndef NO_CORESIGHT
|
||||
-$(MAKE) -C coresight_mode
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifeq "$(SYS)" "Linux"
|
||||
ifndef NO_NYX
|
||||
ifeq "$(SYS)" "Linux"
|
||||
ifndef NO_NYX
|
||||
-cd nyx_mode && ./build_nyx_support.sh
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
-cd qemu_mode && sh ./build_qemu_support.sh
|
||||
ifeq "$(ARCH)" "aarch64"
|
||||
ifndef NO_UNICORN_ARM64
|
||||
@ -659,8 +666,10 @@ endif
|
||||
|
||||
.PHONY: binary-only
|
||||
binary-only: test_shm test_python ready $(PROGS)
|
||||
ifneq "$(SYS)" "Darwin"
|
||||
-$(MAKE) -C utils/libdislocator
|
||||
-$(MAKE) -C utils/libtokencap
|
||||
endif
|
||||
-$(MAKE) -C utils/afl_network_proxy
|
||||
-$(MAKE) -C utils/socket_fuzzing
|
||||
-$(MAKE) -C utils/argv_fuzzing
|
||||
@ -717,9 +726,9 @@ source-only: all
|
||||
-$(MAKE) -j$(nproc) -f GNUmakefile.llvm
|
||||
ifneq "$(SYS)" "Darwin"
|
||||
-$(MAKE) -f GNUmakefile.gcc_plugin
|
||||
endif
|
||||
-$(MAKE) -C utils/libdislocator
|
||||
-$(MAKE) -C utils/libtokencap
|
||||
endif
|
||||
# -$(MAKE) -C utils/plot_ui
|
||||
ifeq "$(SYS)" "Linux"
|
||||
ifndef NO_NYX
|
||||
@ -730,9 +739,9 @@ endif
|
||||
@echo
|
||||
@echo Build Summary:
|
||||
@test -e afl-fuzz && echo "[+] afl-fuzz and supporting tools successfully built" || echo "[-] afl-fuzz could not be built, please set CC to a working compiler"
|
||||
@test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be build, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be build, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it"
|
||||
@test -e afl-llvm-pass.so && echo "[+] LLVM basic mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoveragePCGUARD.so && echo "[+] LLVM mode successfully built" || echo "[-] LLVM mode could not be built, please install at least llvm-11 and clang-11 or newer, see docs/INSTALL.md"
|
||||
@test -e SanitizerCoverageLTO.so && echo "[+] LLVM LTO mode successfully built" || echo "[-] LLVM LTO mode could not be built, it is optional, if you want it, please install LLVM 11-14. More information at instrumentation/README.lto.md on how to build it"
|
||||
ifneq "$(SYS)" "Darwin"
|
||||
test -e afl-gcc-pass.so && echo "[+] gcc_mode successfully built" || echo "[-] gcc_mode could not be built, it is optional, install gcc-VERSION-plugin-dev to enable this"
|
||||
endif
|
||||
@ -744,7 +753,7 @@ endif
|
||||
@echo
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > $@
|
||||
@echo .TH $* 8 $(BUILD_DATE) "AFL++" > $@
|
||||
@echo .SH NAME >> $@
|
||||
@echo .B $* >> $@
|
||||
@echo >> $@
|
||||
@ -756,8 +765,8 @@ endif
|
||||
@./$* -hh 2>&1 | tail -n +4 >> $@
|
||||
@echo >> $@
|
||||
@echo .SH AUTHOR >> $@
|
||||
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> $@
|
||||
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> $@
|
||||
@echo "AFL++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Dominik Maier <domenukk@gmail.com>, Andrea Fioraldi <andreafioraldi@gmail.com> and Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>" >> $@
|
||||
@echo The homepage of AFL++ is: https://github.com/AFLplusplus/AFLplusplus >> $@
|
||||
@echo >> $@
|
||||
@echo .SH LICENSE >> $@
|
||||
@echo Apache License Version 2.0, January 2004 >> $@
|
||||
|
@ -11,7 +11,7 @@
|
||||
# from Laszlo Szekeres.
|
||||
#
|
||||
# Copyright 2015 Google Inc. All rights reserved.
|
||||
# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
# Copyright 2019-2023 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.
|
||||
@ -28,14 +28,14 @@ MAN_PATH ?= $(PREFIX)/share/man/man8
|
||||
|
||||
VERSION = $(shell grep '^$(HASH)define VERSION ' ./config.h | cut -d '"' -f2)
|
||||
|
||||
CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
||||
CFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=1
|
||||
CFLAGS_SAFE := -Wall -Iinclude -Wno-pointer-sign \
|
||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||
-DGCC_VERSION=\"$(GCCVER)\" -DGCC_BINDIR=\"$(GCCBINDIR)\" \
|
||||
-Wno-unused-function
|
||||
override CFLAGS += $(CFLAGS_SAFE)
|
||||
|
||||
CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=2
|
||||
CXXFLAGS ?= -O3 -g -funroll-loops -D_FORTIFY_SOURCE=1
|
||||
CXXEFLAGS := $(CXXFLAGS) -Wall -std=c++11
|
||||
|
||||
CC ?= gcc
|
||||
@ -175,7 +175,7 @@ all_done: test_build
|
||||
.NOTPARALLEL: clean
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "afl++" > ./$@
|
||||
@echo .TH $* 8 `date "+%Y-%m-%d"` "AFL++" > ./$@
|
||||
@echo .SH NAME >> ./$@
|
||||
@echo .B $* >> ./$@
|
||||
@echo >> ./$@
|
||||
@ -187,8 +187,8 @@ all_done: test_build
|
||||
@./$* -h 2>&1 | tail -n +4 >> ./$@
|
||||
@echo >> ./$@
|
||||
@echo .SH AUTHOR >> ./$@
|
||||
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@
|
||||
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||
@echo "AFL++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Dominik Maier <domenukk@gmail.com>, Andrea Fioraldi <andreafioraldi@gmail.com> and Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>" >> ./$@
|
||||
@echo The homepage of AFL++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||
@echo >> ./$@
|
||||
@echo .SH LICENSE >> ./$@
|
||||
@echo Apache License Version 2.0, January 2004 >> ./$@
|
||||
|
@ -46,9 +46,10 @@ LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' | sed 's
|
||||
LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' )
|
||||
LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' )
|
||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 )
|
||||
LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[5-9]' && echo 1 || echo 0 )
|
||||
LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[7-9]' && echo 1 || echo 0 )
|
||||
LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[0-9]' && echo 1 || echo 0 )
|
||||
LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 )
|
||||
LLVM_NEWER_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[6-9]' && echo 1 || echo 0 )
|
||||
LLVM_13_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[3-9]' && echo 1 || echo 0 )
|
||||
LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | grep -E -q '^1[1-9]' && echo 1 || echo 0 )
|
||||
LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null)
|
||||
LLVM_LIBDIR = $(shell $(LLVM_CONFIG) --libdir 2>/dev/null)
|
||||
@ -81,6 +82,11 @@ ifeq "$(LLVM_NEW_API)" "1"
|
||||
LLVM_TOO_OLD=0
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_NEWER_API)" "1"
|
||||
$(info [+] llvm_mode detected llvm 16+, enabling c++17)
|
||||
LLVM_STDCXX = c++17
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_TOO_OLD)" "1"
|
||||
$(info [!] llvm_mode detected an old version of llvm, upgrade to at least 9 or preferable 11!)
|
||||
$(shell sleep 1)
|
||||
@ -89,7 +95,6 @@ endif
|
||||
ifeq "$(LLVM_HAVE_LTO)" "1"
|
||||
$(info [+] llvm_mode detected llvm 11+, enabling afl-lto LTO implementation)
|
||||
LLVM_LTO = 1
|
||||
#TEST_MMAP = 1
|
||||
endif
|
||||
|
||||
ifeq "$(LLVM_LTO)" "0"
|
||||
@ -255,26 +260,34 @@ else
|
||||
AFL_CLANG_DEBUG_PREFIX =
|
||||
endif
|
||||
|
||||
CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
||||
CFLAGS_SAFE := -Wall -g -Wno-cast-qual -Wno-variadic-macros -Wno-pointer-sign -I ./include/ -I ./instrumentation/ \
|
||||
CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=1
|
||||
CFLAGS_SAFE := -Wall -g -Wno-cast-qual -Wno-variadic-macros -Wno-pointer-sign \
|
||||
-I ./include/ -I ./instrumentation/ \
|
||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||
-DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \
|
||||
-DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \
|
||||
-Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \
|
||||
-DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \
|
||||
-DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \
|
||||
-DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \
|
||||
-DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) -Wno-unused-function \
|
||||
$(AFL_CLANG_DEBUG_PREFIX)
|
||||
-DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \
|
||||
-DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \
|
||||
-DCLANG_BIN=\"$(CLANG_BIN)\" -DCLANGPP_BIN=\"$(CLANGPP_BIN)\" -DUSE_BINDIR=$(USE_BINDIR) \
|
||||
-Wno-unused-function $(AFL_CLANG_DEBUG_PREFIX)
|
||||
ifndef LLVM_DEBUG
|
||||
CFLAGS_SAFE += -Wno-deprecated
|
||||
endif
|
||||
|
||||
ifdef CODE_COVERAGE
|
||||
override CFLAGS_SAFE += -D__AFL_CODE_COVERAGE=1
|
||||
override LDFLAGS += -ldl
|
||||
endif
|
||||
|
||||
override CFLAGS += $(CFLAGS_SAFE)
|
||||
|
||||
ifdef AFL_TRACE_PC
|
||||
$(info Compile option AFL_TRACE_PC is deprecated, just set AFL_LLVM_INSTRUMENT=PCGUARD to activate when compiling targets )
|
||||
endif
|
||||
|
||||
CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2
|
||||
CXXFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=1
|
||||
override CXXFLAGS += -Wall -g -I ./include/ \
|
||||
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros \
|
||||
-DVERSION=\"$(VERSION)\" -Wno-variadic-macros -Wno-deprecated-copy-with-dtor \
|
||||
-DLLVM_MINOR=$(LLVM_MINOR) -DLLVM_MAJOR=$(LLVM_MAJOR)
|
||||
|
||||
ifneq "$(shell $(LLVM_CONFIG) --includedir) 2> /dev/null" ""
|
||||
@ -286,6 +299,11 @@ endif
|
||||
CLANG_CPPFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fPIC $(CXXFLAGS) -Wno-deprecated-declarations
|
||||
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
|
||||
|
||||
# wasm fuzzing: disable thread-local storage and unset LLVM debug flag
|
||||
ifdef WAFL_MODE
|
||||
$(info Compiling libraries for use with WAVM)
|
||||
CLANG_CPPFL += -DNDEBUG -DNO_TLS
|
||||
endif
|
||||
|
||||
# User teor2345 reports that this is required to make things work on MacOS X.
|
||||
ifeq "$(SYS)" "Darwin"
|
||||
@ -404,7 +422,7 @@ endif
|
||||
$(CXX) $(CLANG_CPPFL) -Wdeprecated -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o
|
||||
|
||||
./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps
|
||||
ifeq "$(LLVM_10_OK)" "1"
|
||||
ifeq "$(LLVM_13_OK)" "1"
|
||||
-$(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) -Wno-deprecated-copy-dtor -Wdeprecated instrumentation/afl-llvm-common.o
|
||||
endif
|
||||
|
||||
@ -497,7 +515,7 @@ install: all
|
||||
install -m 644 instrumentation/README.*.md $${DESTDIR}$(DOC_PATH)/
|
||||
|
||||
%.8: %
|
||||
@echo .TH $* 8 $(BUILD_DATE) "afl++" > ./$@
|
||||
@echo .TH $* 8 $(BUILD_DATE) "AFL++" > ./$@
|
||||
@echo .SH NAME >> ./$@
|
||||
@printf "%s" ".B $* \- " >> ./$@
|
||||
@./$* -h 2>&1 | head -n 1 | sed -e "s/$$(printf '\e')[^m]*m//g" >> ./$@
|
||||
@ -511,8 +529,8 @@ install: all
|
||||
@./$* -h 2>&1 | tail -n +4 >> ./$@
|
||||
@echo >> ./$@
|
||||
@echo .SH AUTHOR >> ./$@
|
||||
@echo "afl++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>, Andrea Fioraldi <andreafioraldi@gmail.com> and Dominik Maier <domenukk@gmail.com>" >> ./$@
|
||||
@echo The homepage of afl++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||
@echo "AFL++ was written by Michal \"lcamtuf\" Zalewski and is maintained by Marc \"van Hauser\" Heuse <mh@mh-sec.de>, Dominik Maier <domenukk@gmail.com>, Andrea Fioraldi <andreafioraldi@gmail.com> and Heiko \"hexcoder-\" Eissfeldt <heiko.eissfeldt@hexco.de>" >> ./$@
|
||||
@echo The homepage of AFL++ is: https://github.com/AFLplusplus/AFLplusplus >> ./$@
|
||||
@echo >> ./$@
|
||||
@echo .SH LICENSE >> ./$@
|
||||
@echo Apache License Version 2.0, January 2004 >> ./$@
|
||||
|
@ -1,10 +1,10 @@
|
||||
# American Fuzzy Lop plus plus (AFL++)
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/master/static/aflpp_bg.svg" alt="AFL++ logo" width="250" heigh="250">
|
||||
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/main/static/aflpp_bg.svg" alt="AFL++ logo" width="250" heigh="250">
|
||||
|
||||
Release version: [4.04c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
Release version: [4.07c](https://github.com/AFLplusplus/AFLplusplus/releases)
|
||||
|
||||
GitHub version: 4.04a
|
||||
GitHub version: 4.07c
|
||||
|
||||
Repository:
|
||||
[https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)
|
||||
@ -12,9 +12,9 @@ Repository:
|
||||
AFL++ is maintained by:
|
||||
|
||||
* Marc "van Hauser" Heuse <mh@mh-sec.de>
|
||||
* Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>
|
||||
* Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||
* Dominik Maier <mail@dmnk.co>
|
||||
* Heiko "hexcoder-" Eißfeldt <heiko.eissfeldt@hexco.de>
|
||||
* Documentation: Jana Aydinbas <jana.aydinbas@gmail.com>
|
||||
|
||||
Originally developed by Michał "lcamtuf" Zalewski.
|
||||
@ -228,6 +228,7 @@ Thank you! (For people sending pull requests - please add yourself to this list
|
||||
Thomas Rooijakkers David Carlier
|
||||
Ruben ten Hove Joey Jiao
|
||||
fuzzah @intrigus-lgtm
|
||||
Yaakov Saxon
|
||||
```
|
||||
|
||||
</details>
|
||||
|
7
TODO.md
7
TODO.md
@ -2,18 +2,21 @@
|
||||
|
||||
## Should
|
||||
|
||||
- better documentation for custom mutators
|
||||
- afl-crash-analysis
|
||||
- show in the UI when fuzzing is "done"
|
||||
- test cmplog for less than 16bit
|
||||
- support persistent and deferred fork server in afl-showmap?
|
||||
- better autodetection of shifting runtime timeout values
|
||||
- Update afl->pending_not_fuzzed for MOpt
|
||||
- afl-plot to support multiple plot_data
|
||||
- parallel builds for source-only targets
|
||||
- get rid of check_binary, replace with more forkserver communication
|
||||
- first fuzzer should be a main automatically? not sure.
|
||||
|
||||
## Maybe
|
||||
|
||||
- forkserver tells afl-fuzz if cmplog is supported and if so enable
|
||||
it by default, with AFL_CMPLOG_NO=1 (?) set to skip?
|
||||
- afl_custom_fuzz_splice_optin()
|
||||
- afl_custom_splice()
|
||||
- cmdline option from-to range for mutations
|
||||
|
||||
|
164
afl-cmin
164
afl-cmin
@ -103,12 +103,14 @@ function usage() {
|
||||
" -o dir - output directory for minimized files\n" \
|
||||
"\n" \
|
||||
"Execution control settings:\n" \
|
||||
" -T tasks - how many parallel tasks to run (default: 1, all=nproc)\n" \
|
||||
" -f file - location read by the fuzzed program (stdin)\n" \
|
||||
" -m megs - memory limit for child process ("mem_limit" MB)\n" \
|
||||
" -t msec - run time limit for child process (default: none)\n" \
|
||||
" -t msec - run time limit for child process (default: 5000)\n" \
|
||||
" -O - use binary-only instrumentation (FRIDA mode)\n" \
|
||||
" -Q - use binary-only instrumentation (QEMU mode)\n" \
|
||||
" -U - use unicorn-based instrumentation (unicorn mode)\n" \
|
||||
" -X - use Nyx mode\n" \
|
||||
"\n" \
|
||||
"Minimization settings:\n" \
|
||||
" -A - allow crashes and timeouts (not recommended)\n" \
|
||||
@ -118,17 +120,21 @@ function usage() {
|
||||
"For additional tips, please consult README.md\n" \
|
||||
"\n" \
|
||||
"Environment variables used:\n" \
|
||||
"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" \
|
||||
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \
|
||||
"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the forkserver to come up\n" \
|
||||
"AFL_KEEP_TRACES: leave the temporary <out_dir>/.traces directory\n" \
|
||||
"AFL_KILL_SIGNAL: Signal delivered to child processes on timeout (default: SIGKILL)\n" \
|
||||
"AFL_FORK_SERVER_KILL_SIGNAL: Signal delivered to fork server processes on\n" \
|
||||
" termination (default: SIGTERM). If this is not set and AFL_KILL_SIGNAL is\n" \
|
||||
" set, this will be set to the same value as AFL_KILL_SIGNAL.\n" \
|
||||
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" \
|
||||
"AFL_CMIN_ALLOW_ANY: write tuples for crashing inputs also\n" \
|
||||
"AFL_PATH: path for the afl-showmap binary if not found anywhere in PATH\n" \
|
||||
"AFL_PRINT_FILENAMES: If set, the filename currently processed will be " \
|
||||
"printed to stdout\n" \
|
||||
"AFL_SKIP_BIN_CHECK: skip afl instrumentation checks for target binary\n"
|
||||
"AFL_CUSTOM_MUTATOR_LIBRARY: custom mutator library (post_process and send)\n"
|
||||
"AFL_PYTHON_MODULE: custom mutator library (post_process and send)\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -143,7 +149,7 @@ BEGIN {
|
||||
redirected = 0
|
||||
}
|
||||
|
||||
print "corpus minimization tool for afl++ (awk version)\n"
|
||||
print "corpus minimization tool for AFL++ (awk version)\n"
|
||||
|
||||
# defaults
|
||||
extra_par = ""
|
||||
@ -153,13 +159,19 @@ BEGIN {
|
||||
# process options
|
||||
Opterr = 1 # default is to diagnose
|
||||
Optind = 1 # skip ARGV[0]
|
||||
while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eACOQU?")) != -1) {
|
||||
while ((_go_c = getopt(ARGC, ARGV, "hi:o:f:m:t:eACOQUXYT:?")) != -1) {
|
||||
if (_go_c == "i") {
|
||||
if (!Optarg) usage()
|
||||
if (in_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
in_dir = Optarg
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "T") {
|
||||
if (!Optarg) usage()
|
||||
if (threads) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
threads = Optarg
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "o") {
|
||||
if (!Optarg) usage()
|
||||
if (out_dir) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
@ -214,6 +226,12 @@ BEGIN {
|
||||
extra_par = extra_par " -U"
|
||||
unicorn_mode = 1
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "X" || _go_c == "Y") {
|
||||
if (nyx_mode) { print "Option "_go_c" is only allowed once" > "/dev/stderr"}
|
||||
extra_par = extra_par " -X"
|
||||
nyx_mode = 1
|
||||
continue
|
||||
} else
|
||||
if (_go_c == "?") {
|
||||
exit 1
|
||||
@ -222,7 +240,7 @@ BEGIN {
|
||||
} # while options
|
||||
|
||||
if (!mem_limit) mem_limit = "none"
|
||||
if (!timeout) timeout = "none"
|
||||
if (!timeout) timeout = "5000"
|
||||
|
||||
# get program args
|
||||
i = 0
|
||||
@ -241,21 +259,30 @@ BEGIN {
|
||||
# Do a sanity check to discourage the use of /tmp, since we can't really
|
||||
# handle this safely from an awk script.
|
||||
|
||||
if (!ENVIRON["AFL_ALLOW_TMP"]) {
|
||||
dirlist[0] = in_dir
|
||||
dirlist[1] = target_bin
|
||||
dirlist[2] = out_dir
|
||||
dirlist[3] = stdin_file
|
||||
"pwd" | getline dirlist[4] # current directory
|
||||
for (dirind in dirlist) {
|
||||
dir = dirlist[dirind]
|
||||
#if (!ENVIRON["AFL_ALLOW_TMP"]) {
|
||||
# dirlist[0] = in_dir
|
||||
# dirlist[1] = target_bin
|
||||
# dirlist[2] = out_dir
|
||||
# dirlist[3] = stdin_file
|
||||
# "pwd" | getline dirlist[4] # current directory
|
||||
# for (dirind in dirlist) {
|
||||
# dir = dirlist[dirind]
|
||||
#
|
||||
# if (dir ~ /^(\/var)?\/tmp/) {
|
||||
# print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
|
||||
# exit 1
|
||||
# }
|
||||
# }
|
||||
# delete dirlist
|
||||
#}
|
||||
|
||||
if (dir ~ /^(\/var)?\/tmp/) {
|
||||
print "[-] Error: do not use this script in /tmp or /var/tmp." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
delete dirlist
|
||||
if (threads && stdin_file) {
|
||||
print "[-] Error: -T and -f cannot be used together." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (!threads && !stdin_file && !nyx_mode) {
|
||||
print "[*] Are you aware of the '-T all' parallelize option that improves the speed for large/slow corpuses?"
|
||||
}
|
||||
|
||||
# If @@ is specified, but there's no -f, let's come up with a temporary input
|
||||
@ -288,7 +315,8 @@ BEGIN {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (target_bin && !exists_and_is_executable(target_bin)) {
|
||||
|
||||
if (!nyx_mode && target_bin && !exists_and_is_executable(target_bin)) {
|
||||
|
||||
"command -v "target_bin" 2>/dev/null" | getline tnew
|
||||
if (!tnew || !exists_and_is_executable(tnew)) {
|
||||
@ -308,7 +336,7 @@ BEGIN {
|
||||
}
|
||||
}
|
||||
|
||||
if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode) {
|
||||
if (!ENVIRON["AFL_SKIP_BIN_CHECK"] && !qemu_mode && !frida_mode && !unicorn_mode && !nyx_mode) {
|
||||
if (0 != system( "grep -q __AFL_SHM_ID "target_bin )) {
|
||||
print "[-] Error: binary '"target_bin"' doesn't appear to be instrumented." > "/dev/stderr"
|
||||
exit 1
|
||||
@ -337,6 +365,18 @@ BEGIN {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (threads) {
|
||||
"nproc" | getline nproc
|
||||
if (threads == "all") {
|
||||
threads = nproc
|
||||
} else {
|
||||
if (!(threads > 1 && threads <= nproc)) {
|
||||
print "[-] Error: -T option must be between 1 and "nproc" or \"all\"." > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check for the more efficient way to copy files...
|
||||
if (0 != system("mkdir -p -m 0700 "trace_dir)) {
|
||||
print "[-] Error: Cannot create directory "trace_dir > "/dev/stderr"
|
||||
@ -446,27 +486,79 @@ BEGIN {
|
||||
# STEP 1: Collecting traces #
|
||||
#############################
|
||||
|
||||
if (threads) {
|
||||
|
||||
inputsperfile = in_count / threads
|
||||
if (in_count % threads) {
|
||||
inputsperfile++;
|
||||
}
|
||||
|
||||
cnt = 0;
|
||||
tmpfile=out_dir "/.filelist"
|
||||
for (instance = 1; instance < threads; instance++) {
|
||||
for (i = 0; i < inputsperfile; i++) {
|
||||
print in_dir"/"infilesSmallToBigFull[cnt] >> tmpfile"."instance
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
for (; cnt < in_count; cnt++) {
|
||||
print in_dir"/"infilesSmallToBigFull[cnt] >> tmpfile"."threads
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
print "[*] Obtaining traces for "in_count" input files in '"in_dir"'."
|
||||
|
||||
cur = 0;
|
||||
if (!stdin_file) {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string
|
||||
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
|
||||
} else {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null"
|
||||
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
|
||||
if (retval && (!AFL_CMIN_CRASHES_ONLY && !AFL_CMIN_ALLOW_ANY)) {
|
||||
print "[!] Exit code "retval" != 0 received from afl-showmap (this means a crashing or timeout input is likely present), terminating..."
|
||||
if (threads > 1) {
|
||||
|
||||
if (!ENVIRON["AFL_KEEP_TRACES"]) {
|
||||
system("rm -rf "trace_dir" 2>/dev/null")
|
||||
system("rmdir "out_dir)
|
||||
print "[*] Creating " threads " parallel tasks with about " inputsperfile " each."
|
||||
for (i = 1; i <= threads; i++) {
|
||||
|
||||
if (!stdin_file) {
|
||||
# print " { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -- \""target_bin"\" "prog_args_string"; > "tmpfile"."i".done ; } &"
|
||||
retval = system(" { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -- \""target_bin"\" "prog_args_string"; > "tmpfile"."i".done ; } &")
|
||||
} else {
|
||||
stdin_file=tmpfile"."i".stdin"
|
||||
# print " { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null; > "tmpfile"."i".done ; } &"
|
||||
retval = system(" { "AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -I \""tmpfile"."i"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null; > "tmpfile"."i".done ; } &")
|
||||
}
|
||||
}
|
||||
exit retval
|
||||
print "[*] Waiting for parallel tasks to complete ..."
|
||||
# wait for all processes to finish
|
||||
ok=0
|
||||
while (ok < threads) {
|
||||
ok=0
|
||||
for (i = 1; i <= threads; i++) {
|
||||
if (system("test -f "tmpfile"."i".done") == 0) {
|
||||
ok++
|
||||
}
|
||||
}
|
||||
}
|
||||
print "[*] Done!"
|
||||
system("rm -f "tmpfile"*")
|
||||
} else {
|
||||
if (!stdin_file) {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string
|
||||
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -- \""target_bin"\" "prog_args_string)
|
||||
} else {
|
||||
print " Processing "in_count" files (forkserver mode)..."
|
||||
# print AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null"
|
||||
retval = system(AFL_MAP_SIZE AFL_CMIN_ALLOW_ANY AFL_CMIN_CRASHES_ONLY"\""showmap"\" -m "mem_limit" -t "timeout" -o \""trace_dir"\" -Z "extra_par" -i \""in_dir"\" -H \""stdin_file"\" -- \""target_bin"\" "prog_args_string" </dev/null")
|
||||
}
|
||||
|
||||
if (retval && (!AFL_CMIN_CRASHES_ONLY && !AFL_CMIN_ALLOW_ANY)) {
|
||||
print "[!] Exit code "retval" != 0 received from afl-showmap (this means a crashing or timeout input is likely present), terminating..."
|
||||
|
||||
if (!ENVIRON["AFL_KEEP_TRACES"]) {
|
||||
system("rm -rf "trace_dir" 2>/dev/null")
|
||||
system("rmdir "out_dir)
|
||||
}
|
||||
exit retval
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#######################################################
|
||||
|
142
afl-cmin.bash
142
afl-cmin.bash
@ -7,6 +7,8 @@
|
||||
#
|
||||
# Copyright 2014, 2015 Google Inc. All rights reserved.
|
||||
#
|
||||
# Copyright 2019-2023 AFLplusplus
|
||||
#
|
||||
# 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:
|
||||
@ -36,7 +38,7 @@
|
||||
# array sizes.
|
||||
#
|
||||
|
||||
echo "corpus minimization tool for afl-fuzz by Michal Zalewski"
|
||||
echo "corpus minimization tool for afl-fuzz"
|
||||
echo
|
||||
|
||||
#########
|
||||
@ -46,14 +48,14 @@ echo
|
||||
# Process command-line options...
|
||||
|
||||
MEM_LIMIT=none
|
||||
TIMEOUT=none
|
||||
TIMEOUT=5000
|
||||
|
||||
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
|
||||
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
|
||||
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN F_ARG \
|
||||
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE T_ARG
|
||||
|
||||
export AFL_QUIET=1
|
||||
|
||||
while getopts "+i:o:f:m:t:eOQUACh" opt; do
|
||||
while getopts "+i:o:f:m:t:T:eOQUAChXY" opt; do
|
||||
|
||||
case "$opt" in
|
||||
|
||||
@ -69,6 +71,7 @@ while getopts "+i:o:f:m:t:eOQUACh" opt; do
|
||||
;;
|
||||
"f")
|
||||
STDIN_FILE="$OPTARG"
|
||||
F_ARG=1
|
||||
;;
|
||||
"m")
|
||||
MEM_LIMIT="$OPTARG"
|
||||
@ -94,10 +97,21 @@ while getopts "+i:o:f:m:t:eOQUACh" opt; do
|
||||
EXTRA_PAR="$EXTRA_PAR -Q"
|
||||
QEMU_MODE=1
|
||||
;;
|
||||
"Y")
|
||||
EXTRA_PAR="$EXTRA_PAR -X"
|
||||
NYX_MODE=1
|
||||
;;
|
||||
"X")
|
||||
EXTRA_PAR="$EXTRA_PAR -X"
|
||||
NYX_MODE=1
|
||||
;;
|
||||
"U")
|
||||
EXTRA_PAR="$EXTRA_PAR -U"
|
||||
UNICORN_MODE=1
|
||||
;;
|
||||
"T")
|
||||
T_ARG="$OPTARG"
|
||||
;;
|
||||
"?")
|
||||
exit 1
|
||||
;;
|
||||
@ -122,12 +136,14 @@ Required parameters:
|
||||
|
||||
Execution control settings:
|
||||
|
||||
-f file - location read by the fuzzed program (stdin)
|
||||
-m megs - memory limit for child process ($MEM_LIMIT MB)
|
||||
-t msec - run time limit for child process (none)
|
||||
-T tasks - how many parallel processes to create (default=1, "all"=nproc)
|
||||
-f file - location read by the fuzzed program (default: stdin)
|
||||
-m megs - memory limit for child process (default=$MEM_LIMIT MB)
|
||||
-t msec - run time limit for child process (default: 5000ms)
|
||||
-O - use binary-only instrumentation (FRIDA mode)
|
||||
-Q - use binary-only instrumentation (QEMU mode)
|
||||
-U - use unicorn-based instrumentation (Unicorn mode)
|
||||
-X - use Nyx mode
|
||||
|
||||
Minimization settings:
|
||||
|
||||
@ -142,6 +158,8 @@ AFL_KEEP_TRACES: leave the temporary <out_dir>\.traces directory
|
||||
AFL_NO_FORKSRV: run target via execve instead of using the forkserver
|
||||
AFL_PATH: last resort location to find the afl-showmap binary
|
||||
AFL_SKIP_BIN_CHECK: skip check for target binary
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY: custom mutator library (post_process and send)
|
||||
AFL_PYTHON_MODULE: custom mutator library (post_process and send)
|
||||
_EOF_
|
||||
exit 1
|
||||
fi
|
||||
@ -188,6 +206,11 @@ fi
|
||||
|
||||
# Check for obvious errors.
|
||||
|
||||
if [ ! "$T_ARG" = "" -a -n "$F_ARG" -a ! "$NYX_MODE" == 1 ]; then
|
||||
echo "[-] Error: -T and -f can not be used together." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! "$MEM_LIMIT" = "none" ]; then
|
||||
|
||||
if [ "$MEM_LIMIT" -lt "5" ]; then
|
||||
@ -206,20 +229,23 @@ if [ ! "$TIMEOUT" = "none" ]; then
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
|
||||
if [ "$NYX_MODE" = "" ]; then
|
||||
if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
|
||||
|
||||
TNEW="`which "$TARGET_BIN" 2>/dev/null`"
|
||||
TNEW="`which "$TARGET_BIN" 2>/dev/null`"
|
||||
|
||||
if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then
|
||||
echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_BIN="$TNEW"
|
||||
|
||||
if [ ! -f "$TNEW" -o ! -x "$TNEW" ]; then
|
||||
echo "[-] Error: binary '$TARGET_BIN' not found or not executable." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_BIN="$TNEW"
|
||||
|
||||
fi
|
||||
|
||||
grep -aq AFL_DUMP_MAP_SIZE "./$TARGET_BIN" && {
|
||||
grep -aq AFL_DUMP_MAP_SIZE "$TARGET_BIN" && {
|
||||
echo "[!] Trying to obtain the map size of the target ..."
|
||||
MAPSIZE=`AFL_DUMP_MAP_SIZE=1 "./$TARGET_BIN" 2>/dev/null`
|
||||
test -n "$MAPSIZE" && {
|
||||
@ -228,7 +254,7 @@ grep -aq AFL_DUMP_MAP_SIZE "./$TARGET_BIN" && {
|
||||
}
|
||||
}
|
||||
|
||||
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" ]; then
|
||||
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$FRIDA_MODE" = "" -a "$UNICORN_MODE" = "" -a "$NYX_MODE" = "" ]; then
|
||||
|
||||
if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
|
||||
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
|
||||
@ -285,14 +311,34 @@ if [ ! -x "$SHOWMAP" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
THREADS=
|
||||
if [ ! "$T_ARG" = "" ]; then
|
||||
if [ "$T_ARG" = "all" ]; then
|
||||
THREADS=$(nproc)
|
||||
else
|
||||
if [ "$T_ARG" -gt 1 -a "$T_ARG" -le "$(nproc)" ]; then
|
||||
THREADS=$T_ARG
|
||||
else
|
||||
echo "[-] Error: -T parameter must between 2 and $(nproc) or \"all\"." 1>&2
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ -z "$F_ARG" ]; then
|
||||
echo "[*] Are you aware of the '-T all' parallelize option that massively improves the speed?"
|
||||
fi
|
||||
fi
|
||||
|
||||
IN_COUNT=$((`ls -- "$IN_DIR" 2>/dev/null | wc -l`))
|
||||
|
||||
if [ "$IN_COUNT" = "0" ]; then
|
||||
echo "[+] Hmm, no inputs in the target directory. Nothing to be done."
|
||||
echo "[-] Hmm, no inputs in the target directory. Nothing to be done."
|
||||
rm -rf "$TRACE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Are you aware that afl-cmin is faster than this afl-cmin.bash script?"
|
||||
echo "[+] Found $IN_COUNT files for minimizing."
|
||||
|
||||
FIRST_FILE=`ls "$IN_DIR" | head -1`
|
||||
|
||||
# Make sure that we're not dealing with a directory.
|
||||
@ -341,6 +387,18 @@ else
|
||||
|
||||
fi
|
||||
|
||||
TMPFILE=$OUT_DIR/.list.$$
|
||||
if [ ! "$THREADS" = "" ]; then
|
||||
ls -- "$IN_DIR" > $TMPFILE 2>/dev/null
|
||||
IN_COUNT=$(cat $TMPFILE | wc -l)
|
||||
SPLIT=$(($IN_COUNT / $THREADS))
|
||||
if [ "$(($IN_COUNT % $THREADS))" -gt 0 ]; then
|
||||
SPLIT=$(($SPLIT + 1))
|
||||
fi
|
||||
echo "[+] Splitting workload into $THREADS tasks with $SPLIT items on average each."
|
||||
split -l $SPLIT $TMPFILE $TMPFILE.
|
||||
fi
|
||||
|
||||
# Let's roll!
|
||||
|
||||
#############################
|
||||
@ -349,6 +407,7 @@ fi
|
||||
|
||||
echo "[*] Obtaining traces for input files in '$IN_DIR'..."
|
||||
|
||||
if [ "$THREADS" = "" ]; then
|
||||
(
|
||||
|
||||
CUR=0
|
||||
@ -372,17 +431,58 @@ echo "[*] Obtaining traces for input files in '$IN_DIR'..."
|
||||
printf "\\r Processing file $CUR/$IN_COUNT... "
|
||||
|
||||
cp "$IN_DIR/$fn" "$STDIN_FILE"
|
||||
|
||||
"$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -H "$STDIN_FILE" -- "$@" </dev/null
|
||||
|
||||
done
|
||||
|
||||
|
||||
fi
|
||||
|
||||
echo
|
||||
|
||||
)
|
||||
|
||||
echo
|
||||
else
|
||||
|
||||
PIDS=
|
||||
CNT=0
|
||||
for inputs in $(ls ${TMPFILE}.*); do
|
||||
|
||||
(
|
||||
|
||||
if [ "$STDIN_FILE" = "" ]; then
|
||||
|
||||
cat $inputs | while read -r fn; do
|
||||
|
||||
"$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -- "$@" <"$IN_DIR/$fn"
|
||||
|
||||
done
|
||||
|
||||
else
|
||||
|
||||
STDIN_FILE="$inputs.$$"
|
||||
cat $inputs | while read -r fn; do
|
||||
|
||||
cp "$IN_DIR/$fn" "$STDIN_FILE"
|
||||
"$SHOWMAP" -m "$MEM_LIMIT" -t "$TIMEOUT" -o "$TRACE_DIR/$fn" -Z $EXTRA_PAR -H "$STDIN_FILE" -- "$@" </dev/null
|
||||
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
) &
|
||||
|
||||
PIDS="$PIDS $!"
|
||||
done
|
||||
|
||||
echo "[+] Waiting for running tasks IDs:$PIDS"
|
||||
wait
|
||||
echo "[+] all $THREADS running tasks completed."
|
||||
rm -f ${TMPFILE}*
|
||||
|
||||
echo trace dir files: $(ls $TRACE_DIR/*|wc -l)
|
||||
|
||||
fi
|
||||
|
||||
|
||||
##########################
|
||||
# STEP 2: SORTING TUPLES #
|
||||
|
4
afl-plot
4
afl-plot
@ -287,9 +287,9 @@ $PLOT_EG
|
||||
|
||||
_EOF_
|
||||
|
||||
) | gnuplot
|
||||
) | gnuplot || echo "Note: if you see errors concerning 'unknown or ambiguous terminal type' then you need to use a gnuplot that has png support compiled in."
|
||||
|
||||
echo "[?] You can also use -g flag to view the plots in an GUI window, and interact with the plots (if you have built afl-plot-ui). Run \"afl-plot-h\" to know more."
|
||||
echo "[?] You can also use -g flag to view the plots in an GUI window, and interact with the plots (if you have built afl-plot-ui). Run \"afl-plot -h\" to know more."
|
||||
|
||||
fi
|
||||
|
||||
|
@ -110,7 +110,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then
|
||||
sysctl kern.sysv.shmall=131072000
|
||||
echo Settings applied.
|
||||
echo
|
||||
if [ $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ] ; then
|
||||
if $(launchctl list 2>/dev/null | grep -q '\.ReportCrash$') ; then
|
||||
echo
|
||||
echo Unloading the default crash reporter
|
||||
SL=/System/Library; PL=com.apple.ReportCrash
|
||||
@ -119,6 +119,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then
|
||||
echo
|
||||
fi
|
||||
echo It is recommended to disable System Integrity Protection for increased performance.
|
||||
echo See: https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection
|
||||
echo
|
||||
DONE=1
|
||||
fi
|
||||
|
18
afl-whatsup
18
afl-whatsup
@ -6,7 +6,7 @@
|
||||
# Originally written by Michal Zalewski
|
||||
#
|
||||
# Copyright 2015 Google Inc. All rights reserved.
|
||||
# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
# Copyright 2019-2023 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.
|
||||
@ -70,10 +70,10 @@ if [ -d queue ]; then
|
||||
|
||||
fi
|
||||
|
||||
RED=`tput setaf 9 1 1`
|
||||
GREEN=`tput setaf 2 1 1`
|
||||
BLUE=`tput setaf 4 1 1`
|
||||
YELLOW=`tput setaf 11 1 1`
|
||||
RED=`tput setaf 9 1 1 2>/dev/null`
|
||||
GREEN=`tput setaf 2 1 1 2>/dev/null`
|
||||
BLUE=`tput setaf 4 1 1 2>/dev/null`
|
||||
YELLOW=`tput setaf 11 1 1 2>/dev/null`
|
||||
NC=`tput sgr0`
|
||||
RESET="$NC"
|
||||
|
||||
@ -88,6 +88,7 @@ TOTAL_TIME=0
|
||||
TOTAL_EXECS=0
|
||||
TOTAL_EPS=0
|
||||
TOTAL_CRASHES=0
|
||||
TOTAL_HANGS=0
|
||||
TOTAL_PFAV=0
|
||||
TOTAL_PENDING=0
|
||||
|
||||
@ -141,7 +142,8 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
|
||||
|
||||
sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP"
|
||||
. "$TMP"
|
||||
|
||||
DIR=$(dirname "$i")
|
||||
DIR=${DIR##*/}
|
||||
RUN_UNIX=$run_time
|
||||
RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24))
|
||||
RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24))
|
||||
@ -154,7 +156,7 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
|
||||
|
||||
if [ "$SUMMARY_ONLY" = "" ]; then
|
||||
|
||||
echo ">>> $afl_banner ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<"
|
||||
echo ">>> $afl_banner instance: $DIR ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<"
|
||||
echo
|
||||
|
||||
fi
|
||||
@ -189,6 +191,7 @@ for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do
|
||||
TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC))
|
||||
TOTAL_EXECS=$((TOTAL_EXECS + execs_done))
|
||||
TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes))
|
||||
TOTAL_HANGS=$((TOTAL_HANGS + saved_hangs))
|
||||
TOTAL_PENDING=$((TOTAL_PENDING + pending_total))
|
||||
TOTAL_PFAV=$((TOTAL_PFAV + pending_favs))
|
||||
|
||||
@ -300,6 +303,7 @@ if [ "$ALIVE_CNT" -gt "1" ]; then
|
||||
fi
|
||||
|
||||
echo " Crashes saved : $TOTAL_CRASHES"
|
||||
echo " Hangs saved : $TOTAL_HANGS"
|
||||
echo "Cycles without finds : $TOTAL_WCOP"
|
||||
echo " Time without finds : $TOTAL_LAST_FIND"
|
||||
echo
|
||||
|
@ -11,19 +11,6 @@ The `./examples` folder contains examples for custom mutators in python and C.
|
||||
|
||||
In `./rust`, you will find rust bindings, including a simple example in `./rust/example` and an example for structured fuzzing, based on lain, in`./rust/example_lain`.
|
||||
|
||||
## The AFL++ Grammar Mutator
|
||||
|
||||
If you use git to clone AFL++, then the following will incorporate our
|
||||
excellent grammar custom mutator:
|
||||
|
||||
```sh
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Read the README in the [Grammar-Mutator] repository on how to use it.
|
||||
|
||||
[Grammar-Mutator]: https://github.com/AFLplusplus/Grammar-Mutator
|
||||
|
||||
## Production-Ready Custom Mutators
|
||||
|
||||
This directory holds ready to use custom mutators.
|
||||
@ -37,6 +24,42 @@ and add `AFL_CUSTOM_MUTATOR_ONLY=1` if you only want to use the custom mutator.
|
||||
|
||||
Multiple custom mutators can be used by separating their paths with `:` in the environment variable.
|
||||
|
||||
### The AFL++ grammar agnostic grammar mutator
|
||||
|
||||
In `./autotokens` you find a token-level fuzzer that does not need to know
|
||||
anything about the grammar of an input as long as it is in ascii and allows
|
||||
whitespace.
|
||||
It is very fast and effective.
|
||||
|
||||
If you are looking for an example of how to effectively create a custom
|
||||
mutator take a look at this one.
|
||||
|
||||
### The AFL++ Grammar Mutator
|
||||
|
||||
If you use git to clone AFL++, then the following will incorporate our
|
||||
excellent grammar custom mutator:
|
||||
|
||||
```sh
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Read the README in the [Grammar-Mutator] repository on how to use it.
|
||||
|
||||
[Grammar-Mutator]: https://github.com/AFLplusplus/Grammar-Mutator
|
||||
|
||||
Note that this custom mutator is not very good though!
|
||||
|
||||
### Other Mutators
|
||||
|
||||
atnwalk and gramatron are grammar custom mutators. Example grammars are
|
||||
provided.
|
||||
|
||||
honggfuzz, libfuzzer and libafl are partial implementations based on the
|
||||
mutator implementations of the respective fuzzers.
|
||||
More for playing than serious usage.
|
||||
|
||||
radamsa is slow and not very good.
|
||||
|
||||
## 3rd Party Custom Mutators
|
||||
|
||||
### Superion Mutators
|
||||
|
22
custom_mutators/aflpp_tritondse/README.md
Normal file
22
custom_mutators/aflpp_tritondse/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# An AFL++ custom mutator using TritonDSE
|
||||
|
||||
## Installing the requirements
|
||||
|
||||
`pip3 install tritondse`
|
||||
|
||||
## How to run with an example
|
||||
|
||||
```
|
||||
../../afl-cc -o ../../test-instr ../../test-instr.c
|
||||
mkdir -p in
|
||||
echo aaaa > in/in
|
||||
AFL_DISABLE_TRIM=1 AFL_CUSTOM_MUTATOR_ONLY=1 AFL_SYNC_TIME=1 AFL_PYTHON_MODULE=aflpp_tritondse PYTHONPATH=. ../../afl-fuzz -i in -o out -- ../../test-instr
|
||||
```
|
||||
|
||||
Note that this custom mutator works differently, new finds are synced
|
||||
after 10-60 seconds to the fuzzing instance. This is necessary because only
|
||||
C/C++ custom mutators have access to the internal AFL++ state.
|
||||
|
||||
Note that you should run first with `AFL_DEBUG` for 5-10 minutes and see if
|
||||
all important libraries and syscalls are hooked (look at `WARNING` and `CRITICAL`
|
||||
output during the run, best use with `AFL_NO_UI=1`)
|
220
custom_mutators/aflpp_tritondse/aflpp_tritondse.py
Normal file
220
custom_mutators/aflpp_tritondse/aflpp_tritondse.py
Normal file
@ -0,0 +1,220 @@
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import hashlib
|
||||
|
||||
from tritondse import CleLoader
|
||||
from tritondse import CompositeData
|
||||
from tritondse import Config
|
||||
from tritondse import CoverageStrategy
|
||||
from tritondse import ProcessState
|
||||
from tritondse import Program
|
||||
from tritondse import Seed
|
||||
from tritondse import SeedFormat
|
||||
from tritondse import SymbolicExecutor
|
||||
from tritondse import SymbolicExplorator
|
||||
|
||||
is_debug = False
|
||||
out_path = ""
|
||||
input_file = None
|
||||
prog = None
|
||||
config = None
|
||||
dse = None
|
||||
cycle = 0
|
||||
count = 0
|
||||
finding = 0
|
||||
hashes = set()
|
||||
format = SeedFormat.RAW
|
||||
|
||||
def pre_exec_hook(se: SymbolicExecutor, state: ProcessState):
|
||||
global count
|
||||
global hashes
|
||||
global finding
|
||||
if se.seed.hash not in hashes:
|
||||
hashes.add(se.seed.hash)
|
||||
finding = 1
|
||||
filename = out_path + "/id:" + f"{count:06}" + "," + se.seed.hash
|
||||
if not os.path.exists(filename):
|
||||
if is_debug:
|
||||
print('Creating queue input ' + filename)
|
||||
with open(filename, 'wb') as file:
|
||||
if input_file:
|
||||
file.write(se.seed.content.files[input_file])
|
||||
else:
|
||||
file.write(se.seed.content)
|
||||
count += 1
|
||||
#if input_file:
|
||||
# if is_debug:
|
||||
# print('Writing to ' + input_file + ' the content: ' + str(se.seed.content))
|
||||
# with open(input_file, 'wb') as file:
|
||||
# file.write(se.seed.content)
|
||||
|
||||
|
||||
#def rtn_open(se: SymbolicExecutor, pstate: ProcessState, pc):
|
||||
# """
|
||||
# The open behavior.
|
||||
# """
|
||||
# logging.debug('open hooked')
|
||||
#
|
||||
# # Get arguments
|
||||
# arg0 = pstate.get_argument_value(0) # const char *pathname
|
||||
# flags = pstate.get_argument_value(1) # int flags
|
||||
# mode = pstate.get_argument_value(2) # int mode
|
||||
# arg0s = pstate.memory.read_string(arg0)
|
||||
#
|
||||
# # Concretize the whole path name
|
||||
# pstate.concretize_memory_bytes(arg0, len(arg0s)+1) # Concretize the whole string + \0
|
||||
#
|
||||
# # We use flags as concrete value
|
||||
# pstate.concretize_argument(1)
|
||||
#
|
||||
# # Use the flags to open the file in the write mode.
|
||||
# mode = ""
|
||||
# if (flags & 0xFF) == 0x00: # O_RDONLY
|
||||
# mode = "r"
|
||||
# elif (flags & 0xFF) == 0x01: # O_WRONLY
|
||||
# mode = "w"
|
||||
# elif (flags & 0xFF) == 0x02: # O_RDWR
|
||||
# mode = "r+"
|
||||
#
|
||||
# if (flags & 0x0100): # O_CREAT
|
||||
# mode += "x"
|
||||
# if (flags & 0x0200): # O_APPEND
|
||||
# mode = "a" # replace completely value
|
||||
#
|
||||
# if se.seed.is_file_defined(arg0s) and "r" in mode: # input file and opened in reading
|
||||
# logging.info(f"opening an input file: {arg0s}")
|
||||
# # Program is opening an input
|
||||
# data = se.seed.get_file_input(arg0s)
|
||||
# filedesc = pstate.create_file_descriptor(arg0s, io.BytesIO(data))
|
||||
# fd = filedesc.id
|
||||
# else:
|
||||
# # Try to open it as a regular file
|
||||
# try:
|
||||
# fd = open(arg0s, mode) # use the mode here
|
||||
# filedesc = pstate.create_file_descriptor(arg0s, fd)
|
||||
# fd = filedesc.id
|
||||
# except Exception as e:
|
||||
# logging.debug(f"Failed to open {arg0s} {e}")
|
||||
# fd = pstate.minus_one
|
||||
#
|
||||
# pstate.write_register("rax", fd) # write the return value
|
||||
# pstate.cpu.program_counter = pstate.pop_stack_value() # pop the return value
|
||||
# se.skip_instruction() # skip the current instruction so that the engine go straight fetching the next instruction
|
||||
|
||||
|
||||
def init(seed):
|
||||
global config
|
||||
global dse
|
||||
global format
|
||||
global input_file
|
||||
global is_debug
|
||||
global out_path
|
||||
global prog
|
||||
# Load the program (LIEF-based program loader).
|
||||
prog = CleLoader(os.environ['AFL_CUSTOM_INFO_PROGRAM'])
|
||||
# Process other configuration environment variables.
|
||||
argv = None
|
||||
try:
|
||||
foo = os.environ['AFL_DEBUG']
|
||||
is_debug = True
|
||||
except KeyError:
|
||||
pass
|
||||
if is_debug:
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
else:
|
||||
logging.basicConfig(level=logging.CRITICAL)
|
||||
try:
|
||||
foo = os.environ['AFL_CUSTOM_INFO_OUT']
|
||||
out_path = foo + '/../tritondse/queue'
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
foo = os.environ['AFL_CUSTOM_INFO_PROGRAM_INPUT']
|
||||
input_file = foo
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
argv_list = os.environ['AFL_CUSTOM_INFO_PROGRAM_ARGV']
|
||||
argv_tmp = [ os.environ['AFL_CUSTOM_INFO_PROGRAM'] ]
|
||||
argv_tmp += argv_list.split()
|
||||
argv = []
|
||||
# now check for @@
|
||||
for item in argv_tmp:
|
||||
if "@@" in item:
|
||||
input_file = out_path + '/../.input'
|
||||
argv.append(input_file)
|
||||
else:
|
||||
argv.append(item)
|
||||
except KeyError:
|
||||
pass
|
||||
# Create the output directory
|
||||
os.makedirs(out_path, exist_ok=True)
|
||||
# Debug
|
||||
if is_debug:
|
||||
print('DEBUG target: ' + os.environ['AFL_CUSTOM_INFO_PROGRAM'])
|
||||
if argv:
|
||||
print('DEBUG argv: ')
|
||||
print(argv)
|
||||
if input_file:
|
||||
print('DEBUG input_file: ' + input_file)
|
||||
print('DEBUG out_path: ' + out_path)
|
||||
print('')
|
||||
if input_file:
|
||||
format = SeedFormat.COMPOSITE
|
||||
# Now set up TritonDSE
|
||||
config = Config(coverage_strategy = CoverageStrategy.PATH,
|
||||
debug = is_debug,
|
||||
pipe_stdout = is_debug,
|
||||
pipe_stderr = is_debug,
|
||||
execution_timeout = 1,
|
||||
program_argv = argv,
|
||||
smt_timeout= 50,
|
||||
seed_format = format)
|
||||
# Create an instance of the Symbolic Explorator
|
||||
dse = SymbolicExplorator(config, prog)
|
||||
# Add callbacks.
|
||||
dse.callback_manager.register_pre_execution_callback(pre_exec_hook)
|
||||
#dse.callback_manager.register_function_callback("open", rtn_open)
|
||||
|
||||
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
global finding
|
||||
finding = 1
|
||||
while finding == 1:
|
||||
finding = 0
|
||||
dse.step()
|
||||
return b""
|
||||
|
||||
|
||||
def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
global cycle
|
||||
global dse
|
||||
# Add seed to the worklist.
|
||||
with open(filename_new_queue, "rb") as file:
|
||||
data = file.read()
|
||||
hash = hashlib.md5(data).hexdigest()
|
||||
if hash not in hashes:
|
||||
hashes.add(hash)
|
||||
if is_debug:
|
||||
print("NEW FILE " + filename_new_queue + " hash " + hash + " count " + str(cycle))
|
||||
cycle += 1
|
||||
if input_file:
|
||||
seed = Seed(CompositeData(files={"stdin": b"", # nothing on stdin
|
||||
input_file: data}))
|
||||
else:
|
||||
seed = Seed(data)
|
||||
dse.add_input_seed(seed)
|
||||
# Start exploration!
|
||||
#dse.step()
|
||||
#dse.explore()
|
||||
pass
|
||||
|
||||
|
||||
# we simulate just doing one single fuzz in the custom mutator
|
||||
def fuzz_count(buf):
|
||||
return 1
|
||||
|
||||
|
||||
def splice_optout():
|
||||
pass
|
7
custom_mutators/atnwalk/Makefile
Normal file
7
custom_mutators/atnwalk/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
all: atnwalk.so
|
||||
|
||||
atnwalk.so: atnwalk.c
|
||||
$(CC) -I ../../include/ -shared -fPIC -O3 -o atnwalk.so atnwalk.c
|
||||
|
||||
clean:
|
||||
rm -f *.so *.o *~ core
|
43
custom_mutators/atnwalk/README.md
Normal file
43
custom_mutators/atnwalk/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# ATNwalk: Grammar-Based Fuzzing using Only Bit-Mutations
|
||||
|
||||
This is a custom mutator integration of ATNwalk that works by communicating via UNIX domain sockets.
|
||||
|
||||
Refer to [https://github.com/atnwalk/testbed](https://github.com/atnwalk/testbed) for detailed instructions on how to get ATNwalk running.
|
||||
|
||||
## Build
|
||||
|
||||
Just type `make` to build `atnwalk.so`.
|
||||
|
||||
## Run
|
||||
|
||||
**NOTE:** The commands below just demonstrate an example how running ATNwalk looks like and require a working [testbed](https://github.com/atnwalk/testbed)
|
||||
|
||||
```bash
|
||||
# create the required a random seed first
|
||||
mkdir -p ~/campaign/example/seeds
|
||||
cd ~/campaign/example/seeds
|
||||
head -c1 /dev/urandom | ~/atnwalk/build/javascript/bin/decode -wb > seed.decoded 2> seed.encoded
|
||||
|
||||
# create the required atnwalk directory and copy the seed
|
||||
cd ../
|
||||
mkdir -p atnwalk/in
|
||||
cp ./seeds/seed.encoded atnwalk/in/seed
|
||||
cd atnwalk
|
||||
|
||||
# assign to a single core when benchmarking it, change the CPU number as required
|
||||
CPU_ID=0
|
||||
|
||||
# start the ATNwalk server
|
||||
nohup taskset -c ${CPU_ID} ${HOME}/atnwalk/build/javascript/bin/server 100 > server.log 2>&1 &
|
||||
|
||||
# start AFL++ with ATNwalk
|
||||
AFL_SKIP_CPUFREQ=1 \
|
||||
AFL_DISABLE_TRIM=1 \
|
||||
AFL_CUSTOM_MUTATOR_ONLY=1 \
|
||||
AFL_CUSTOM_MUTATOR_LIBRARY=${HOME}/AFLplusplus/custom_mutators/atnwalk/atnwalk.so \
|
||||
AFL_POST_PROCESS_KEEP_ORIGINAL=1 \
|
||||
~/AFLplusplus/afl-fuzz -t 100 -i in/ -o out -b ${CPU_ID} -- ~/jerryscript/build/bin/jerry
|
||||
|
||||
# make sure to kill the ATNwalk server process after you're done
|
||||
kill "$(cat atnwalk.pid)"
|
||||
```
|
539
custom_mutators/atnwalk/atnwalk.c
Normal file
539
custom_mutators/atnwalk/atnwalk.c
Normal file
@ -0,0 +1,539 @@
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BUF_SIZE_INIT 4096
|
||||
#define SOCKET_NAME "./atnwalk.socket"
|
||||
|
||||
// how many errors (e.g. timeouts) to tolerate until moving on to the next queue
|
||||
// entry
|
||||
#define ATNWALK_ERRORS_MAX 1
|
||||
|
||||
// how many execution timeouts to tolerate until moving on to the next queue
|
||||
// entry
|
||||
#define EXEC_TIMEOUT_MAX 2
|
||||
|
||||
// handshake constants
|
||||
const uint8_t SERVER_ARE_YOU_ALIVE = 213;
|
||||
const uint8_t SERVER_YES_I_AM_ALIVE = 42;
|
||||
|
||||
// control bits
|
||||
const uint8_t SERVER_CROSSOVER_BIT = 0b00000001;
|
||||
const uint8_t SERVER_MUTATE_BIT = 0b00000010;
|
||||
const uint8_t SERVER_DECODE_BIT = 0b00000100;
|
||||
const uint8_t SERVER_ENCODE_BIT = 0b00001000;
|
||||
|
||||
typedef struct atnwalk_mutator {
|
||||
|
||||
afl_state_t *afl;
|
||||
uint8_t atnwalk_error_count;
|
||||
uint64_t prev_timeouts;
|
||||
uint32_t prev_hits;
|
||||
uint32_t stage_havoc_cur;
|
||||
uint32_t stage_havoc_max;
|
||||
uint32_t stage_splice_cur;
|
||||
uint32_t stage_splice_max;
|
||||
uint8_t *fuzz_buf;
|
||||
size_t fuzz_size;
|
||||
uint8_t *post_process_buf;
|
||||
size_t post_process_size;
|
||||
|
||||
} atnwalk_mutator_t;
|
||||
|
||||
int read_all(int fd, uint8_t *buf, size_t buf_size) {
|
||||
|
||||
int n;
|
||||
size_t offset = 0;
|
||||
while (offset < buf_size) {
|
||||
|
||||
n = read(fd, buf + offset, buf_size - offset);
|
||||
if (n == -1) { return 0; }
|
||||
offset += n;
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int write_all(int fd, uint8_t *buf, size_t buf_size) {
|
||||
|
||||
int n;
|
||||
size_t offset = 0;
|
||||
while (offset < buf_size) {
|
||||
|
||||
n = write(fd, buf + offset, buf_size - offset);
|
||||
if (n == -1) { return 0; }
|
||||
offset += n;
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
void put_uint32(uint8_t *buf, uint32_t val) {
|
||||
|
||||
buf[0] = (uint8_t)(val >> 24);
|
||||
buf[1] = (uint8_t)((val & 0x00ff0000) >> 16);
|
||||
buf[2] = (uint8_t)((val & 0x0000ff00) >> 8);
|
||||
buf[3] = (uint8_t)(val & 0x000000ff);
|
||||
|
||||
}
|
||||
|
||||
uint32_t to_uint32(uint8_t *buf) {
|
||||
|
||||
uint32_t val = 0;
|
||||
val |= (((uint32_t)buf[0]) << 24);
|
||||
val |= (((uint32_t)buf[1]) << 16);
|
||||
val |= (((uint32_t)buf[2]) << 8);
|
||||
val |= ((uint32_t)buf[3]);
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
void put_uint64(uint8_t *buf, uint64_t val) {
|
||||
|
||||
buf[0] = (uint8_t)(val >> 56);
|
||||
buf[1] = (uint8_t)((val & 0x00ff000000000000) >> 48);
|
||||
buf[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40);
|
||||
buf[3] = (uint8_t)((val & 0x000000ff00000000) >> 32);
|
||||
buf[4] = (uint8_t)((val & 0x00000000ff000000) >> 24);
|
||||
buf[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16);
|
||||
buf[6] = (uint8_t)((val & 0x000000000000ff00) >> 8);
|
||||
buf[7] = (uint8_t)(val & 0x00000000000000ff);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this custom mutator
|
||||
*
|
||||
* @param[in] afl a pointer to the internal state object. Can be ignored for
|
||||
* now.
|
||||
* @param[in] seed A seed for this mutator - the same seed should always mutate
|
||||
* in the same way.
|
||||
* @return Pointer to the data object this custom mutator instance should use.
|
||||
* There may be multiple instances of this mutator in one afl-fuzz run!
|
||||
* Return NULL on error.
|
||||
*/
|
||||
atnwalk_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
srand(seed);
|
||||
atnwalk_mutator_t *data =
|
||||
(atnwalk_mutator_t *)malloc(sizeof(atnwalk_mutator_t));
|
||||
if (!data) {
|
||||
|
||||
perror("afl_custom_init alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->afl = afl;
|
||||
data->prev_hits = 0;
|
||||
data->fuzz_buf = (uint8_t *)malloc(BUF_SIZE_INIT);
|
||||
data->fuzz_size = BUF_SIZE_INIT;
|
||||
data->post_process_buf = (uint8_t *)malloc(BUF_SIZE_INIT);
|
||||
data->post_process_size = BUF_SIZE_INIT;
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
unsigned int afl_custom_fuzz_count(atnwalk_mutator_t *data,
|
||||
const unsigned char *buf, size_t buf_size) {
|
||||
|
||||
// afl_custom_fuzz_count is called exactly once before entering the
|
||||
// 'stage-loop' for the current queue entry thus, we use it to reset the error
|
||||
// count and to initialize stage variables (somewhat not intended by the API,
|
||||
// but still better than rewriting the whole thing to have a custom mutator
|
||||
// stage)
|
||||
data->atnwalk_error_count = 0;
|
||||
data->prev_timeouts = data->afl->total_tmouts;
|
||||
|
||||
// it might happen that on the last execution of the splice stage a new path
|
||||
// is found we need to fix that here and count it
|
||||
if (data->prev_hits) {
|
||||
|
||||
data->afl->stage_finds[STAGE_SPLICE] +=
|
||||
data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
|
||||
|
||||
}
|
||||
|
||||
data->prev_hits = data->afl->queued_items + data->afl->saved_crashes;
|
||||
data->stage_havoc_cur = 0;
|
||||
data->stage_splice_cur = 0;
|
||||
|
||||
// 50% havoc, 50% splice
|
||||
data->stage_havoc_max = data->afl->stage_max >> 1;
|
||||
if (data->stage_havoc_max < HAVOC_MIN) { data->stage_havoc_max = HAVOC_MIN; }
|
||||
data->stage_splice_max = data->stage_havoc_max;
|
||||
return data->stage_havoc_max + data->stage_splice_max;
|
||||
|
||||
}
|
||||
|
||||
size_t fail_fatal(int fd_socket, uint8_t **out_buf) {
|
||||
|
||||
if (fd_socket != -1) { close(fd_socket); }
|
||||
*out_buf = NULL;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
size_t fail_gracefully(int fd_socket, atnwalk_mutator_t *data, uint8_t *buf,
|
||||
size_t buf_size, uint8_t **out_buf) {
|
||||
|
||||
if (fd_socket != -1) { close(fd_socket); }
|
||||
data->atnwalk_error_count++;
|
||||
if (data->atnwalk_error_count > ATNWALK_ERRORS_MAX) {
|
||||
|
||||
data->afl->stage_max = data->afl->stage_cur;
|
||||
|
||||
}
|
||||
|
||||
*out_buf = buf;
|
||||
return buf_size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform custom mutations on a given input
|
||||
*
|
||||
* (Optional for now. Required in the future)
|
||||
*
|
||||
* @param[in] data pointer returned in afl_custom_init for this fuzz case
|
||||
* @param[in] buf Pointer to input data to be mutated
|
||||
* @param[in] buf_size Size of input data
|
||||
* @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on
|
||||
* error.
|
||||
* @param[in] add_buf Buffer containing the additional test case
|
||||
* @param[in] add_buf_size Size of the additional test case
|
||||
* @param[in] max_size Maximum size of the mutated output. The mutation must not
|
||||
* produce data larger than max_size.
|
||||
* @return Size of the mutated output.
|
||||
*/
|
||||
size_t afl_custom_fuzz(atnwalk_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
uint8_t **out_buf, uint8_t *add_buf, size_t add_buf_size,
|
||||
size_t max_size) {
|
||||
|
||||
struct sockaddr_un addr;
|
||||
int fd_socket;
|
||||
uint8_t ctrl_buf[8];
|
||||
uint8_t wanted;
|
||||
|
||||
// let's display what's going on in a nice way
|
||||
if (data->stage_havoc_cur == 0) {
|
||||
|
||||
data->afl->stage_name = (uint8_t *)"atnwalk - havoc";
|
||||
|
||||
}
|
||||
|
||||
if (data->stage_havoc_cur == data->stage_havoc_max) {
|
||||
|
||||
data->afl->stage_name = (uint8_t *)"atnwalk - splice";
|
||||
|
||||
}
|
||||
|
||||
// increase the respective havoc or splice counters
|
||||
if (data->stage_havoc_cur < data->stage_havoc_max) {
|
||||
|
||||
data->stage_havoc_cur++;
|
||||
data->afl->stage_cycles[STAGE_HAVOC]++;
|
||||
|
||||
} else {
|
||||
|
||||
// if there is nothing to splice, continue with havoc and skip splicing this
|
||||
// time
|
||||
if (data->afl->ready_for_splicing_count < 1) {
|
||||
|
||||
data->stage_havoc_max = data->afl->stage_max;
|
||||
data->stage_havoc_cur++;
|
||||
data->afl->stage_cycles[STAGE_HAVOC]++;
|
||||
|
||||
} else {
|
||||
|
||||
data->stage_splice_cur++;
|
||||
data->afl->stage_cycles[STAGE_SPLICE]++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// keep track of found new corpus seeds per stage
|
||||
if (data->afl->queued_items + data->afl->saved_crashes > data->prev_hits) {
|
||||
|
||||
if (data->stage_splice_cur <= 1) {
|
||||
|
||||
data->afl->stage_finds[STAGE_HAVOC] +=
|
||||
data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
|
||||
|
||||
} else {
|
||||
|
||||
data->afl->stage_finds[STAGE_SPLICE] +=
|
||||
data->afl->queued_items + data->afl->saved_crashes - data->prev_hits;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data->prev_hits = data->afl->queued_items + data->afl->saved_crashes;
|
||||
|
||||
// check whether this input produces a lot of timeouts, if it does then
|
||||
// abandon this queue entry
|
||||
if (data->afl->total_tmouts - data->prev_timeouts >= EXEC_TIMEOUT_MAX) {
|
||||
|
||||
data->afl->stage_max = data->afl->stage_cur;
|
||||
return fail_gracefully(-1, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// initialize the socket
|
||||
fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd_socket == -1) { return fail_fatal(fd_socket, out_buf); }
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
|
||||
if (connect(fd_socket, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// ask whether the server is alive
|
||||
ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
|
||||
if (!write_all(fd_socket, ctrl_buf, 1)) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// see whether the server replies as expected
|
||||
if (!read_all(fd_socket, ctrl_buf, 1) ||
|
||||
ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// tell the server what we want to do
|
||||
wanted = SERVER_MUTATE_BIT | SERVER_ENCODE_BIT;
|
||||
|
||||
// perform a crossover if we are splicing
|
||||
if (data->stage_splice_cur > 0) { wanted |= SERVER_CROSSOVER_BIT; }
|
||||
|
||||
// tell the server what we want and how much data will be sent
|
||||
ctrl_buf[0] = wanted;
|
||||
put_uint32(ctrl_buf + 1, (uint32_t)buf_size);
|
||||
if (!write_all(fd_socket, ctrl_buf, 5)) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// send the data to mutate and encode
|
||||
if (!write_all(fd_socket, buf, buf_size)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
if (wanted & SERVER_CROSSOVER_BIT) {
|
||||
|
||||
// since we requested crossover, we will first tell how much additional data
|
||||
// is to be expected
|
||||
put_uint32(ctrl_buf, (uint32_t)add_buf_size);
|
||||
if (!write_all(fd_socket, ctrl_buf, 4)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// send the additional data for crossover
|
||||
if (!write_all(fd_socket, add_buf, add_buf_size)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// lastly, a seed is required for crossover so send one
|
||||
put_uint64(ctrl_buf, (uint64_t)rand());
|
||||
if (!write_all(fd_socket, ctrl_buf, 8)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// since we requested mutation, we need to provide a seed for that
|
||||
put_uint64(ctrl_buf, (uint64_t)rand());
|
||||
if (!write_all(fd_socket, ctrl_buf, 8)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// obtain the required buffer size for the data that will be returned
|
||||
if (!read_all(fd_socket, ctrl_buf, 4)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
size_t new_size = (size_t)to_uint32(ctrl_buf);
|
||||
|
||||
// if the data is too large then we ignore this round
|
||||
if (new_size > max_size) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
if (new_size > buf_size) {
|
||||
|
||||
// buf is too small, need to use data->fuzz_buf, let's see whether we need
|
||||
// to reallocate
|
||||
if (new_size > data->fuzz_size) {
|
||||
|
||||
data->fuzz_size = new_size << 1;
|
||||
data->fuzz_buf = (uint8_t *)realloc(data->fuzz_buf, data->fuzz_size);
|
||||
|
||||
}
|
||||
|
||||
*out_buf = data->fuzz_buf;
|
||||
|
||||
} else {
|
||||
|
||||
// new_size fits into buf, so re-use it
|
||||
*out_buf = buf;
|
||||
|
||||
}
|
||||
|
||||
// obtain the encoded data
|
||||
if (!read_all(fd_socket, *out_buf, new_size)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
close(fd_socket);
|
||||
return new_size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A post-processing function to use right before AFL writes the test case to
|
||||
* disk in order to execute the target.
|
||||
*
|
||||
* (Optional) If this functionality is not needed, simply don't define this
|
||||
* function.
|
||||
*
|
||||
* @param[in] data pointer returned in afl_custom_init for this fuzz case
|
||||
* @param[in] buf Buffer containing the test case to be executed
|
||||
* @param[in] buf_size Size of the test case
|
||||
* @param[out] out_buf Pointer to the buffer containing the test case after
|
||||
* processing. External library should allocate memory for out_buf.
|
||||
* The buf pointer may be reused (up to the given buf_size);
|
||||
* @return Size of the output buffer after processing or the needed amount.
|
||||
* A return of 0 indicates an error.
|
||||
*/
|
||||
size_t afl_custom_post_process(atnwalk_mutator_t *data, uint8_t *buf,
|
||||
size_t buf_size, uint8_t **out_buf) {
|
||||
|
||||
struct sockaddr_un addr;
|
||||
int fd_socket;
|
||||
uint8_t ctrl_buf[8];
|
||||
|
||||
// initialize the socket
|
||||
fd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd_socket == -1) { return fail_fatal(fd_socket, out_buf); }
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
|
||||
if (connect(fd_socket, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// ask whether the server is alive
|
||||
ctrl_buf[0] = SERVER_ARE_YOU_ALIVE;
|
||||
if (!write_all(fd_socket, ctrl_buf, 1)) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// see whether the server replies as expected
|
||||
if (!read_all(fd_socket, ctrl_buf, 1) ||
|
||||
ctrl_buf[0] != SERVER_YES_I_AM_ALIVE) {
|
||||
|
||||
return fail_fatal(fd_socket, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// tell the server what we want and how much data will be sent
|
||||
ctrl_buf[0] = SERVER_DECODE_BIT;
|
||||
put_uint32(ctrl_buf + 1, (uint32_t)buf_size);
|
||||
if (!write_all(fd_socket, ctrl_buf, 5)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// send the data to decode
|
||||
if (!write_all(fd_socket, buf, buf_size)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
// obtain the required buffer size for the data that will be returned
|
||||
if (!read_all(fd_socket, ctrl_buf, 4)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
size_t new_size = (size_t)to_uint32(ctrl_buf);
|
||||
|
||||
// need to use data->post_process_buf, let's see whether we need to reallocate
|
||||
if (new_size > data->post_process_size) {
|
||||
|
||||
data->post_process_size = new_size << 1;
|
||||
data->post_process_buf =
|
||||
(uint8_t *)realloc(data->post_process_buf, data->post_process_size);
|
||||
|
||||
}
|
||||
|
||||
*out_buf = data->post_process_buf;
|
||||
|
||||
// obtain the decoded data
|
||||
if (!read_all(fd_socket, *out_buf, new_size)) {
|
||||
|
||||
return fail_gracefully(fd_socket, data, buf, buf_size, out_buf);
|
||||
|
||||
}
|
||||
|
||||
close(fd_socket);
|
||||
return new_size;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize everything
|
||||
*
|
||||
* @param data The data ptr from afl_custom_init
|
||||
*/
|
||||
void afl_custom_deinit(atnwalk_mutator_t *data) {
|
||||
|
||||
free(data->fuzz_buf);
|
||||
free(data->post_process_buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
26
custom_mutators/autotokens/Makefile
Normal file
26
custom_mutators/autotokens/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
ifdef debug
|
||||
CPPLAGS += -fsanitize=address
|
||||
CXXFLAGS += -Wall
|
||||
CC := clang
|
||||
CXX := clang++
|
||||
endif
|
||||
ifdef DEBUG
|
||||
CPPFLAGS += -fsanitize=address
|
||||
CXXFLAGS += -Wall
|
||||
CC := clang
|
||||
CXX := clang++
|
||||
endif
|
||||
|
||||
all: autotokens.so
|
||||
|
||||
afl-fuzz-queue.o: ../../src/afl-fuzz-queue.c
|
||||
$(CC) -D_STANDALONE_MODULE=1 -I../../include -g -O3 $(CPPFLAGS) -fPIC -c -o ./afl-fuzz-queue.o ../../src/afl-fuzz-queue.c
|
||||
|
||||
afl-common.o: ../../src/afl-common.c
|
||||
$(CC) -I../../include -g -O3 $(CPPFLAGS) -DBIN_PATH=\"dummy\" -Wno-pointer-sign -fPIC -c -o ./afl-common.o ../../src/afl-common.c
|
||||
|
||||
autotokens.so: afl-fuzz-queue.o afl-common.o autotokens.cpp
|
||||
$(CXX) -Wno-deprecated -g -O3 $(CXXFLAGS) $(CPPFLAGS) -shared -fPIC -o autotokens.so -I../../include autotokens.cpp ./afl-fuzz-queue.o ../../src/afl-performance.o ./afl-common.o
|
||||
|
||||
clean:
|
||||
rm -f autotokens.so *.o *~ core
|
34
custom_mutators/autotokens/README
Normal file
34
custom_mutators/autotokens/README
Normal file
@ -0,0 +1,34 @@
|
||||
# Autotokens
|
||||
|
||||
This implements an improved autotoken grammar fuzzing idea presented in
|
||||
[Token-Level Fuzzing][https://www.usenix.org/system/files/sec21-salls.pdf].
|
||||
It is a grammar fuzzer without actually knowing the grammar, but only works
|
||||
with text based inputs.
|
||||
|
||||
It is recommended to run with together in an instance with `CMPLOG`.
|
||||
|
||||
If you have a dictionary (`-x`) this improves this custom grammar mutator.
|
||||
|
||||
If **not** running with `CMPLOG`, it is possible to set
|
||||
`AFL_CUSTOM_MUTATOR_ONLY` to concentrate on grammar bug classes.
|
||||
|
||||
Do **not** set `AFL_DISABLE_TRIM` with this custom mutator!
|
||||
|
||||
## Configuration via environment variables
|
||||
|
||||
`AUTOTOKENS_ONLY_FAV` - only use this mutator on favorite queue items
|
||||
`AUTOTOKENS_COMMENT` - what character or string starts a comment which will be
|
||||
removed. Default: `/* ... */`
|
||||
`AUTOTOKENS_FUZZ_COUNT_SHIFT` - reduce the number of fuzzing performed, shifting
|
||||
the value by this number, e.g. 1.
|
||||
`AUTOTOKENS_AUTO_DISABLE` - disable this module if the seeds are not ascii
|
||||
(or no input and no (ascii) dictionary)
|
||||
`AUTOTOKENS_LEARN_DICT` - learn from dictionaries?
|
||||
0 = none
|
||||
1 = only -x or autodict
|
||||
2 = -x, autodict and `CMPLOG`
|
||||
`AUTOTOKENS_CHANGE_MIN` - minimum number of mutations (1-256, default 8)
|
||||
`AUTOTOKENS_CHANGE_MAX` - maximum number of mutations (1-4096, default 64)
|
||||
`AUTOTOKENS_CREATE_FROM_THIN_AIR` - if only one small start file is present and
|
||||
a dictionary loaded then create one initial
|
||||
structure based on the dictionary.
|
1101
custom_mutators/autotokens/autotokens.cpp
Normal file
1101
custom_mutators/autotokens/autotokens.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,342 +0,0 @@
|
||||
#ifndef CUSTOM_MUTATOR_HELPERS
|
||||
#define CUSTOM_MUTATOR_HELPERS
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INITIAL_GROWTH_SIZE (64)
|
||||
|
||||
#define RAND_BELOW(limit) (rand() % (limit))
|
||||
|
||||
/* Use in a struct: creates a name_buf and a name_size variable. */
|
||||
#define BUF_VAR(type, name) \
|
||||
type * name##_buf; \
|
||||
size_t name##_size;
|
||||
/* this fills in `&structptr->something_buf, &structptr->something_size`. */
|
||||
#define BUF_PARAMS(struct, name) \
|
||||
(void **)&struct->name##_buf, &struct->name##_size
|
||||
|
||||
typedef struct {
|
||||
|
||||
} afl_t;
|
||||
|
||||
static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) {
|
||||
|
||||
static s8 interesting_8[] = {INTERESTING_8};
|
||||
static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16};
|
||||
static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32};
|
||||
|
||||
switch (RAND_BELOW(12)) {
|
||||
|
||||
case 0: {
|
||||
|
||||
/* Flip a single bit somewhere. Spooky! */
|
||||
|
||||
s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8);
|
||||
|
||||
out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 1: {
|
||||
|
||||
/* Set byte to interesting value. */
|
||||
|
||||
u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))];
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] = val;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 2: {
|
||||
|
||||
/* Set word to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)];
|
||||
break;
|
||||
case 1:
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 3: {
|
||||
|
||||
/* Set dword to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
|
||||
break;
|
||||
case 1:
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 4: {
|
||||
|
||||
/* Set qword to interesting value, randomly choosing endian. */
|
||||
|
||||
if (end - begin < 8) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 7) break;
|
||||
|
||||
switch (RAND_BELOW(2)) {
|
||||
|
||||
case 0:
|
||||
*(u64 *)(out_buf + byte_idx) =
|
||||
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)];
|
||||
break;
|
||||
case 1:
|
||||
*(u64 *)(out_buf + byte_idx) = SWAP64(
|
||||
(s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 5: {
|
||||
|
||||
/* Randomly subtract from byte. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 6: {
|
||||
|
||||
/* Randomly add to byte. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 7: {
|
||||
|
||||
/* Randomly subtract from word, random endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u16 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 8: {
|
||||
|
||||
/* Randomly add to word, random endian. */
|
||||
|
||||
if (end - begin < 2) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 1) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u16 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u16 *)(out_buf + byte_idx) =
|
||||
SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 9: {
|
||||
|
||||
/* Randomly subtract from dword, random endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u32 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 10: {
|
||||
|
||||
/* Randomly add to dword, random endian. */
|
||||
|
||||
if (end - begin < 4) break;
|
||||
|
||||
s32 byte_idx = (RAND_BELOW(end - begin) + begin);
|
||||
|
||||
if (byte_idx >= end - 3) break;
|
||||
|
||||
if (RAND_BELOW(2)) {
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
} else {
|
||||
|
||||
u32 num = 1 + RAND_BELOW(ARITH_MAX);
|
||||
|
||||
*(u32 *)(out_buf + byte_idx) =
|
||||
SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num);
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 11: {
|
||||
|
||||
/* Just set a random byte to a random value. Because,
|
||||
why not. We use XOR with 1-255 to eliminate the
|
||||
possibility of a no-op. */
|
||||
|
||||
out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This function calculates the next power of 2 greater or equal its argument.
|
||||
@return The rounded up power of 2 (if no overflow) or 0 on overflow.
|
||||
*/
|
||||
static inline size_t next_pow2(size_t in) {
|
||||
|
||||
if (in == 0 || in > (size_t)-1)
|
||||
return 0; /* avoid undefined behaviour under-/overflow */
|
||||
size_t out = in - 1;
|
||||
out |= out >> 1;
|
||||
out |= out >> 2;
|
||||
out |= out >> 4;
|
||||
out |= out >> 8;
|
||||
out |= out >> 16;
|
||||
return out + 1;
|
||||
|
||||
}
|
||||
|
||||
/* This function makes sure *size is > size_needed after call.
|
||||
It will realloc *buf otherwise.
|
||||
*size will grow exponentially as per:
|
||||
https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/
|
||||
Will return NULL and free *buf if size_needed is <1 or realloc failed.
|
||||
@return For convenience, this function returns *buf.
|
||||
*/
|
||||
static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) {
|
||||
|
||||
/* No need to realloc */
|
||||
if (likely(size_needed && *size >= size_needed)) return *buf;
|
||||
|
||||
/* No initial size was set */
|
||||
if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE;
|
||||
|
||||
/* grow exponentially */
|
||||
size_t next_size = next_pow2(size_needed);
|
||||
|
||||
/* handle overflow */
|
||||
if (!next_size) { next_size = size_needed; }
|
||||
|
||||
/* alloc */
|
||||
*buf = realloc(*buf, next_size);
|
||||
*size = *buf ? next_size : 0;
|
||||
|
||||
return *buf;
|
||||
|
||||
}
|
||||
|
||||
/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */
|
||||
static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2,
|
||||
size_t *size2) {
|
||||
|
||||
void * scratch_buf = *buf1;
|
||||
size_t scratch_size = *size1;
|
||||
*buf1 = *buf2;
|
||||
*size1 = *size2;
|
||||
*buf2 = scratch_buf;
|
||||
*size2 = scratch_size;
|
||||
|
||||
}
|
||||
|
||||
#undef INITIAL_GROWTH_SIZE
|
||||
|
||||
#endif
|
||||
|
63
custom_mutators/examples/custom_send.c
Normal file
63
custom_mutators/examples/custom_send.c
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// This is an example on how to use afl_custom_send
|
||||
// It writes each mutated data set to /tmp/foo
|
||||
// You can modify this to send to IPC, shared memory, etc.
|
||||
//
|
||||
// cc -O3 -fPIC -shared -g -o custom_send.so -I../../include custom_send.c
|
||||
// cd ../..
|
||||
// afl-cc -o test-instr test-instr.c
|
||||
// AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/examples/custom_send.so \
|
||||
// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_state_t *afl;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
if (!data) {
|
||||
|
||||
perror("afl_custom_init alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->afl = afl;
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
void afl_custom_fuzz_send(my_mutator_t *data, uint8_t *buf, size_t buf_size) {
|
||||
|
||||
int fd = open("/tmp/foo", O_CREAT | O_NOFOLLOW | O_TRUNC | O_RDWR, 0644);
|
||||
|
||||
if (fd >= 0) {
|
||||
|
||||
(void)write(fd, buf, buf_size);
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
Dominik Maier <mail@dmnk.co>
|
||||
*/
|
||||
|
||||
// You need to use -I /path/to/AFLplusplus/include
|
||||
#include "custom_mutator_helpers.h"
|
||||
// You need to use -I/path/to/AFLplusplus/include -I.
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -26,19 +26,14 @@ static const char *commands[] = {
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
afl_state_t *afl;
|
||||
|
||||
// any additional data here!
|
||||
size_t trim_size_current;
|
||||
int trimmming_steps;
|
||||
int cur_step;
|
||||
|
||||
// Reused buffers:
|
||||
BUF_VAR(u8, fuzz);
|
||||
BUF_VAR(u8, data);
|
||||
BUF_VAR(u8, havoc);
|
||||
BUF_VAR(u8, trim);
|
||||
BUF_VAR(u8, post_process);
|
||||
u8 *mutated_out, *post_process_buf, *trim_buf;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
@ -53,7 +48,7 @@ typedef struct my_mutator {
|
||||
* There may be multiple instances of this mutator in one afl-fuzz run!
|
||||
* Return NULL on error.
|
||||
*/
|
||||
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
srand(seed); // needed also by surgical_havoc_mutate()
|
||||
|
||||
@ -65,6 +60,27 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
|
||||
}
|
||||
|
||||
if ((data->mutated_out = (u8 *)malloc(MAX_FILE)) == NULL) {
|
||||
|
||||
perror("afl_custom_init malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
if ((data->post_process_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
|
||||
|
||||
perror("afl_custom_init malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
if ((data->trim_buf = (u8 *)malloc(MAX_FILE)) == NULL) {
|
||||
|
||||
perror("afl_custom_init malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->afl = afl;
|
||||
|
||||
return data;
|
||||
@ -96,29 +112,14 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
// the fuzzer
|
||||
size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size;
|
||||
|
||||
// maybe_grow is optimized to be quick for reused buffers.
|
||||
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size);
|
||||
if (!mutated_out) {
|
||||
|
||||
*out_buf = NULL;
|
||||
perror("custom mutator allocation (maybe_grow)");
|
||||
return 0; /* afl-fuzz will very likely error out after this. */
|
||||
|
||||
}
|
||||
memcpy(data->mutated_out, buf, buf_size);
|
||||
|
||||
// Randomly select a command string to add as a header to the packet
|
||||
memcpy(mutated_out, commands[rand() % 3], 3);
|
||||
memcpy(data->mutated_out, commands[rand() % 3], 3);
|
||||
|
||||
// Mutate the payload of the packet
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (mutated_size > max_size) { mutated_size = max_size; }
|
||||
|
||||
// Randomly perform one of the (no len modification) havoc mutations
|
||||
surgical_havoc_mutate(mutated_out, 3, mutated_size);
|
||||
|
||||
}
|
||||
|
||||
*out_buf = mutated_out;
|
||||
*out_buf = data->mutated_out;
|
||||
return mutated_size;
|
||||
|
||||
}
|
||||
@ -142,24 +143,16 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf,
|
||||
size_t buf_size, uint8_t **out_buf) {
|
||||
|
||||
uint8_t *post_process_buf =
|
||||
maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5);
|
||||
if (!post_process_buf) {
|
||||
if (buf_size + 5 > MAX_FILE) { buf_size = MAX_FILE - 5; }
|
||||
|
||||
perror("custom mutator realloc failed.");
|
||||
*out_buf = NULL;
|
||||
return 0;
|
||||
memcpy(data->post_process_buf + 5, buf, buf_size);
|
||||
data->post_process_buf[0] = 'A';
|
||||
data->post_process_buf[1] = 'F';
|
||||
data->post_process_buf[2] = 'L';
|
||||
data->post_process_buf[3] = '+';
|
||||
data->post_process_buf[4] = '+';
|
||||
|
||||
}
|
||||
|
||||
memcpy(post_process_buf + 5, buf, buf_size);
|
||||
post_process_buf[0] = 'A';
|
||||
post_process_buf[1] = 'F';
|
||||
post_process_buf[2] = 'L';
|
||||
post_process_buf[3] = '+';
|
||||
post_process_buf[4] = '+';
|
||||
|
||||
*out_buf = post_process_buf;
|
||||
*out_buf = data->post_process_buf;
|
||||
|
||||
return buf_size + 5;
|
||||
|
||||
@ -195,13 +188,6 @@ int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf,
|
||||
|
||||
data->cur_step = 0;
|
||||
|
||||
if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) {
|
||||
|
||||
perror("init_trim grow");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
memcpy(data->trim_buf, buf, buf_size);
|
||||
|
||||
data->trim_size_current = buf_size;
|
||||
@ -282,27 +268,11 @@ int32_t afl_custom_post_trim(my_mutator_t *data, int success) {
|
||||
size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size,
|
||||
u8 **out_buf, size_t max_size) {
|
||||
|
||||
if (buf_size == 0) {
|
||||
*out_buf = buf; // in-place mutation
|
||||
|
||||
*out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1);
|
||||
if (!*out_buf) {
|
||||
if (buf_size <= sizeof(size_t)) { return buf_size; }
|
||||
|
||||
perror("custom havoc: maybe_grow");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
**out_buf = rand() % 256;
|
||||
buf_size = 1;
|
||||
|
||||
} else {
|
||||
|
||||
// We reuse buf here. It's legal and faster.
|
||||
*out_buf = buf;
|
||||
|
||||
}
|
||||
|
||||
size_t victim = rand() % buf_size;
|
||||
size_t victim = rand() % (buf_size - sizeof(size_t));
|
||||
(*out_buf)[victim] += rand() % 10;
|
||||
|
||||
return buf_size;
|
||||
@ -369,9 +339,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t *data,
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->post_process_buf);
|
||||
free(data->havoc_buf);
|
||||
free(data->data_buf);
|
||||
free(data->fuzz_buf);
|
||||
free(data->mutated_out);
|
||||
free(data->trim_buf);
|
||||
free(data);
|
||||
|
||||
|
@ -45,9 +45,8 @@
|
||||
1) If you don't want to modify the test case, simply set `*out_buf = in_buf`
|
||||
and return the original `len`.
|
||||
|
||||
NOTE: the following is currently NOT true, we abort in this case!
|
||||
2) If you want to skip this test case altogether and have AFL generate a
|
||||
new one, return 0 or set `*out_buf = NULL`.
|
||||
new one, return 0.
|
||||
Use this sparingly - it's faster than running the target program
|
||||
with patently useless inputs, but still wastes CPU time.
|
||||
|
||||
@ -59,8 +58,6 @@
|
||||
Note that the buffer will *not* be freed for you. To avoid memory leaks,
|
||||
you need to free it or reuse it on subsequent calls (as shown below).
|
||||
|
||||
*** Feel free to reuse the original 'in_buf' BUFFER and return it. ***
|
||||
|
||||
Alright. The example below shows a simple postprocessor that tries to make
|
||||
sure that all input files start with "GIF89a".
|
||||
|
||||
@ -72,7 +69,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "alloc-inl.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* Header that must be present at the beginning of every test case: */
|
||||
|
||||
@ -80,8 +77,7 @@
|
||||
|
||||
typedef struct post_state {
|
||||
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
size_t size;
|
||||
|
||||
} post_state_t;
|
||||
|
||||
@ -95,15 +91,6 @@ void *afl_custom_init(void *afl) {
|
||||
|
||||
}
|
||||
|
||||
state->buf = calloc(sizeof(unsigned char), 4096);
|
||||
if (!state->buf) {
|
||||
|
||||
free(state);
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
@ -113,6 +100,10 @@ void *afl_custom_init(void *afl) {
|
||||
size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
|
||||
unsigned int len, unsigned char **out_buf) {
|
||||
|
||||
/* we do in-place modification as we do not increase the size */
|
||||
|
||||
*out_buf = in_buf;
|
||||
|
||||
/* Skip execution altogether for buffers shorter than 6 bytes (just to
|
||||
show how it's done). We can trust len to be sane. */
|
||||
|
||||
@ -120,34 +111,7 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
|
||||
|
||||
/* Do nothing for buffers that already start with the expected header. */
|
||||
|
||||
if (!memcmp(in_buf, HEADER, strlen(HEADER))) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
/* Allocate memory for new buffer, reusing previous allocation if
|
||||
possible. Note we have to use afl-fuzz's own realloc!
|
||||
Note that you should only do this if you need to grow the buffer,
|
||||
otherwise work with in_buf, and assign it to *out_buf instead. */
|
||||
|
||||
*out_buf = afl_realloc(out_buf, len);
|
||||
|
||||
/* If we're out of memory, the most graceful thing to do is to return the
|
||||
original buffer and give up on modifying it. Let AFL handle OOM on its
|
||||
own later on. */
|
||||
|
||||
if (!*out_buf) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
if (len > strlen(HEADER))
|
||||
memcpy(*out_buf + strlen(HEADER), in_buf + strlen(HEADER),
|
||||
len - strlen(HEADER));
|
||||
if (!memcmp(in_buf, HEADER, strlen(HEADER))) { return len; }
|
||||
|
||||
/* Insert the new header. */
|
||||
|
||||
@ -162,7 +126,6 @@ size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf,
|
||||
/* Gets called afterwards */
|
||||
void afl_custom_deinit(post_state_t *data) {
|
||||
|
||||
free(data->buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "alloc-inl.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
/* A macro to round an integer up to 4 kB. */
|
||||
|
||||
@ -53,7 +53,7 @@ void *afl_custom_init(void *afl) {
|
||||
|
||||
}
|
||||
|
||||
state->buf = calloc(sizeof(unsigned char), 4096);
|
||||
state->buf = calloc(sizeof(unsigned char), MAX_FILE);
|
||||
if (!state->buf) {
|
||||
|
||||
free(state);
|
||||
@ -80,21 +80,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
|
||||
|
||||
}
|
||||
|
||||
/* This is not a good way to do it, if you do not need to grow the buffer
|
||||
then just work with in_buf instead for speed reasons.
|
||||
But we want to show how to grow a buffer, so this is how it's done: */
|
||||
|
||||
unsigned int pos = 8;
|
||||
unsigned char *new_buf = afl_realloc(out_buf, UP4K(len));
|
||||
|
||||
if (!new_buf) {
|
||||
|
||||
*out_buf = in_buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
memcpy(new_buf, in_buf, len);
|
||||
unsigned int pos = 8;
|
||||
|
||||
/* Minimum size of a zero-length PNG chunk is 12 bytes; if we
|
||||
don't have that, we can bail out. */
|
||||
@ -124,7 +110,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
|
||||
|
||||
if (real_cksum != file_cksum) {
|
||||
|
||||
*(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum;
|
||||
*(uint32_t *)(data->buf + pos + 8 + chunk_len) = real_cksum;
|
||||
|
||||
}
|
||||
|
||||
@ -134,7 +120,7 @@ size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf,
|
||||
|
||||
}
|
||||
|
||||
*out_buf = new_buf;
|
||||
*out_buf = data->buf;
|
||||
return len;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// This simple example just creates random buffer <= 100 filled with 'A'
|
||||
// needs -I /path/to/AFLplusplus/include
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -13,14 +13,14 @@
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
afl_state_t *afl;
|
||||
|
||||
// Reused buffers:
|
||||
BUF_VAR(u8, fuzz);
|
||||
u8 *fuzz_buf;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
srand(seed);
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
@ -31,6 +31,14 @@ my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
|
||||
}
|
||||
|
||||
data->fuzz_buf = (u8 *)malloc(MAX_FILE);
|
||||
if (!data->fuzz_buf) {
|
||||
|
||||
perror("afl_custom_init malloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->afl = afl;
|
||||
|
||||
return data;
|
||||
@ -44,18 +52,10 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
|
||||
|
||||
int size = (rand() % 100) + 1;
|
||||
if (size > max_size) size = max_size;
|
||||
u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size);
|
||||
if (!mutated_out) {
|
||||
|
||||
*out_buf = NULL;
|
||||
perror("custom mutator allocation (maybe_grow)");
|
||||
return 0; /* afl-fuzz will very likely error out after this. */
|
||||
memset(data->fuzz_buf, _FIXED_CHAR, size);
|
||||
|
||||
}
|
||||
|
||||
memset(mutated_out, _FIXED_CHAR, size);
|
||||
|
||||
*out_buf = mutated_out;
|
||||
*out_buf = data->fuzz_buf;
|
||||
return size;
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
# Adapted for AFLplusplus by Dominik Maier <mail@dmnk.co>
|
||||
#
|
||||
# Copyright 2017 Battelle Memorial Institute. All rights reserved.
|
||||
# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
# Copyright 2019-2023 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.
|
||||
|
@ -14,7 +14,7 @@
|
||||
# <andreafioraldi@gmail.com>
|
||||
#
|
||||
# Copyright 2017 Battelle Memorial Institute. All rights reserved.
|
||||
# Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
# Copyright 2019-2023 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.
|
||||
|
@ -1,22 +0,0 @@
|
||||
#ifndef CUSTOM_MUTATOR_HELPERS
|
||||
#define CUSTOM_MUTATOR_HELPERS
|
||||
|
||||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "afl-fuzz.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INITIAL_GROWTH_SIZE (64)
|
||||
|
||||
/* Use in a struct: creates a name_buf and a name_size variable. */
|
||||
#define BUF_VAR(type, name) \
|
||||
type * name##_buf; \
|
||||
size_t name##_size;
|
||||
/* this filles in `&structptr->something_buf, &structptr->something_size`. */
|
||||
#define BUF_PARAMS(struct, name) \
|
||||
(void **)&struct->name##_buf, &struct->name##_size
|
||||
|
||||
#undef INITIAL_GROWTH_SIZE
|
||||
|
||||
#endif
|
||||
|
@ -3,14 +3,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
#include "mangle.h"
|
||||
|
||||
#define NUMBER_OF_MUTATIONS 5
|
||||
|
||||
uint8_t * queue_input;
|
||||
uint8_t *queue_input;
|
||||
size_t queue_input_size;
|
||||
afl_state_t * afl_struct;
|
||||
afl_state_t *afl_struct;
|
||||
run_t run;
|
||||
honggfuzz_t global;
|
||||
struct _dynfile_t dynfile;
|
||||
@ -18,8 +18,8 @@ struct _dynfile_t dynfile;
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_state_t *afl;
|
||||
run_t * run;
|
||||
u8 * mutator_buf;
|
||||
run_t *run;
|
||||
u8 *mutator_buf;
|
||||
unsigned int seed;
|
||||
unsigned int extras_cnt, a_extras_cnt;
|
||||
|
||||
@ -65,9 +65,9 @@ my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
/* When a new queue entry is added we check if there are new dictionary
|
||||
entries to add to honggfuzz structure */
|
||||
|
||||
uint8_t afl_custom_queue_new_entry(my_mutator_t * data,
|
||||
const uint8_t *filename_new_queue,
|
||||
const uint8_t *filename_orig_queue) {
|
||||
void afl_custom_queue_new_entry(my_mutator_t *data,
|
||||
const uint8_t *filename_new_queue,
|
||||
const uint8_t *filename_orig_queue) {
|
||||
|
||||
if (run.global->mutate.dictionaryCnt >= 1024) return;
|
||||
|
||||
@ -97,7 +97,7 @@ uint8_t afl_custom_queue_new_entry(my_mutator_t * data,
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libafl = { git = "https://github.com/AFLplusplus/LibAFL.git", rev = "62614ce1016c86e3f00f35b56399292ceabd486b" }
|
||||
libafl = { git = "https://github.com/AFLplusplus/LibAFL.git", rev = "266677bb88abe75165430f34e7de897c35560504" }
|
||||
custom_mutator = { path = "../rust/custom_mutator", features = ["afl_internals"] }
|
||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![cfg(unix)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::{
|
||||
@ -18,10 +17,12 @@ use libafl::{
|
||||
scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
|
||||
Mutator,
|
||||
},
|
||||
state::{HasCorpus, HasMaxSize, HasMetadata, HasRand, State},
|
||||
prelude::UsesInput,
|
||||
state::{HasCorpus, HasMaxSize, HasMetadata, HasRand, State, UsesState},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[allow(clippy::identity_op)]
|
||||
const MAX_FILE: usize = 1 * 1024 * 1024;
|
||||
|
||||
static mut AFL: Option<&'static afl_state> = None;
|
||||
@ -64,24 +65,32 @@ impl<'de> Deserialize<'de> for AFLCorpus {
|
||||
}
|
||||
}
|
||||
|
||||
impl Corpus<BytesInput> for AFLCorpus {
|
||||
impl UsesState for AFLCorpus {
|
||||
type State = AFLState;
|
||||
}
|
||||
|
||||
impl Corpus for AFLCorpus {
|
||||
#[inline]
|
||||
fn count(&self) -> usize {
|
||||
afl().queued_items as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add(&mut self, testcase: Testcase<BytesInput>) -> Result<usize, Error> {
|
||||
fn add(&mut self, _testcase: Testcase<BytesInput>) -> Result<usize, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn replace(&mut self, idx: usize, testcase: Testcase<BytesInput>) -> Result<(), Error> {
|
||||
fn replace(
|
||||
&mut self,
|
||||
_idx: usize,
|
||||
_testcase: Testcase<BytesInput>,
|
||||
) -> Result<Testcase<Self::Input>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<BytesInput>>, Error> {
|
||||
fn remove(&mut self, _idx: usize) -> Result<Option<Testcase<BytesInput>>, Error> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@ -92,7 +101,7 @@ impl Corpus<BytesInput> for AFLCorpus {
|
||||
entries.entry(idx).or_insert_with(|| {
|
||||
let queue_buf = std::slice::from_raw_parts_mut(afl().queue_buf, self.count());
|
||||
let entry = queue_buf[idx].as_mut().unwrap();
|
||||
let fname = CStr::from_ptr((entry.fname as *mut i8).as_ref().unwrap())
|
||||
let fname = CStr::from_ptr((entry.fname.cast::<i8>()).as_ref().unwrap())
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
@ -127,9 +136,10 @@ pub struct AFLState {
|
||||
}
|
||||
|
||||
impl AFLState {
|
||||
#[must_use]
|
||||
pub fn new(seed: u32) -> Self {
|
||||
Self {
|
||||
rand: StdRand::with_seed(seed as u64),
|
||||
rand: StdRand::with_seed(u64::from(seed)),
|
||||
corpus: AFLCorpus::default(),
|
||||
metadata: SerdeAnyMap::new(),
|
||||
max_size: MAX_FILE,
|
||||
@ -153,7 +163,11 @@ impl HasRand for AFLState {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasCorpus<BytesInput> for AFLState {
|
||||
impl UsesInput for AFLState {
|
||||
type Input = BytesInput;
|
||||
}
|
||||
|
||||
impl HasCorpus for AFLState {
|
||||
type Corpus = AFLCorpus;
|
||||
|
||||
#[inline]
|
||||
@ -208,7 +222,7 @@ impl CustomMutator for LibAFLBaseCustomMutator {
|
||||
tokens.push(data.to_vec());
|
||||
}
|
||||
if !tokens.is_empty() {
|
||||
state.add_metadata(Tokens::new(tokens));
|
||||
state.add_metadata(Tokens::from(tokens));
|
||||
}
|
||||
Ok(Self {
|
||||
state,
|
||||
@ -220,7 +234,7 @@ impl CustomMutator for LibAFLBaseCustomMutator {
|
||||
fn fuzz<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
add_buff: Option<&[u8]>,
|
||||
_add_buff: Option<&[u8]>,
|
||||
max_size: usize,
|
||||
) -> Result<Option<&'b [u8]>, Self::Error> {
|
||||
self.state.set_max_size(max_size);
|
||||
|
@ -1 +0,0 @@
|
||||
../examples/custom_mutator_helpers.h
|
@ -1,6 +1,5 @@
|
||||
// This simple example just creates random buffer <= 100 filled with 'A'
|
||||
// needs -I /path/to/AFLplusplus/include
|
||||
//#include "custom_mutator_helpers.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@ -8,19 +7,17 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "radamsa.h"
|
||||
#include "custom_mutator_helpers.h"
|
||||
#include "afl-fuzz.h"
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_t *afl;
|
||||
|
||||
u8 *mutator_buf;
|
||||
|
||||
afl_state_t *afl;
|
||||
u8 *mutator_buf;
|
||||
unsigned int seed;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) {
|
||||
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
srand(seed);
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
|
@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "custom_mutator-sys"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.56"
|
||||
bindgen = "0.63"
|
||||
|
@ -15,8 +15,8 @@ fn main() {
|
||||
// The input header we would like to generate
|
||||
// bindings for.
|
||||
.header("wrapper.h")
|
||||
.whitelist_type("afl_state_t")
|
||||
.blacklist_type(r"u\d+")
|
||||
.allowlist_type("afl_state_t")
|
||||
.blocklist_type(r"u\d+")
|
||||
.opaque_type(r"_.*")
|
||||
.opaque_type("FILE")
|
||||
.opaque_type("in_addr(_t)?")
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::too_many_lines)]
|
||||
#![allow(clippy::used_underscore_binding)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "custom_mutator"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
//! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++.
|
||||
//!
|
||||
//! # Access to AFL++ internals
|
||||
//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state.
|
||||
//! This crate has an optional feature "`afl_internals`", which gives access to AFL++'s internal state.
|
||||
//! The state is passed to [`CustomMutator::init`], when the feature is activated.
|
||||
//!
|
||||
//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._
|
||||
@ -115,7 +115,7 @@ pub mod wrappers {
|
||||
impl<M: RawCustomMutator> FFIContext<M> {
|
||||
fn from(ptr: *mut c_void) -> ManuallyDrop<Box<Self>> {
|
||||
assert!(!ptr.is_null());
|
||||
ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) })
|
||||
ManuallyDrop::new(unsafe { Box::from_raw(ptr.cast::<Self>()) })
|
||||
}
|
||||
|
||||
fn into_ptr(self: Box<Self>) -> *const c_void {
|
||||
@ -141,27 +141,28 @@ pub mod wrappers {
|
||||
}
|
||||
|
||||
/// panic handler called for every panic
|
||||
fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! {
|
||||
fn panic_handler(method: &str, panic_info: &Box<dyn Any + Send + 'static>) -> ! {
|
||||
use std::ops::Deref;
|
||||
let cause = panic_info
|
||||
.downcast_ref::<String>()
|
||||
.map(String::deref)
|
||||
.unwrap_or_else(|| {
|
||||
let cause = panic_info.downcast_ref::<String>().map_or_else(
|
||||
|| {
|
||||
panic_info
|
||||
.downcast_ref::<&str>()
|
||||
.copied()
|
||||
.unwrap_or("<cause unknown>")
|
||||
});
|
||||
eprintln!("A panic occurred at {}: {}", method, cause);
|
||||
},
|
||||
String::deref,
|
||||
);
|
||||
eprintln!("A panic occurred at {method}: {cause}");
|
||||
abort()
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
#[must_use]
|
||||
pub fn afl_custom_init_<M: RawCustomMutator>(seed: u32) -> *const c_void {
|
||||
match catch_unwind(|| FFIContext::<M>::new(seed).into_ptr()) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_init", err),
|
||||
Err(err) => panic_handler("afl_custom_init", &err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,7 +177,7 @@ pub mod wrappers {
|
||||
FFIContext::<M>::new(afl, seed).into_ptr()
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_init", err),
|
||||
Err(err) => panic_handler("afl_custom_init", &err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,32 +197,27 @@ pub mod wrappers {
|
||||
) -> usize {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if buf.is_null() {
|
||||
panic!("null buf passed to afl_custom_fuzz")
|
||||
}
|
||||
if out_buf.is_null() {
|
||||
panic!("null out_buf passed to afl_custom_fuzz")
|
||||
}
|
||||
|
||||
assert!(!buf.is_null(), "null buf passed to afl_custom_fuzz");
|
||||
assert!(!out_buf.is_null(), "null out_buf passed to afl_custom_fuzz");
|
||||
|
||||
let buff_slice = slice::from_raw_parts_mut(buf, buf_size);
|
||||
let add_buff_slice = if add_buf.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(slice::from_raw_parts(add_buf, add_buf_size))
|
||||
};
|
||||
match context.mutator.fuzz(buff_slice, add_buff_slice, max_size) {
|
||||
Some(buffer) => {
|
||||
*out_buf = buffer.as_ptr();
|
||||
buffer.len()
|
||||
}
|
||||
None => {
|
||||
// return the input buffer with 0-length to let AFL skip this mutation attempt
|
||||
*out_buf = buf;
|
||||
0
|
||||
}
|
||||
if let Some(buffer) = context.mutator.fuzz(buff_slice, add_buff_slice, max_size) {
|
||||
*out_buf = buffer.as_ptr();
|
||||
buffer.len()
|
||||
} else {
|
||||
// return the input buffer with 0-length to let AFL skip this mutation attempt
|
||||
*out_buf = buf;
|
||||
0
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_fuzz", err),
|
||||
Err(err) => panic_handler("afl_custom_fuzz", &err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,9 +233,8 @@ pub mod wrappers {
|
||||
) -> u32 {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if buf.is_null() {
|
||||
panic!("null buf passed to afl_custom_fuzz")
|
||||
}
|
||||
assert!(!buf.is_null(), "null buf passed to afl_custom_fuzz");
|
||||
|
||||
let buf_slice = slice::from_raw_parts(buf, buf_size);
|
||||
// see https://doc.rust-lang.org/nomicon/borrow-splitting.html
|
||||
let ctx = &mut **context;
|
||||
@ -247,37 +242,39 @@ pub mod wrappers {
|
||||
mutator.fuzz_count(buf_slice)
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_fuzz_count", err),
|
||||
Err(err) => panic_handler("afl_custom_fuzz_count", &err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
|
||||
pub unsafe fn afl_custom_queue_new_entry_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
filename_new_queue: *const c_char,
|
||||
filename_orig_queue: *const c_char,
|
||||
) -> bool {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
if filename_new_queue.is_null() {
|
||||
panic!("received null filename_new_queue in afl_custom_queue_new_entry");
|
||||
}
|
||||
assert!(
|
||||
!filename_new_queue.is_null(),
|
||||
"received null filename_new_queue in afl_custom_queue_new_entry"
|
||||
);
|
||||
|
||||
let filename_new_queue = Path::new(OsStr::from_bytes(
|
||||
unsafe { CStr::from_ptr(filename_new_queue) }.to_bytes(),
|
||||
));
|
||||
let filename_orig_queue = if !filename_orig_queue.is_null() {
|
||||
let filename_orig_queue = if filename_orig_queue.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Path::new(OsStr::from_bytes(
|
||||
unsafe { CStr::from_ptr(filename_orig_queue) }.to_bytes(),
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
context
|
||||
.mutator
|
||||
.queue_new_entry(filename_new_queue, filename_orig_queue)
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_queue_new_entry", err),
|
||||
Err(err) => panic_handler("afl_custom_queue_new_entry", &err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,7 +289,7 @@ pub mod wrappers {
|
||||
ManuallyDrop::into_inner(FFIContext::<M>::from(data));
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_deinit", err),
|
||||
Err(err) => panic_handler("afl_custom_deinit", &err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,13 +303,13 @@ pub mod wrappers {
|
||||
buf.extend_from_slice(res.as_bytes());
|
||||
buf.push(0);
|
||||
// unwrapping here, as the error case should be extremely rare
|
||||
CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
|
||||
CStr::from_bytes_with_nul(buf).unwrap().as_ptr()
|
||||
} else {
|
||||
null()
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_introspection", err),
|
||||
Err(err) => panic_handler("afl_custom_introspection", &err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,18 +326,18 @@ pub mod wrappers {
|
||||
buf.extend_from_slice(res.as_bytes());
|
||||
buf.push(0);
|
||||
// unwrapping here, as the error case should be extremely rare
|
||||
CStr::from_bytes_with_nul(&buf).unwrap().as_ptr()
|
||||
CStr::from_bytes_with_nul(buf).unwrap().as_ptr()
|
||||
} else {
|
||||
null()
|
||||
}
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_describe", err),
|
||||
Err(err) => panic_handler("afl_custom_describe", &err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub fn afl_custom_queue_get_<M: RawCustomMutator>(
|
||||
pub unsafe fn afl_custom_queue_get_<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
filename: *const c_char,
|
||||
) -> u8 {
|
||||
@ -348,12 +345,12 @@ pub mod wrappers {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
assert!(!filename.is_null());
|
||||
|
||||
context.mutator.queue_get(Path::new(OsStr::from_bytes(
|
||||
u8::from(context.mutator.queue_get(Path::new(OsStr::from_bytes(
|
||||
unsafe { CStr::from_ptr(filename) }.to_bytes(),
|
||||
))) as u8
|
||||
))))
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_queue_get", err),
|
||||
Err(err) => panic_handler("afl_custom_queue_get", &err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -373,7 +370,7 @@ macro_rules! _define_afl_custom_init {
|
||||
};
|
||||
}
|
||||
|
||||
/// An exported macro to defined afl_custom_init meant for insternal usage
|
||||
/// An exported macro to defined `afl_custom_init` meant for internal usage
|
||||
#[cfg(not(feature = "afl_internals"))]
|
||||
#[macro_export]
|
||||
macro_rules! _define_afl_custom_init {
|
||||
@ -444,7 +441,7 @@ macro_rules! export_mutator {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_queue_new_entry(
|
||||
pub unsafe extern "C" fn afl_custom_queue_new_entry(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
filename_new_queue: *const ::std::os::raw::c_char,
|
||||
filename_orig_queue: *const ::std::os::raw::c_char,
|
||||
@ -457,7 +454,7 @@ macro_rules! export_mutator {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn afl_custom_queue_get(
|
||||
pub unsafe extern "C" fn afl_custom_queue_get(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
filename: *const ::std::os::raw::c_char,
|
||||
) -> u8 {
|
||||
@ -520,9 +517,10 @@ mod sanity_test {
|
||||
export_mutator!(ExampleMutator);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// A custom mutator.
|
||||
/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`].
|
||||
#[allow(unused_variables)]
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub trait CustomMutator {
|
||||
/// The error type. All methods must return the same error type.
|
||||
type Error: Debug;
|
||||
@ -537,7 +535,7 @@ pub trait CustomMutator {
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
eprintln!("Error in custom mutator: {:?}", err)
|
||||
eprintln!("Error in custom mutator: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -759,8 +757,7 @@ mod truncate_test {
|
||||
let actual_output = truncate_str_unicode_safe(input, *max_len);
|
||||
assert_eq!(
|
||||
&actual_output, expected_output,
|
||||
"{:#?} truncated to {} bytes should be {:#?}, but is {:#?}",
|
||||
input, max_len, expected_output, actual_output
|
||||
"{input:#?} truncated to {max_len} bytes should be {expected_output:#?}, but is {actual_output:#?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "example_mutator"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "example_lain"
|
||||
version = "0.1.0"
|
||||
authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -5,6 +5,8 @@ This uses the symcc to find new paths into the target.
|
||||
Note that this is a just a proof of concept example! It is better to use
|
||||
the fuzzing helpers of symcc, symqemu, Fuzzolic, etc. rather than this.
|
||||
|
||||
Also the symqemu custom mutator is better than this.
|
||||
|
||||
To use this custom mutator follow the steps in the symcc repository
|
||||
[https://github.com/eurecom-s3/symcc/](https://github.com/eurecom-s3/symcc/)
|
||||
on how to build symcc and how to instrument a target binary (the same target
|
||||
|
14
custom_mutators/symqemu/Makefile
Normal file
14
custom_mutators/symqemu/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -DDEBUG
|
||||
endif
|
||||
|
||||
all: symqemu-mutator.so
|
||||
|
||||
CFLAGS += -O3 -funroll-loops
|
||||
|
||||
symqemu-mutator.so: symqemu.c
|
||||
$(CC) -g $(CFLAGS) $(CPPFLAGS) -g -I../../include -shared -fPIC -o symqemu-mutator.so symqemu.c
|
||||
|
||||
clean:
|
||||
rm -f symqemu-mutator.so *.o *~ core
|
19
custom_mutators/symqemu/README.md
Normal file
19
custom_mutators/symqemu/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# custum mutator: symqemu
|
||||
|
||||
This uses the symcc to find new paths into the target.
|
||||
|
||||
## How to build and use
|
||||
|
||||
To use this custom mutator follow the steps in the symqemu repository
|
||||
[https://github.com/eurecom-s3/symqemu/](https://github.com/eurecom-s3/symqemu/)
|
||||
on how to build symqemu-x86_x64 and put it in your `PATH`.
|
||||
|
||||
Just type `make` to build this custom mutator.
|
||||
|
||||
```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symqemu/symqemu-mutator.so AFL_DISABLE_TRIM=1 afl-fuzz ...```
|
||||
|
||||
## Options
|
||||
|
||||
`SYMQEMU_ALL=1` - use concolic solving on **all** queue items, not only interesting/favorite ones.
|
||||
|
||||
`SYMQEMU_LATE=1` - use concolic solving only after there have been no finds for 5 minutes.
|
424
custom_mutators/symqemu/symqemu.c
Normal file
424
custom_mutators/symqemu/symqemu.c
Normal file
@ -0,0 +1,424 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "afl-fuzz.h"
|
||||
#include "common.h"
|
||||
|
||||
afl_state_t *afl_struct;
|
||||
static u32 debug = 0;
|
||||
static u32 found_items = 0;
|
||||
|
||||
#define SYMQEMU_LOCATION "symqemu"
|
||||
|
||||
#define DBG(x...) \
|
||||
if (debug) { fprintf(stderr, x); }
|
||||
|
||||
typedef struct my_mutator {
|
||||
|
||||
afl_state_t *afl;
|
||||
u32 all;
|
||||
u32 late;
|
||||
u8 *mutator_buf;
|
||||
u8 *out_dir;
|
||||
u8 *target;
|
||||
u8 *symqemu;
|
||||
u8 *input_file;
|
||||
u32 counter;
|
||||
u32 seed;
|
||||
u32 argc;
|
||||
u8 **argv;
|
||||
|
||||
} my_mutator_t;
|
||||
|
||||
my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
|
||||
|
||||
if (getenv("AFL_DEBUG")) debug = 1;
|
||||
|
||||
my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
|
||||
if (!data) {
|
||||
|
||||
perror("afl_custom_init alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
char *path = getenv("PATH");
|
||||
char *exec_name = "symqemu-x86_64";
|
||||
char *token = strtok(path, ":");
|
||||
char exec_path[4096];
|
||||
|
||||
while (token != NULL && data->symqemu == NULL) {
|
||||
|
||||
snprintf(exec_path, sizeof(exec_path), "%s/%s", token, exec_name);
|
||||
if (access(exec_path, X_OK) == 0) {
|
||||
|
||||
data->symqemu = (u8 *)strdup(exec_path);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
token = strtok(NULL, ":");
|
||||
|
||||
}
|
||||
|
||||
if (!data->symqemu) FATAL("symqemu binary %s not found", exec_name);
|
||||
DBG("Found %s\n", data->symqemu);
|
||||
|
||||
if (getenv("AFL_CUSTOM_MUTATOR_ONLY")) {
|
||||
|
||||
WARNF(
|
||||
"the symqemu module is not very effective with "
|
||||
"AFL_CUSTOM_MUTATOR_ONLY.");
|
||||
|
||||
}
|
||||
|
||||
if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) {
|
||||
|
||||
free(data);
|
||||
perror("mutator_buf alloc");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
data->target = getenv("AFL_CUSTOM_INFO_PROGRAM");
|
||||
|
||||
u8 *path_tmp = getenv("AFL_CUSTOM_INFO_OUT");
|
||||
u32 len = strlen(path_tmp) + 32;
|
||||
u8 *symqemu_path = malloc(len);
|
||||
data->out_dir = malloc(len);
|
||||
snprintf(symqemu_path, len, "%s/%s", path_tmp, SYMQEMU_LOCATION);
|
||||
snprintf(data->out_dir, len, "%s/out", symqemu_path, path_tmp);
|
||||
|
||||
(void)mkdir(symqemu_path, 0755);
|
||||
(void)mkdir(data->out_dir, 0755);
|
||||
|
||||
setenv("SYMCC_OUTPUT_DIR", data->out_dir, 1);
|
||||
|
||||
data->input_file = getenv("AFL_CUSTOM_INFO_PROGRAM_INPUT");
|
||||
|
||||
u8 *tmp = NULL;
|
||||
if ((tmp = getenv("AFL_CUSTOM_INFO_PROGRAM_ARGV")) && *tmp) {
|
||||
|
||||
int argc = 0, index = 2;
|
||||
for (u32 i = 0; i < strlen(tmp); ++i)
|
||||
if (isspace(tmp[i])) ++argc;
|
||||
|
||||
data->argv = (u8 **)malloc((argc + 4) * sizeof(u8 **));
|
||||
u8 *p = strdup(tmp);
|
||||
|
||||
do {
|
||||
|
||||
data->argv[index] = p;
|
||||
while (*p && !isspace(*p))
|
||||
++p;
|
||||
if (*p) {
|
||||
|
||||
*p++ = 0;
|
||||
while (isspace(*p))
|
||||
++p;
|
||||
|
||||
}
|
||||
|
||||
if (strcmp(data->argv[index], "@@") == 0) {
|
||||
|
||||
if (!data->input_file) {
|
||||
|
||||
u32 ilen = strlen(symqemu_path) + 32;
|
||||
data->input_file = malloc(ilen);
|
||||
snprintf(data->input_file, ilen, "%s/.input", symqemu_path);
|
||||
|
||||
}
|
||||
|
||||
data->argv[index] = data->input_file;
|
||||
|
||||
}
|
||||
|
||||
DBG("%d: %s\n", index, data->argv[index]);
|
||||
index++;
|
||||
|
||||
} while (*p);
|
||||
|
||||
data->argv[index] = NULL;
|
||||
data->argc = index;
|
||||
|
||||
} else {
|
||||
|
||||
data->argv = (u8 **)malloc(8 * sizeof(u8 **));
|
||||
data->argc = 2;
|
||||
data->argv[2] = NULL;
|
||||
|
||||
}
|
||||
|
||||
data->argv[0] = data->symqemu;
|
||||
data->argv[1] = data->target;
|
||||
data->afl = afl;
|
||||
data->seed = seed;
|
||||
afl_struct = afl;
|
||||
|
||||
if (getenv("SYMQEMU_ALL")) { data->all = 1; }
|
||||
if (getenv("SYMQEMU_LATE")) { data->late = 1; }
|
||||
if (data->input_file) { setenv("SYMCC_INPUT_FILE", data->input_file, 1); }
|
||||
|
||||
DBG("out_dir=%s, target=%s, input_file=%s, argc=%u\n", data->out_dir,
|
||||
data->target,
|
||||
data->input_file ? (char *)data->input_file : (char *)"<stdin>",
|
||||
data->argc);
|
||||
|
||||
if (debug) {
|
||||
|
||||
fprintf(stderr, "[");
|
||||
for (u32 i = 0; i <= data->argc; ++i)
|
||||
fprintf(stderr, " \"%s\"",
|
||||
data->argv[i] ? (char *)data->argv[i] : "<NULL>");
|
||||
fprintf(stderr, " ]\n");
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/* No need to receive a splicing item */
|
||||
void afl_custom_splice_optout(void *data) {
|
||||
|
||||
(void)(data);
|
||||
|
||||
}
|
||||
|
||||
/* Get unix time in milliseconds */
|
||||
|
||||
inline u64 get_cur_time(void) {
|
||||
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
gettimeofday(&tv, &tz);
|
||||
|
||||
return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000);
|
||||
|
||||
}
|
||||
|
||||
u32 afl_custom_fuzz_count(my_mutator_t *data, const u8 *buf, size_t buf_size) {
|
||||
|
||||
if (likely((!afl_struct->queue_cur->favored && !data->all) ||
|
||||
afl_struct->queue_cur->was_fuzzed)) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (likely(data->late)) {
|
||||
|
||||
if (unlikely(get_cur_time() - afl_struct->last_find_time <=
|
||||
10 * 60 * 1000)) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int pipefd[2];
|
||||
struct stat st;
|
||||
|
||||
if (afl_struct->afl_env.afl_no_ui) {
|
||||
|
||||
ACTF("Sending to symqemu: %s", afl_struct->queue_cur->fname);
|
||||
|
||||
}
|
||||
|
||||
if (!(stat(afl_struct->queue_cur->fname, &st) == 0 && S_ISREG(st.st_mode) &&
|
||||
st.st_size)) {
|
||||
|
||||
PFATAL("Couldn't find enqueued file: %s", afl_struct->queue_cur->fname);
|
||||
|
||||
}
|
||||
|
||||
if (afl_struct->fsrv.use_stdin) {
|
||||
|
||||
if (pipe(pipefd) == -1) {
|
||||
|
||||
PFATAL(
|
||||
"Couldn't create a pipe for interacting with symqemu child process");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (data->input_file) {
|
||||
|
||||
int fd = open(data->input_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
ssize_t s = write(fd, buf, buf_size);
|
||||
close(fd);
|
||||
DBG("wrote %zd/%zd to %s\n", s, buf_size, data->input_file);
|
||||
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
|
||||
if (pid == -1) return 0;
|
||||
|
||||
if (likely(pid)) {
|
||||
|
||||
if (!data->input_file || afl_struct->fsrv.use_stdin) {
|
||||
|
||||
close(pipefd[0]);
|
||||
|
||||
if (fcntl(pipefd[1], F_GETPIPE_SZ)) {
|
||||
|
||||
fcntl(pipefd[1], F_SETPIPE_SZ, MAX_FILE);
|
||||
|
||||
}
|
||||
|
||||
ck_write(pipefd[1], buf, buf_size, data->input_file);
|
||||
|
||||
close(pipefd[1]);
|
||||
|
||||
}
|
||||
|
||||
pid = waitpid(pid, NULL, 0);
|
||||
DBG("symqemu finished executing!\n");
|
||||
|
||||
} else /* (pid == 0) */ { // child
|
||||
|
||||
if (afl_struct->fsrv.use_stdin) {
|
||||
|
||||
close(pipefd[1]);
|
||||
dup2(pipefd[0], 0);
|
||||
|
||||
}
|
||||
|
||||
DBG("exec=%s\n", data->target);
|
||||
if (!debug) {
|
||||
|
||||
close(1);
|
||||
close(2);
|
||||
dup2(afl_struct->fsrv.dev_null_fd, 1);
|
||||
dup2(afl_struct->fsrv.dev_null_fd, 2);
|
||||
|
||||
}
|
||||
|
||||
execvp((char *)data->argv[0], (char **)data->argv);
|
||||
fprintf(stderr, "Executing: [");
|
||||
for (u32 i = 0; i <= data->argc; ++i)
|
||||
fprintf(stderr, " \"%s\"",
|
||||
data->argv[i] ? (char *)data->argv[i] : "<NULL>");
|
||||
fprintf(stderr, " ]\n");
|
||||
FATAL("Failed to execute %s %s\n", data->argv[0], data->argv[1]);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
/* back in mother process */
|
||||
|
||||
struct dirent **nl;
|
||||
s32 i, items = scandir(data->out_dir, &nl, NULL, NULL);
|
||||
found_items = 0;
|
||||
char source_name[4096];
|
||||
|
||||
if (items > 0) {
|
||||
|
||||
for (i = 0; i < (u32)items; ++i) {
|
||||
|
||||
// symqemu output files start with a digit
|
||||
if (!isdigit(nl[i]->d_name[0])) continue;
|
||||
|
||||
struct stat st;
|
||||
snprintf(source_name, sizeof(source_name), "%s/%s", data->out_dir,
|
||||
nl[i]->d_name);
|
||||
DBG("file=%s\n", source_name);
|
||||
|
||||
if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
|
||||
|
||||
++found_items;
|
||||
|
||||
}
|
||||
|
||||
free(nl[i]);
|
||||
|
||||
}
|
||||
|
||||
free(nl);
|
||||
|
||||
}
|
||||
|
||||
DBG("Done, found %u items!\n", found_items);
|
||||
|
||||
return found_items;
|
||||
|
||||
}
|
||||
|
||||
size_t afl_custom_fuzz(my_mutator_t *data, u8 *buf, size_t buf_size,
|
||||
u8 **out_buf, u8 *add_buf, size_t add_buf_size,
|
||||
size_t max_size) {
|
||||
|
||||
struct dirent **nl;
|
||||
s32 done = 0, i, items = scandir(data->out_dir, &nl, NULL, NULL);
|
||||
char source_name[4096];
|
||||
|
||||
if (items > 0) {
|
||||
|
||||
for (i = 0; i < (u32)items; ++i) {
|
||||
|
||||
// symqemu output files start with a digit
|
||||
if (!isdigit(nl[i]->d_name[0])) continue;
|
||||
|
||||
struct stat st;
|
||||
snprintf(source_name, sizeof(source_name), "%s/%s", data->out_dir,
|
||||
nl[i]->d_name);
|
||||
DBG("file=%s\n", source_name);
|
||||
|
||||
if (stat(source_name, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) {
|
||||
|
||||
int fd = open(source_name, O_RDONLY);
|
||||
if (fd < 0) { goto got_an_issue; }
|
||||
|
||||
ssize_t r = read(fd, data->mutator_buf, MAX_FILE);
|
||||
close(fd);
|
||||
|
||||
DBG("fn=%s, fd=%d, size=%ld\n", source_name, fd, r);
|
||||
|
||||
if (r < 1) { goto got_an_issue; }
|
||||
|
||||
done = 1;
|
||||
--found_items;
|
||||
unlink(source_name);
|
||||
|
||||
*out_buf = data->mutator_buf;
|
||||
return (u32)r;
|
||||
|
||||
}
|
||||
|
||||
free(nl[i]);
|
||||
|
||||
}
|
||||
|
||||
free(nl);
|
||||
|
||||
}
|
||||
|
||||
got_an_issue:
|
||||
*out_buf = NULL;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitialize everything
|
||||
*
|
||||
* @param data The data ptr from afl_custom_init
|
||||
*/
|
||||
void afl_custom_deinit(my_mutator_t *data) {
|
||||
|
||||
free(data->mutator_buf);
|
||||
free(data);
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,86 @@
|
||||
This is the list of all noteworthy changes made in every public
|
||||
release of the tool. See README.md for the general instruction manual.
|
||||
|
||||
### Version ++4.07c (release)
|
||||
- afl-fuzz:
|
||||
- reverse reading the seeds only on restarts (increases performance)
|
||||
- new env `AFL_POST_PROCESS_KEEP_ORIGINAL` to keep the orignal
|
||||
data before post process on finds (for atnwalk custom mutator)
|
||||
- new env `AFL_IGNORE_PROBLEMS_COVERAGE` to ignore coverage from
|
||||
loaded libs after forkserver initialization (required by Mozilla)
|
||||
- afl-cc:
|
||||
- added @responsefile support
|
||||
- new env `AFL_LLVM_LTO_SKIPINIT` to support the AFL++ based WASM
|
||||
(https://github.com/fgsect/WAFL) project
|
||||
- error and print help if afl-clan-lto is used with lto=thin
|
||||
- rewrote our PCGUARD pass to be compatible with LLVM 15+ shenanigans,
|
||||
requires LLVM 13+ now instead of 10.0.1+
|
||||
- fallback to native LLVM PCGUARD if our PCGUARD is unavailable
|
||||
- fixed a crash in GCC CMPLOG
|
||||
- afl-showmap:
|
||||
- added custom mutator post_process and send support
|
||||
- add `-I filelist` option, an alternative to `-i in_dir`
|
||||
- afl-cmin + afl-cmin.bash:
|
||||
- `-T threads` parallel task support, can be a huge speedup!
|
||||
- qemu_mode:
|
||||
- Persistent mode + QASAN support for ppc32 targets by @worksbutnottested
|
||||
- a new grammar custom mutator atnwalk was submitted by @voidptr127 !
|
||||
- two new custom mutators are now available:
|
||||
- TritonDSE in custom_mutators/aflpp_tritondse
|
||||
- SymQEMU in custom_mutators/symqemu
|
||||
|
||||
|
||||
### Version ++4.06c (release)
|
||||
- afl-fuzz:
|
||||
- ensure temporary file descriptor is closed when not used
|
||||
- added `AFL_NO_WARN_INSTABILITY`
|
||||
- added time_wo_finds to fuzzer_stats
|
||||
- fixed a crash in pizza (1st april easter egg) mode. Sorry for
|
||||
everyone who was affected!
|
||||
- allow pizza mode to be disabled when AFL_PIZZA_MODE is set to -1
|
||||
- option `-p mmopt` now also selects new queue items more often
|
||||
- fix bug in post_process custom mutator implementation
|
||||
- print name of custom mutator in UI
|
||||
- slight changes that improve fuzzer performance
|
||||
- afl-cc:
|
||||
- add CFI sanitizer variant to gcc targets
|
||||
- llvm 16 + 17 support (thanks to @devnexen!)
|
||||
- support llvm 15 native pcguard changes
|
||||
- support for LLVMFuzzerTestOneInput -1 return
|
||||
- LTO autoken and llvm_mode: added AFL_LLVM_DICT2FILE_NO_MAIN support
|
||||
- qemu_mode:
|
||||
- fix _RANGES envs to allow hyphens in the filenames
|
||||
- basic riscv support
|
||||
- frida_mode:
|
||||
- added `AFL_FRIDA_STATS_INTERVAL`
|
||||
- fix issue on MacOS
|
||||
- unicorn_mode:
|
||||
- updated and minor issues fixed
|
||||
- nyx_mode support for all tools
|
||||
- better sanitizer default options support for all tools
|
||||
- new custom module: autotoken, a grammar free fuzzer for text inputs
|
||||
- fixed custom mutator C examples
|
||||
- more minor fixes and cross-platform support
|
||||
|
||||
### Version ++4.05c (release)
|
||||
- MacOS: libdislocator, libtokencap etc. do not work with modern
|
||||
MacOS anymore, but could be patched to work, see this issue if you
|
||||
want to make the effort and send a PR:
|
||||
https://github.com/AFLplusplus/AFLplusplus/issues/1594
|
||||
- afl-fuzz:
|
||||
- added afl_custom_fuzz_send custom mutator feature. Now your can
|
||||
send fuzz data to the target as you need, e.g. via IPC.
|
||||
- cmplog mode now has a -l R option for random colorization, thanks
|
||||
to guyf2010 for the PR!
|
||||
- queue statistics are written every 30 minutes to
|
||||
out/NAME/queue_data if compiled with INTROSPECTION
|
||||
- new env: AFL_FORK_SERVER_KILL_SIGNAL
|
||||
- afl-showmap/afl-cmin
|
||||
- `-t none` now translates to `-t 120000` (120 seconds)
|
||||
- unicorn_mode updated
|
||||
- updated rust custom mutator dependencies and LibAFL custom mutator
|
||||
- overall better sanitizer default setting handling
|
||||
- several minor bugfixes
|
||||
|
||||
### Version ++4.04c (release)
|
||||
- fix gramatron and grammar_mutator build scripts
|
||||
@ -10,6 +90,8 @@
|
||||
scripts
|
||||
- afl-fuzz:
|
||||
- force writing all stats on exit
|
||||
- ensure targets are killed on exit
|
||||
- `AFL_FORK_SERVER_KILL_SIGNAL` added
|
||||
- afl-cc:
|
||||
- make gcc_mode (afl-gcc-fast) work with gcc down to version 3.6
|
||||
- qemu_mode:
|
||||
@ -158,7 +240,7 @@
|
||||
afl-showmap and other tools.
|
||||
- afl-cc:
|
||||
- detect overflow reads on initial input buffer for asan
|
||||
- new cmplog mode (incompatible with older afl++ versions)
|
||||
- new cmplog mode (incompatible with older AFL++ versions)
|
||||
- support llvm IR select instrumentation for default PCGUARD and LTO
|
||||
- fix for shared linking on MacOS
|
||||
- better selective instrumentation AFL_LLVM_{ALLOW|DENY}LIST
|
||||
|
62
docs/FAQ.md
62
docs/FAQ.md
@ -171,6 +171,14 @@ If you find an interesting or important question missing, submit it via
|
||||
The more "unstable" edges there are, the harder it is for AFL++ to identify
|
||||
valid new paths.
|
||||
|
||||
If you fuzz in persistent mode (`AFL_LOOP` or `LLVMFuzzerTestOneInput()`
|
||||
harnesses, a large number of unstable edges can mean that the target keeps
|
||||
internal state and therefore it is possible that crashes cannot be replayed.
|
||||
In such a case do either **not** fuzz in persistent mode (remove `AFL_LOOP()`
|
||||
from your harness or call `LLVMFuzzerTestOneInput()` harnesses with `@@`),
|
||||
or set a low `AFL_LOOP` value, e.g. 100, and enable `AFL_PERSISTENT_RECORD`
|
||||
in `config.h` with the same value.
|
||||
|
||||
A value above 90% is usually fine and a value above 80% is also still ok, and
|
||||
even a value above 20% can still result in successful finds of bugs. However,
|
||||
it is recommended that for values below 90% or 80% you should take
|
||||
@ -229,7 +237,8 @@ If you find an interesting or important question missing, submit it via
|
||||
If this is not a viable option, you can set `AFL_IGNORE_PROBLEMS=1` but then
|
||||
the existing map will be used also for the newly loaded libraries, which
|
||||
allows it to work, however, the efficiency of the fuzzing will be partially
|
||||
degraded.
|
||||
degraded. Note that there is additionally `AFL_IGNORE_PROBLEMS_COVERAGE` to
|
||||
additionally tell AFL++ to ignore any coverage from the late loaded libaries.
|
||||
</p></details>
|
||||
|
||||
<details>
|
||||
@ -270,3 +279,54 @@ If you find an interesting or important question missing, submit it via
|
||||
|
||||
Solution: just do an `export AFL_MAP_SIZE=(the value in the warning)`.
|
||||
</p></details>
|
||||
|
||||
<details>
|
||||
<summary id="linker-errors">Linker errors.</summary><p>
|
||||
|
||||
If you compile C++ harnesses and see `undefined reference` errors for
|
||||
variables named `__afl_...`, e.g.:
|
||||
|
||||
```
|
||||
/usr/bin/ld: /tmp/test-d3085f.o: in function `foo::test()':
|
||||
test.cpp:(.text._ZN3fooL4testEv[_ZN3fooL4testEv]+0x35): undefined reference to `foo::__afl_connected'
|
||||
clang: error: linker command failed with exit code 1 (use -v to see invocation)
|
||||
```
|
||||
|
||||
Then you use AFL++ macros like `__AFL_LOOP` within a namespace and this
|
||||
will not work.
|
||||
|
||||
Solution: Move that harness portion to the global namespace, e.g. before:
|
||||
```
|
||||
#include <cstdio>
|
||||
namespace foo {
|
||||
static void test() {
|
||||
while(__AFL_LOOP(1000)) {
|
||||
foo::function();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
foo::test();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
after:
|
||||
```
|
||||
#include <cstdio>
|
||||
static void mytest() {
|
||||
while(__AFL_LOOP(1000)) {
|
||||
foo::function();
|
||||
}
|
||||
}
|
||||
namespace foo {
|
||||
static void test() {
|
||||
mytest();
|
||||
}
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
foo::test();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
</p></details>
|
||||
|
@ -3,9 +3,8 @@
|
||||
## Linux on x86
|
||||
|
||||
An easy way to install AFL++ with everything compiled is available via docker:
|
||||
You can use the [Dockerfile](../Dockerfile) (which has gcc-10 and clang-12 -
|
||||
hence afl-clang-lto is available) or just pull directly from the Docker Hub
|
||||
(for x86_64 and arm64):
|
||||
You can use the [Dockerfile](../Dockerfile) or just pull directly from the
|
||||
Docker Hub (for x86_64 and arm64):
|
||||
|
||||
```shell
|
||||
docker pull aflplusplus/aflplusplus:
|
||||
@ -21,14 +20,14 @@ development state of AFL++.
|
||||
If you want to build AFL++ yourself, you have many options. The easiest choice
|
||||
is to build and install everything:
|
||||
|
||||
NOTE: depending on your Debian/Ubuntu/Kali/... release, replace `-12` with
|
||||
whatever llvm version is available. We recommend llvm 12, 13 or 14.
|
||||
NOTE: depending on your Debian/Ubuntu/Kali/... release, replace `-14` with
|
||||
whatever llvm version is available. We recommend llvm 13, 14, 15 or 16.
|
||||
|
||||
```shell
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev
|
||||
# try to install llvm 12 and install the distro default if that fails
|
||||
sudo apt-get install -y lld-12 llvm-12 llvm-12-dev clang-12 || sudo apt-get install -y lld llvm llvm-dev clang
|
||||
# try to install llvm 14 and install the distro default if that fails
|
||||
sudo apt-get install -y lld-14 llvm-14 llvm-14-dev clang-14 || 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
|
||||
sudo apt-get install -y ninja-build # for QEMU mode
|
||||
git clone https://github.com/AFLplusplus/AFLplusplus
|
||||
@ -51,7 +50,7 @@ make source-only
|
||||
|
||||
These build targets exist:
|
||||
|
||||
* all: the main afl++ binaries and llvm/gcc instrumentation
|
||||
* all: the main AFL++ binaries and llvm/gcc instrumentation
|
||||
* binary-only: everything for binary-only fuzzing: frida_mode, nyx_mode,
|
||||
qemu_mode, frida_mode, unicorn_mode, coresight_mode, libdislocator,
|
||||
libtokencap
|
||||
@ -79,21 +78,20 @@ make STATIC=1
|
||||
These build options exist:
|
||||
|
||||
* STATIC - compile AFL++ static
|
||||
* CODE_COVERAGE - compile the target for code coverage (see docs/instrumentation/README.llvm.md)
|
||||
* ASAN_BUILD - compiles AFL++ with memory sanitizer for debug purposes
|
||||
* UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for
|
||||
debug purposes
|
||||
* UBSAN_BUILD - compiles AFL++ tools with undefined behaviour sanitizer for debug purposes
|
||||
* DEBUG - no optimization, -ggdb3, all warnings and -Werror
|
||||
* LLVM_DEBUG - shows llvm deprecation warnings
|
||||
* PROFILING - compile afl-fuzz with profiling information
|
||||
* INTROSPECTION - compile afl-fuzz with mutation introspection
|
||||
* NO_PYTHON - disable python support
|
||||
* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for
|
||||
normal fuzzing
|
||||
* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing
|
||||
* NO_NYX - disable building nyx mode dependencies
|
||||
* NO_CORESIGHT - disable building coresight (arm64 only)
|
||||
* NO_UNICORN_ARM64 - disable building unicorn on arm64
|
||||
* AFL_NO_X86 - if compiling on non-intel/amd platforms
|
||||
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config
|
||||
(e.g., Debian)
|
||||
* LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g., Debian)
|
||||
|
||||
e.g.: `make LLVM_CONFIG=llvm-config-14`
|
||||
|
||||
|
@ -483,6 +483,7 @@ directory. This includes:
|
||||
- `fuzzer_pid` - PID of the fuzzer process
|
||||
- `cycles_done` - queue cycles completed so far
|
||||
- `cycles_wo_finds` - number of cycles without any new paths found
|
||||
- `time_wo_finds` - longest time in seconds no new path was found
|
||||
- `execs_done` - number of execve() calls attempted
|
||||
- `execs_per_sec` - overall number of execs per second
|
||||
- `corpus_count` - total number of entries in the queue
|
||||
|
@ -131,6 +131,11 @@ jitter, or is a hash map function etc., then it should not be instrumented.
|
||||
To be able to exclude these functions (based on AFL++'s measured stability), the
|
||||
following process will allow to identify functions with variable edges.
|
||||
|
||||
Note that this is only useful for non-persistent targets!
|
||||
If a persistent target is unstable whereas when run non-persistent is fine,
|
||||
then this means that the target is keeping internal state, which is bad for
|
||||
fuzzing. Fuzz such targets **without** persistent mode.
|
||||
|
||||
Four steps are required to do this and it also requires quite some knowledge of
|
||||
coding and/or disassembly and is effectively possible only with `afl-clang-fast`
|
||||
`PCGUARD` and `afl-clang-lto` `LTO` instrumentation.
|
||||
|
@ -48,6 +48,7 @@ C/C++:
|
||||
```c
|
||||
void *afl_custom_init(afl_state_t *afl, unsigned int seed);
|
||||
unsigned int afl_custom_fuzz_count(void *data, const unsigned char *buf, size_t buf_size);
|
||||
void afl_custom_splice_optout(void *data);
|
||||
size_t afl_custom_fuzz(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, unsigned char *add_buf, size_t add_buf_size, size_t max_size);
|
||||
const char *afl_custom_describe(void *data, size_t max_description_len);
|
||||
size_t afl_custom_post_process(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf);
|
||||
@ -57,6 +58,7 @@ int afl_custom_post_trim(void *data, unsigned char success);
|
||||
size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, size_t max_size);
|
||||
unsigned char afl_custom_havoc_mutation_probability(void *data);
|
||||
unsigned char afl_custom_queue_get(void *data, const unsigned char *filename);
|
||||
void (*afl_custom_fuzz_send)(void *data, const u8 *buf, size_t buf_size);
|
||||
u8 afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue);
|
||||
const char* afl_custom_introspection(my_mutator_t *data);
|
||||
void afl_custom_deinit(void *data);
|
||||
@ -71,6 +73,9 @@ def init(seed):
|
||||
def fuzz_count(buf):
|
||||
return cnt
|
||||
|
||||
def splice_optout()
|
||||
pass
|
||||
|
||||
def fuzz(buf, add_buf, max_size):
|
||||
return mutated_out
|
||||
|
||||
@ -98,6 +103,9 @@ def havoc_mutation_probability():
|
||||
def queue_get(filename):
|
||||
return True
|
||||
|
||||
def fuzz_send(buf):
|
||||
pass
|
||||
|
||||
def queue_new_entry(filename_new_queue, filename_orig_queue):
|
||||
return False
|
||||
|
||||
@ -110,7 +118,7 @@ def deinit(): # optional for Python
|
||||
|
||||
### Custom Mutation
|
||||
|
||||
- `init`:
|
||||
- `init` (optional in Python):
|
||||
|
||||
This method is called when AFL++ starts up and is used to seed RNG and set
|
||||
up buffers and state.
|
||||
@ -128,13 +136,24 @@ def deinit(): # optional for Python
|
||||
for a specific queue entry, use this function. This function is most useful
|
||||
if `AFL_CUSTOM_MUTATOR_ONLY` is **not** used.
|
||||
|
||||
- `splice_optout` (optional):
|
||||
|
||||
If this function is present, no splicing target is passed to the `fuzz`
|
||||
function. This saves time if splicing data is not needed by the custom
|
||||
fuzzing function.
|
||||
This function is never called, just needs to be present to activate.
|
||||
|
||||
- `fuzz` (optional):
|
||||
|
||||
This method performs custom mutations on a given input. It also accepts an
|
||||
additional test case. Note that this function is optional - but it makes
|
||||
sense to use it. You would only skip this if `post_process` is used to fix
|
||||
checksums etc. so if you are using it, e.g., as a post processing library.
|
||||
Note that a length > 0 *must* be returned!
|
||||
This method performs your custom mutations on a given input.
|
||||
The add_buf is the contents of another queue item that can be used for
|
||||
splicing - or anything else - and can also be ignored. If you are not
|
||||
using this additional data then define `splice_optout` (see above).
|
||||
This function is optional.
|
||||
Returing a length of 0 is valid and is interpreted as skipping this
|
||||
one mutation result.
|
||||
For non-Python: the returned output buffer is under **your** memory
|
||||
management!
|
||||
|
||||
- `describe` (optional):
|
||||
|
||||
@ -168,6 +187,18 @@ def deinit(): # optional for Python
|
||||
to the target, e.g. if it is too short, too corrupted, etc. If so,
|
||||
return a NULL buffer and zero length (or a 0 length string in Python).
|
||||
|
||||
NOTE: Do not make any random changes to the data in this function!
|
||||
|
||||
PERFORMANCE for C/C++: If possible make the changes in-place (so modify
|
||||
the `*data` directly, and return it as `*outbuf = data`.
|
||||
|
||||
- `fuzz_send` (optional):
|
||||
|
||||
This method can be used if you want to send data to the target yourself,
|
||||
e.g. via IPC. This replaces some usage of utils/afl_proxy but requires
|
||||
that you start the target with afl-fuzz.
|
||||
Example: [custom_mutators/examples/custom_send.c](custom_mutators/examples/custom_send.c)
|
||||
|
||||
- `queue_new_entry` (optional):
|
||||
|
||||
This methods is called after adding a new test case to the queue. If the
|
||||
@ -179,7 +210,7 @@ def deinit(): # optional for Python
|
||||
discovered if compiled with INTROSPECTION. The custom mutator can then
|
||||
return a string (const char *) that reports the exact mutations used.
|
||||
|
||||
- `deinit`:
|
||||
- `deinit` (optional in Python):
|
||||
|
||||
The last method to be called, deinitializing the state.
|
||||
|
||||
@ -269,13 +300,41 @@ sudo apt install python-dev
|
||||
```
|
||||
|
||||
Then, AFL++ can be compiled with Python support. The AFL++ Makefile detects
|
||||
Python 2 and 3 through `python-config` if it is in the PATH and compiles
|
||||
`afl-fuzz` with the feature if available.
|
||||
Python3 through `python-config`/`python3-config` if it is in the PATH and
|
||||
compiles `afl-fuzz` with the feature if available.
|
||||
|
||||
Note: for some distributions, you might also need the package `python[23]-apt`.
|
||||
Note: for some distributions, you might also need the package `python[3]-apt`.
|
||||
In case your setup is different, set the necessary variables like this:
|
||||
`PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make`.
|
||||
|
||||
### Helpers
|
||||
|
||||
For C/C++ custom mutators you get a pointer to `afl_state_t *afl` in the
|
||||
`afl_custom_init()` which contains all information that you need.
|
||||
Note that if you access it, you need to recompile your custom mutator if
|
||||
you update AFL++ because the structure might have changed!
|
||||
|
||||
For mutators written in Python, Rust, GO, etc. there are a few environment
|
||||
variables set to help you to get started:
|
||||
|
||||
`AFL_CUSTOM_INFO_PROGRAM` - the program name of the target that is executed.
|
||||
If your custom mutator is used with modes like Qemu (`-Q`), this will still
|
||||
contain the target program, not afl-qemu-trace.
|
||||
|
||||
`AFL_CUSTOM_INFO_PROGRAM_INPUT` - if the `-f` parameter is used with afl-fuzz
|
||||
then this value is found in this environment variable.
|
||||
|
||||
`AFL_CUSTOM_INFO_PROGRAM_ARGV` - this contains the parameters given to the
|
||||
target program and still has the `@@` identifier in there.
|
||||
|
||||
Note: If `AFL_CUSTOM_INFO_PROGRAM_INPUT` is empty and `AFL_CUSTOM_INFO_PROGRAM_ARGV`
|
||||
is either empty or does not contain `@@` then the target gets the input via
|
||||
`stdin`.
|
||||
|
||||
`AFL_CUSTOM_INFO_OUT` - This is the output directory for this fuzzer instance,
|
||||
so if `afl-fuzz` was called with `-o out -S foobar`, then this will be set to
|
||||
`out/foobar`.
|
||||
|
||||
### Custom Mutator Preparation
|
||||
|
||||
For C/C++ mutators, the source code must be compiled as a shared object:
|
||||
|
@ -129,6 +129,9 @@ subset of the settings discussed in section 1, with the exception of:
|
||||
write all constant string comparisons to this file to be used later with
|
||||
afl-fuzz' `-x` option.
|
||||
|
||||
- An option to `AFL_LLVM_DICT2FILE` is `AFL_LLVM_DICT2FILE_NO_MAIN=1` which
|
||||
skill not parse `main()`.
|
||||
|
||||
- `TMPDIR` and `AFL_KEEP_ASSEMBLY`, since no temporary assembly files are
|
||||
created.
|
||||
|
||||
@ -153,7 +156,7 @@ Available options:
|
||||
- LTO - LTO instrumentation
|
||||
- NATIVE - clang's original pcguard based instrumentation
|
||||
- NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16)
|
||||
- PCGUARD - our own pcgard based instrumentation (default)
|
||||
- PCGUARD - our own pcguard based instrumentation (default)
|
||||
|
||||
#### CMPLOG
|
||||
|
||||
@ -237,7 +240,9 @@ combined.
|
||||
the default `0x10000`. A value of 0 or empty sets the map address to be
|
||||
dynamic (the original AFL way, which is slower).
|
||||
- `AFL_LLVM_MAP_DYNAMIC` sets the shared memory address to be dynamic.
|
||||
|
||||
- `AFL_LLVM_LTO_SKIPINIT` skips adding initialization code. Some global vars
|
||||
(e.g. the highest location ID) are not injected. Needed to instrument with
|
||||
[WAFL](https://github.com/fgsect/WAFL.git).
|
||||
For more information, see
|
||||
[instrumentation/README.lto.md](../instrumentation/README.lto.md).
|
||||
|
||||
@ -354,6 +359,9 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
- Setting `AFL_KEEP_TIMEOUTS` will keep longer running inputs if they reach
|
||||
new coverage
|
||||
|
||||
- On the contrary, if you are not interested in any timeouts, you can set
|
||||
`AFL_IGNORE_TIMEOUTS` to get a bit of speed instead.
|
||||
|
||||
- `AFL_EXIT_ON_SEED_ISSUES` will restore the vanilla afl-fuzz behavior which
|
||||
does not allow crashes or timeout seeds in the initial -i corpus.
|
||||
|
||||
@ -378,10 +386,10 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
valid terminal was detected (for virtual consoles).
|
||||
|
||||
- Setting `AFL_FORKSRV_INIT_TMOUT` allows you to specify a different timeout
|
||||
to wait for the forkserver to spin up. The default is the `-t` value times
|
||||
`FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the
|
||||
default would wait for `1000` milliseconds. Setting a different time here is
|
||||
useful if the target has a very slow startup time, for example, when doing
|
||||
to wait for the forkserver to spin up. The specified value is the new timeout, in milliseconds.
|
||||
The default is the `-t` value times `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the default would wait for `1000` milliseconds.
|
||||
The `AFL_FORKSRV_INIT_TMOUT` value does not get multiplied. It overwrites the initial timeout afl-fuzz waits for the target to come up with a constant time.
|
||||
Setting a different time here is useful if the target has a very slow startup time, for example, when doing
|
||||
full-system fuzzing or emulation, but you don't want the actual runs to wait
|
||||
too long for timeouts.
|
||||
|
||||
@ -398,7 +406,8 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
|
||||
- If afl-fuzz encounters an incorrect fuzzing setup during a fuzzing session
|
||||
(not at startup), it will terminate. If you do not want this, then you can
|
||||
set `AFL_IGNORE_PROBLEMS`.
|
||||
set `AFL_IGNORE_PROBLEMS`. If you additionally want to also ignore coverage
|
||||
from late loaded libraries, you can set `AFL_IGNORE_PROBLEMS_COVERAGE`.
|
||||
|
||||
- When running in the `-M` or `-S` mode, setting `AFL_IMPORT_FIRST` causes the
|
||||
fuzzer to import test cases from other instances before doing anything else.
|
||||
@ -409,11 +418,22 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
the afl-fuzz -g/-G command line option to control the minimum/maximum
|
||||
of fuzzing input generated.
|
||||
|
||||
- `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes on
|
||||
timeout. Unless you implement your own targets or instrumentation, you
|
||||
- `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes
|
||||
on timeout. Unless you implement your own targets or instrumentation, you
|
||||
likely don't have to set it. By default, on timeout and on exit, `SIGKILL`
|
||||
(`AFL_KILL_SIGNAL=9`) will be delivered to the child.
|
||||
|
||||
- `AFL_FORK_SERVER_KILL_SIGNAL`: Set the signal ID to be delivered to the
|
||||
fork server when AFL++ is terminated. Unless you implement your
|
||||
fork server, you likely do not have to set it. By default, `SIGTERM`
|
||||
(`AFL_FORK_SERVER_KILL_SIGNAL=15`) will be delivered to the fork server.
|
||||
If only `AFL_KILL_SIGNAL` is provided, `AFL_FORK_SERVER_KILL_SIGNAL` will
|
||||
be set to same value as `AFL_KILL_SIGNAL` to provide backward compatibility.
|
||||
If `AFL_FORK_SERVER_KILL_SIGNAL` is also set, it takes precedence.
|
||||
|
||||
NOTE: Uncatchable signals, such as `SIGKILL`, cause child processes of
|
||||
the fork server to be orphaned and leaves them in a zombie state.
|
||||
|
||||
- `AFL_MAP_SIZE` sets the size of the shared map that afl-analyze, afl-fuzz,
|
||||
afl-showmap, and afl-tmin create to gather instrumentation data from the
|
||||
target. This must be equal or larger than the size the target was compiled
|
||||
@ -463,7 +483,10 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
output from afl-fuzz is redirected to a file or to a pipe.
|
||||
|
||||
- Setting `AFL_NO_STARTUP_CALIBRATION` will skip the initial calibration
|
||||
of all starting seeds, and start fuzzing at once.
|
||||
of all starting seeds, and start fuzzing at once. Use with care, this
|
||||
degrades the fuzzing performance!
|
||||
|
||||
- Setting `AFL_NO_WARN_INSTABILITY` will suppress instability warnings.
|
||||
|
||||
- In QEMU mode (-Q) and FRIDA mode (-O), `AFL_PATH` will be searched for
|
||||
afl-qemu-trace and afl-frida-trace.so.
|
||||
@ -561,9 +584,15 @@ checks or alter some of the more exotic semantics of the tool:
|
||||
constructors in your target, you can set `AFL_EARLY_FORKSERVER`.
|
||||
Note that this is not a compile time option but a runtime option :-)
|
||||
|
||||
- Set `AFL_PIZZA_MODE` to 1 to enable the April 1st stats menu, set to 0
|
||||
- Set `AFL_PIZZA_MODE` to 1 to enable the April 1st stats menu, set to -1
|
||||
to disable although it is 1st of April.
|
||||
|
||||
- If you need a specific interval to update fuzzer_stats file, you can
|
||||
set `AFL_FUZZER_STATS_UPDATE_INTERVAL` to the interval in seconds you'd
|
||||
the file to be updated.
|
||||
Note that will not be exact and with slow targets it can take seconds
|
||||
until there is a slice for the time test.
|
||||
|
||||
## 5) Settings for afl-qemu-trace
|
||||
|
||||
The QEMU wrapper used to instrument binary-only code supports several settings:
|
||||
@ -590,6 +619,14 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
|
||||
- Setting `AFL_INST_LIBS` causes the translator to also instrument the code
|
||||
inside any dynamically linked libraries (notably including glibc).
|
||||
|
||||
- You can use `AFL_QEMU_INST_RANGES=0xaaaa-0xbbbb,0xcccc-0xdddd` to just
|
||||
instrument specific memory locations, e.g. a specific library.
|
||||
Excluding ranges takes priority over any included ranges or `AFL_INST_LIBS`.
|
||||
|
||||
- You can use `AFL_QEMU_EXCLUDE_RANGES=0xaaaa-0xbbbb,0xcccc-0xdddd` to **NOT**
|
||||
instrument specific memory locations, e.g. a specific library.
|
||||
Excluding ranges takes priority over any included ranges or `AFL_INST_LIBS`.
|
||||
|
||||
- It is possible to set `AFL_INST_RATIO` to skip the instrumentation on some
|
||||
of the basic blocks, which can be useful when dealing with very complex
|
||||
binaries.
|
||||
@ -651,6 +688,8 @@ support.
|
||||
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
|
||||
code. Code is considered to be JIT if the executable segment is not backed by
|
||||
a file.
|
||||
* `AFL_FRIDA_INST_NO_DYNAMIC_LOAD` - Don't instrument the code loaded late at
|
||||
runtime. Strictly limits instrumentation to what has been included.
|
||||
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
|
||||
instrumentation (the default where available). Required to use
|
||||
`AFL_FRIDA_INST_TRACE`.
|
||||
|
@ -201,10 +201,10 @@ afl-clang-fast's.
|
||||
### RetroWrite
|
||||
|
||||
RetroWrite is a static binary rewriter that can be combined with AFL++. If you
|
||||
have an x86_64 binary that still has its symbols (i.e., not stripped binary), is
|
||||
compiled with position independent code (PIC/PIE), and does not contain C++
|
||||
exceptions, then the RetroWrite solution might be for you. It decompiles to ASM
|
||||
files which can then be instrumented with afl-gcc.
|
||||
have an x86_64 or arm64 binary that does not contain C++ exceptions and - if
|
||||
x86_64 - still has it's symbols and compiled with position independent code
|
||||
(PIC/PIE), then the RetroWrite solution might be for you.
|
||||
It decompiles to ASM files which can then be instrumented with afl-gcc.
|
||||
|
||||
Binaries that are statically instrumented for fuzzing using RetroWrite are close
|
||||
in performance to compiler-instrumented binaries and outperform the QEMU-based
|
||||
|
@ -534,6 +534,8 @@ dictionaries/FORMAT.dict`.
|
||||
* With `afl-clang-fast`, you can set
|
||||
`AFL_LLVM_DICT2FILE=/full/path/to/new/file.dic` to automatically generate a
|
||||
dictionary during target compilation.
|
||||
Adding `AFL_LLVM_DICT2FILE_NO_MAIN=1` to not parse main (usually command line
|
||||
parameter parsing) is often a good idea too.
|
||||
* You also have the option to generate a dictionary yourself during an
|
||||
independent run of the target, see
|
||||
[utils/libtokencap/README.md](../utils/libtokencap/README.md).
|
||||
@ -628,7 +630,8 @@ If you have a large corpus, a corpus from a previous run or are fuzzing in a CI,
|
||||
then also set `export AFL_CMPLOG_ONLY_NEW=1` and `export AFL_FAST_CAL=1`.
|
||||
If the queue in the CI is huge and/or the execution time is slow then you can
|
||||
also add `AFL_NO_STARTUP_CALIBRATION=1` to skip the initial queue calibration
|
||||
phase and start fuzzing at once.
|
||||
phase and start fuzzing at once - but only do this if the calibration phase
|
||||
would be too long for your fuzz run time.
|
||||
|
||||
You can also use different fuzzers. If you are using AFL spinoffs or AFL
|
||||
conforming fuzzers, then just use the same -o directory and give it a unique
|
||||
@ -900,6 +903,13 @@ then color-codes the input based on which sections appear to be critical and
|
||||
which are not; while not bulletproof, it can often offer quick insights into
|
||||
complex file formats.
|
||||
|
||||
`casr-afl` from [CASR](https://github.com/ispras/casr) tools provides
|
||||
comfortable triaging for crashes found by AFL++. Reports are clustered and
|
||||
contain severity and other information.
|
||||
```shell
|
||||
casr-afl -i /path/to/afl/out/dir -o /path/to/casr/out/dir
|
||||
```
|
||||
|
||||
## 5. CI fuzzing
|
||||
|
||||
Some notes on continuous integration (CI) fuzzing - this fuzzing is different to
|
||||
@ -907,7 +917,8 @@ normal fuzzing campaigns as these are much shorter runnings.
|
||||
|
||||
If the queue in the CI is huge and/or the execution time is slow then you can
|
||||
also add `AFL_NO_STARTUP_CALIBRATION=1` to skip the initial queue calibration
|
||||
phase and start fuzzing at once.
|
||||
phase and start fuzzing at once. But only do that if the calibration time is
|
||||
too long for your overall available fuzz run time.
|
||||
|
||||
1. Always:
|
||||
* LTO has a much longer compile time which is diametrical to short fuzzing -
|
||||
@ -928,7 +939,7 @@ phase and start fuzzing at once.
|
||||
3. Also randomize the afl-fuzz runtime options, e.g.:
|
||||
* 65% for `AFL_DISABLE_TRIM`
|
||||
* 50% for `AFL_KEEP_TIMEOUTS`
|
||||
* 50% use a dictionary generated by `AFL_LLVM_DICT2FILE`
|
||||
* 50% use a dictionary generated by `AFL_LLVM_DICT2FILE` + `AFL_LLVM_DICT2FILE_NO_MAIN=1`
|
||||
* 40% use MOpt (`-L 0`)
|
||||
* 40% for `AFL_EXPAND_HAVOC_NOW`
|
||||
* 20% for old queue processing (`-Z`)
|
||||
|
@ -3,6 +3,8 @@
|
||||
In the following, we describe a variety of ideas that could be implemented for
|
||||
future AFL++ versions.
|
||||
|
||||
**NOTE:** Our GSoC participation is concerning [libafl](https://github.com/AFLplusplus/libafl), not AFL++.
|
||||
|
||||
## Analysis software
|
||||
|
||||
Currently analysis is done by using afl-plot, which is rather outdated. A GTK or
|
||||
@ -16,17 +18,6 @@ and Y axis, zoom factor, log scaling on-off, etc.
|
||||
|
||||
Mentor: vanhauser-thc
|
||||
|
||||
## WASM Instrumentation
|
||||
|
||||
Currently, AFL++ can be used for source code fuzzing and traditional binaries.
|
||||
With the rise of WASM as a compile target, however, a novel way of
|
||||
instrumentation needs to be implemented for binaries compiled to Webassembly.
|
||||
This can either be done by inserting instrumentation directly into the WASM AST,
|
||||
or by patching feedback into a WASM VM of choice, similar to the current Unicorn
|
||||
instrumentation.
|
||||
|
||||
Mentor: any
|
||||
|
||||
## Support other programming languages
|
||||
|
||||
Other programming languages also use llvm hence they could be (easily?)
|
||||
|
@ -1,5 +1,10 @@
|
||||
# Tools that help fuzzing with AFL++
|
||||
|
||||
## AFL++ and other development languages
|
||||
|
||||
* [afl-rs](https://github.com/rust-fuzz/afl.rs) - AFL++ for RUST
|
||||
* [WASM](https://github.com/fgsect/WAFL) - AFL++ for WASM
|
||||
|
||||
## Speeding up fuzzing
|
||||
|
||||
* [libfiowrapper](https://github.com/marekzmyslowski/libfiowrapper) - if the
|
||||
@ -62,3 +67,5 @@
|
||||
generates builds of debian packages suitable for AFL.
|
||||
* [afl-fid](https://github.com/FoRTE-Research/afl-fid) - a set of tools for
|
||||
working with input data.
|
||||
* [CASR](https://github.com/ispras/casr) - a set of tools for crash triage and
|
||||
analysis.
|
||||
|
@ -20,6 +20,10 @@ training, then we can highly recommend the following:
|
||||
|
||||
* [https://github.com/antonio-morales/Fuzzing101](https://github.com/antonio-morales/Fuzzing101)
|
||||
|
||||
Here is good workflow description for frida_mode:
|
||||
|
||||
* [https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html](https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html)
|
||||
|
||||
If you are interested in fuzzing structured data (where you define what the
|
||||
structure is), these links have you covered (some are outdated though):
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
"__afl_auto_first";
|
||||
"__afl_auto_init";
|
||||
"__afl_auto_second";
|
||||
"__afl_connected";
|
||||
"__afl_coverage_discard";
|
||||
"__afl_coverage_interesting";
|
||||
"__afl_coverage_off";
|
||||
@ -53,4 +54,5 @@
|
||||
"__sanitizer_cov_trace_pc_guard";
|
||||
"__sanitizer_cov_trace_pc_guard_init";
|
||||
"__sanitizer_cov_trace_switch";
|
||||
"LLVMFuzzerTestOneInput";
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
PWD:=$(shell pwd)/
|
||||
ROOT:=$(PWD)../
|
||||
INC_DIR:=$(PWD)include/
|
||||
@ -57,7 +58,10 @@ ifdef DEBUG
|
||||
CFLAGS+=-Werror \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wpointer-arith
|
||||
-Wpointer-arith \
|
||||
-Wno-unknown-pragmas \
|
||||
-Wno-pointer-to-int-cast \
|
||||
-Wno-int-to-pointer-cast
|
||||
else
|
||||
CFLAGS+=-Wno-pointer-arith
|
||||
endif
|
||||
@ -95,6 +99,25 @@ ifeq "$(shell uname)" "Darwin"
|
||||
OS:=macos
|
||||
AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-deprecated-declarations
|
||||
GUM_ARCH:=""
|
||||
ifeq "$(ARCH)" "arm64"
|
||||
TARGET_CC= \
|
||||
"clang" \
|
||||
"-target" \
|
||||
"arm64-apple-macos10.9"
|
||||
TARGET_CXX= \
|
||||
"clang++" \
|
||||
"-target" \
|
||||
"arm64-apple-macos10.9"
|
||||
else
|
||||
TARGET_CC= \
|
||||
"clang" \
|
||||
"-target" \
|
||||
"x86_64-apple-macos10.9"
|
||||
TARGET_CXX= \
|
||||
"clang++" \
|
||||
"-target" \
|
||||
"x86_64-apple-macos10.9"
|
||||
endif
|
||||
else
|
||||
ifdef DEBUG
|
||||
AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-prio-ctor-dtor
|
||||
@ -142,7 +165,7 @@ ifndef OS
|
||||
$(error "Operating system unsupported")
|
||||
endif
|
||||
|
||||
GUM_DEVKIT_VERSION=15.2.1
|
||||
GUM_DEVKIT_VERSION=16.0.11
|
||||
GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz
|
||||
GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)"
|
||||
|
||||
@ -188,6 +211,9 @@ all: $(FRIDA_TRACE) $(FRIDA_TRACE_LIB) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QE
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
|
||||
arm:
|
||||
CFLAGS="-marm" LDFLAGS="-marm" ARCH="armhf" TARGET_CC=arm-linux-gnueabihf-gcc TARGET_CXX=arm-linux-gnueabihf-g++ make all
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
|
||||
@ -206,7 +232,7 @@ $(FRIDA_MAKEFILE): | $(BUILD_DIR)
|
||||
.PHONY: $(GUM_DEVIT_LIBRARY)
|
||||
|
||||
$(GUM_DEVIT_LIBRARY): $(FRIDA_MAKEFILE)
|
||||
cd $(FRIDA_DIR) && make gum-$(OS)$(GUM_ARCH)
|
||||
cd $(FRIDA_DIR) && make gum-$(OS)$(GUM_ARCH) FRIDA_V8=disabled
|
||||
|
||||
$(GUM_DEVIT_HEADER): $(FRIDA_MAKEFILE) | $(FRIDA_BUILD_DIR)
|
||||
echo "#include <stdio.h>" > $@
|
||||
@ -245,40 +271,40 @@ TRACE_LDFLAGS+=$(FRIDA_DIR)build/frida-$(OS)-$(ARCH)/lib/libfrida-gum-1.0.a \
|
||||
|
||||
else ifeq "$(ARCH)" "arm64"
|
||||
|
||||
CFLAGS+=-I $(FRIDA_DIR)build/frida_thin-$(OS)-$(ARCH)/include/frida-1.0 \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/glib-2.0/ \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/capstone/ \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \
|
||||
CFLAGS+=-I $(FRIDA_DIR)build/$(OS)-$(ARCH)/include/frida-1.0 \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/glib-2.0/ \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/capstone/ \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \
|
||||
|
||||
ifeq "$(OS)" "android"
|
||||
CFLAGS += -static-libstdc++
|
||||
endif
|
||||
else
|
||||
CFLAGS+=-I $(FRIDA_DIR)build/frida_thin-$(OS)-$(ARCH)/include/frida-1.0 \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/glib-2.0/ \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/capstone/ \
|
||||
-I $(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \
|
||||
CFLAGS+=-I $(FRIDA_DIR)build/$(OS)-$(ARCH)/include/frida-1.0 \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/glib-2.0/ \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/glib-2.0/include/ \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/capstone/ \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/json-glib-1.0/ \
|
||||
|
||||
endif
|
||||
|
||||
TRACE_LDFLAGS+=$(FRIDA_DIR)build/frida-$(OS)-$(ARCH)/lib/libfrida-gum-1.0.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libsoup-2.4.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libsqlite3.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libtcc.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libjson-glib-1.0.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libquickjs.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libcapstone.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libunwind.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libffi.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libdwarf.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libelf.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libgio-2.0.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libgobject-2.0.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libglib-2.0.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/liblzma.a \
|
||||
$(FRIDA_DIR)build/frida_thin-sdk-$(OS)-$(ARCH)/lib/libz.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libsoup-2.4.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libsqlite3.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libtcc.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libjson-glib-1.0.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libquickjs.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libcapstone.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libunwind.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libffi.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libdwarf.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libelf.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libgio-2.0.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libgobject-2.0.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libglib-2.0.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/liblzma.a \
|
||||
$(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/lib/libz.a \
|
||||
|
||||
CFLAGS+=-I $(FRIDA_DIR)build/frida-$(OS)-$(ARCH)/include/frida-1.0 \
|
||||
-I $(FRIDA_DIR)build/sdk-$(OS)-$(ARCH)/include/glib-2.0/ \
|
||||
|
@ -7,6 +7,8 @@ variables.
|
||||
|
||||
In FRIDA mode, binary programs are instrumented, similarly to QEMU mode.
|
||||
|
||||
A tutorial can be found at [https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html](https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html)
|
||||
|
||||
## Current progress
|
||||
|
||||
As FRIDA mode is new, it is missing a lot of features. The design is such that
|
||||
@ -178,11 +180,13 @@ Default is 256Mb.
|
||||
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
|
||||
code. Code is considered to be JIT if the executable segment is not backed by
|
||||
a file.
|
||||
* `AFL_FRIDA_INST_NO_DYNAMIC_LOAD` - Don't instrument the code loaded late at
|
||||
runtime. Strictly limits instrumentation to what has been included.
|
||||
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
|
||||
instrumentation (the default where available). Required to use
|
||||
`AFL_FRIDA_INST_TRACE`.
|
||||
* `AFL_FRIDA_INST_REGS_FILE` - File to write raw register contents at the start
|
||||
of each block.
|
||||
`AFL_FRIDA_INST_TRACE`.
|
||||
* `AFL_FRIDA_INST_NO_CACHE` - Don't use a look-up table to cache real to
|
||||
instrumented address block translations.
|
||||
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default, the child will
|
||||
@ -193,6 +197,13 @@ instrumented address block translations.
|
||||
backpatching information. By default, the child will report applied
|
||||
backpatches to the parent so that they can be applied and then be inherited by
|
||||
the next child on fork.
|
||||
* `AFL_FRIDA_INST_NO_SUPPRESS` - Disable deterministic branch suppression.
|
||||
Deterministic branch suppression skips the preamble which generates coverage
|
||||
information at the start of each block, if the block is reached by a
|
||||
deterministic branch. This reduces map polution, and may improve performance
|
||||
when all the executing blocks have been prefetched and backpatching applied.
|
||||
However, in the event that backpatching is incomplete, this may incur a
|
||||
performance penatly as branch instructions are disassembled on each branch.
|
||||
* `AFL_FRIDA_INST_SEED` - Sets the initial seed for the hash function used to
|
||||
generate block (and hence edge) IDs. Setting this to a constant value may be
|
||||
useful for debugging purposes, e.g., investigating unstable edges.
|
||||
|
@ -390,7 +390,7 @@ Consider the [following](test/js/test2.c) test code...
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
@ -844,6 +844,12 @@ class Afl {
|
||||
static setInstrumentLibraries() {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
|
||||
*/
|
||||
static setInstrumentNoDynamicLoad() {
|
||||
Afl.jsApiSetInstrumentNoDynamicLoad();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
|
@ -19,9 +19,11 @@
|
||||
js_api_set_instrument_jit;
|
||||
js_api_set_instrument_libraries;
|
||||
js_api_set_instrument_instructions;
|
||||
js_api_set_instrument_no_dynamic_load;
|
||||
js_api_set_instrument_no_optimize;
|
||||
js_api_set_instrument_regs_file;
|
||||
js_api_set_instrument_seed;
|
||||
js_api_set_instrument_suppress_disable;
|
||||
js_api_set_instrument_trace;
|
||||
js_api_set_instrument_trace_unique;
|
||||
js_api_set_instrument_unstable_coverage_file;
|
||||
|
@ -54,10 +54,12 @@ __attribute__((visibility("default"))) void afl_persistent_hook(
|
||||
|
||||
__attribute__((visibility("default"))) void afl_persistent_hook(
|
||||
GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
// do a length check matching the target!
|
||||
|
||||
memcpy((void *)regs->r[0], input_buf, input_buf_len);
|
||||
regs->r[1] = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -14,8 +14,6 @@ void entry_init(void);
|
||||
|
||||
void entry_start(void);
|
||||
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
|
||||
|
||||
void entry_on_fork(void);
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,7 @@ extern guint64 instrument_hash_zero;
|
||||
extern char *instrument_coverage_unstable_filename;
|
||||
extern gboolean instrument_coverage_insn;
|
||||
extern char *instrument_regs_filename;
|
||||
extern gboolean instrument_suppress;
|
||||
|
||||
extern gboolean instrument_use_fixed_seed;
|
||||
extern guint64 instrument_fixed_seed;
|
||||
|
@ -6,6 +6,7 @@
|
||||
extern gboolean ranges_debug_maps;
|
||||
extern gboolean ranges_inst_libs;
|
||||
extern gboolean ranges_inst_jit;
|
||||
extern gboolean ranges_inst_dynamic_load;
|
||||
|
||||
void ranges_config(void);
|
||||
void ranges_init(void);
|
||||
|
@ -204,10 +204,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
|
||||
|
||||
gsize address = context->pc;
|
||||
|
||||
register uintptr_t k = (uintptr_t)address;
|
||||
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 1;
|
||||
register uintptr_t k = instrument_get_offset_hash(GUM_ADDRESS(address));
|
||||
|
||||
if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS)
|
||||
__afl_cmp_map->headers[k].hits = 0;
|
||||
|
@ -188,10 +188,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
|
||||
|
||||
gsize address = ctx_read_reg(context, X86_REG_RIP);
|
||||
|
||||
register uintptr_t k = (uintptr_t)address;
|
||||
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 7;
|
||||
register uintptr_t k = instrument_get_offset_hash(GUM_ADDRESS(address));
|
||||
|
||||
if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS)
|
||||
__afl_cmp_map->headers[k].hits = 0;
|
||||
|
@ -193,10 +193,7 @@ static void cmplog_handle_cmp_sub(GumCpuContext *context, gsize operand1,
|
||||
|
||||
gsize address = ctx_read_reg(context, X86_REG_EIP);
|
||||
|
||||
register uintptr_t k = (uintptr_t)address;
|
||||
|
||||
k = (k >> 4) ^ (k << 8);
|
||||
k &= CMP_MAP_W - 1;
|
||||
register uintptr_t k = instrument_get_offset_hash(GUM_ADDRESS(address));
|
||||
|
||||
if (__afl_cmp_map->headers[k].type != CMP_TYPE_INS)
|
||||
__afl_cmp_map->headers[k].hits = 0;
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg) {
|
||||
|
||||
UNUSED_PARAMETER(ctx);
|
||||
UNUSED_PARAMETER(reg);
|
||||
FFATAL("ctx_read_reg unimplemented for this architecture");
|
||||
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ void entry_init(void) {
|
||||
|
||||
void entry_start(void) {
|
||||
|
||||
FVERBOSE("AFL_ENTRYPOINT reached");
|
||||
if (persistent_start == 0) {
|
||||
|
||||
ranges_exclude();
|
||||
@ -85,32 +86,7 @@ void entry_start(void) {
|
||||
|
||||
}
|
||||
|
||||
if (entry_point == 0) { entry_launch(); }
|
||||
|
||||
}
|
||||
|
||||
static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(cpu_context);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
entry_compiled = TRUE;
|
||||
entry_launch();
|
||||
|
||||
}
|
||||
|
||||
void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
|
||||
|
||||
UNUSED_PARAMETER(output);
|
||||
FVERBOSE("AFL_ENTRYPOINT reached");
|
||||
|
||||
if (persistent_start == 0) {
|
||||
|
||||
ranges_exclude();
|
||||
stalker_trust();
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ gboolean instrument_optimize = false;
|
||||
gboolean instrument_unique = false;
|
||||
guint64 instrument_hash_zero = 0;
|
||||
guint64 instrument_hash_seed = 0;
|
||||
gboolean instrument_suppress = false;
|
||||
|
||||
gboolean instrument_use_fixed_seed = FALSE;
|
||||
guint64 instrument_fixed_seed = 0;
|
||||
@ -168,7 +169,6 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
|
||||
|
||||
if (instr->address == entry_point) { entry_prologue(iterator, output); }
|
||||
if (instr->address == persistent_start) { persistent_prologue(output); }
|
||||
if (instr->address == persistent_ret) { persistent_epilogue(output); }
|
||||
|
||||
@ -290,6 +290,7 @@ void instrument_config(void) {
|
||||
(getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE"));
|
||||
instrument_coverage_insn = (getenv("AFL_FRIDA_INST_INSN") != NULL);
|
||||
instrument_regs_filename = getenv("AFL_FRIDA_INST_REGS_FILE");
|
||||
instrument_suppress = (getenv("AFL_FRIDA_INST_NO_SUPPRESS") == NULL);
|
||||
|
||||
instrument_debug_config();
|
||||
instrument_coverage_config();
|
||||
@ -321,6 +322,9 @@ void instrument_init(void) {
|
||||
FOKF(cBLU "Instrumentation" cRST " - " cGRN "instructions:" cYEL " [%c]",
|
||||
instrument_coverage_insn ? 'X' : ' ');
|
||||
|
||||
FOKF(cBLU "Instrumentation" cRST " - " cGRN "suppression:" cYEL " [%c]",
|
||||
instrument_suppress ? 'X' : ' ');
|
||||
|
||||
if (instrument_tracing && instrument_optimize) {
|
||||
|
||||
WARNF("AFL_FRIDA_INST_TRACE implies AFL_FRIDA_INST_NO_OPTIMIZE");
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "stalker.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__arm__)
|
||||
@ -8,8 +9,9 @@
|
||||
#define PAGE_MASK (~(GUM_ADDRESS(0xfff)))
|
||||
#define PAGE_ALIGNED(x) ((GUM_ADDRESS(x) & PAGE_MASK) == GUM_ADDRESS(x))
|
||||
|
||||
gboolean instrument_cache_enabled = FALSE;
|
||||
gsize instrument_cache_size = 0;
|
||||
gboolean instrument_cache_enabled = FALSE;
|
||||
gsize instrument_cache_size = 0;
|
||||
static GHashTable *coverage_blocks = NULL;
|
||||
|
||||
extern __thread guint64 instrument_previous_pc;
|
||||
|
||||
@ -22,7 +24,24 @@ typedef struct {
|
||||
// shared_mem[cur_location ^ prev_location]++;
|
||||
// prev_location = cur_location >> 1;
|
||||
|
||||
/* We can remove this branch when we add support for branch suppression */
|
||||
// str r0, [sp, #-128] ; 0xffffff80
|
||||
// str r1, [sp, #-132] ; 0xffffff7c
|
||||
// ldr r0, [pc, #-20] ; 0xf691b29c
|
||||
// ldrh r1, [r0]
|
||||
// movw r0, #33222 ; 0x81c6
|
||||
// eor r0, r0, r1
|
||||
// ldr r1, [pc, #-40] ; 0xf691b298
|
||||
// add r1, r1, r0
|
||||
// ldrb r0, [r1]
|
||||
// add r0, r0, #1
|
||||
// add r0, r0, r0, lsr #8
|
||||
// strb r0, [r1]
|
||||
// movw r0, #49379 ; 0xc0e3
|
||||
// ldr r1, [pc, #-64] ; 0xf691b29c
|
||||
// strh r0, [r1]
|
||||
// ldr r1, [sp, #-132] ; 0xffffff7c
|
||||
// ldr r0, [sp, #-128] ; 0xffffff80
|
||||
|
||||
uint32_t b_code; /* b imm */
|
||||
uint8_t *shared_mem;
|
||||
uint64_t *prev_location;
|
||||
@ -115,6 +134,46 @@ gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address, void *from_insn,
|
||||
gpointer *target) {
|
||||
|
||||
UNUSED_PARAMETER(self);
|
||||
UNUSED_PARAMETER(from_address);
|
||||
UNUSED_PARAMETER(start_address);
|
||||
UNUSED_PARAMETER(from_insn);
|
||||
|
||||
if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target))) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
*target =
|
||||
(guint8 *)*target + G_STRUCT_OFFSET(afl_log_code_asm_t, str_r0_sp_rz);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_suppress_init(void) {
|
||||
|
||||
static gboolean initialized = false;
|
||||
if (initialized) { return; }
|
||||
initialized = true;
|
||||
|
||||
GumStalkerObserver *observer = stalker_get_observer();
|
||||
GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer);
|
||||
iface->switch_callback = instrument_coverage_switch;
|
||||
|
||||
coverage_blocks = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
if (coverage_blocks == NULL) {
|
||||
|
||||
FATAL("Failed to g_hash_table_new, errno: %d", errno);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void patch_t3_insn(uint32_t *insn, uint16_t val) {
|
||||
|
||||
uint32_t orig = GUINT32_FROM_LE(*insn);
|
||||
@ -135,14 +194,17 @@ void instrument_coverage_optimize(const cs_insn *instr,
|
||||
guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
|
||||
gsize map_size_pow2;
|
||||
gsize area_offset_ror;
|
||||
GumAddress code_addr = 0;
|
||||
|
||||
// gum_arm64_writer_put_brk_imm(cw, 0x0);
|
||||
|
||||
code_addr = cw->pc;
|
||||
instrument_coverage_suppress_init();
|
||||
|
||||
block_start = GSIZE_TO_POINTER(GUM_ADDRESS(cw->code));
|
||||
|
||||
if (!g_hash_table_add(coverage_blocks, block_start)) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
|
||||
}
|
||||
|
||||
code.code = template;
|
||||
|
||||
g_assert(PAGE_ALIGNED(__afl_area_ptr));
|
||||
@ -211,7 +273,19 @@ void instrument_flush(GumStalkerOutput *output) {
|
||||
|
||||
gpointer instrument_cur(GumStalkerOutput *output) {
|
||||
|
||||
return gum_arm_writer_cur(output->writer.arm);
|
||||
gpointer curr = NULL;
|
||||
|
||||
if (output->encoding == GUM_INSTRUCTION_SPECIAL) {
|
||||
|
||||
curr = gum_thumb_writer_cur(output->writer.thumb);
|
||||
|
||||
} else {
|
||||
|
||||
curr = gum_arm_writer_cur(output->writer.arm);
|
||||
|
||||
}
|
||||
|
||||
return curr;
|
||||
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,45 @@ typedef struct {
|
||||
|
||||
} afl_log_code_asm_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
uint32_t b_imm8; /* br #XX (end) */
|
||||
|
||||
uint32_t restoration_prolog; /* ldp x16, x17, [sp], #0x90 */
|
||||
|
||||
uint32_t stp_x0_x1; /* stp x0, x1, [sp, #-0xa0] */
|
||||
|
||||
uint32_t ldr_x0_p_prev_loc_1; /* ldr x0, #0xXXXX */
|
||||
uint32_t ldr_x1_ptr_x0; /* ldr x1, [x0] */
|
||||
|
||||
uint32_t ldr_x0_p_area_offset; /* ldr x0, #0xXXXX */
|
||||
uint32_t eor_x0_x1_x0; /* eor x0, x1, x0 */
|
||||
uint32_t ldr_x1_p_area_ptr; /* ldr x1, #0xXXXX */
|
||||
uint32_t add_x0_x1_x0; /* add x0, x1, x0 */
|
||||
|
||||
uint32_t ldrb_w1_x0; /* ldrb w1, [x0] */
|
||||
uint32_t add_w1_w1_1; /* add w1, w1, #1 */
|
||||
uint32_t add_w1_w1_w1_lsr_8; /* add x1, x1, x1, lsr #8 */
|
||||
|
||||
uint32_t strb_w1_ptr_x0; /* strb w1, [x0] */
|
||||
|
||||
uint32_t ldr_x0_p_prev_loc_2; /* ldr x0, #0xXXXX */
|
||||
uint32_t ldr_x1_p_area_offset_ror; /* ldr x1, #0xXXXX */
|
||||
uint32_t str_x1_ptr_x0; /* str x1, [x0] */
|
||||
|
||||
uint32_t ldp_x0_x1; /* ldp x0, x1, [sp, #-0xa0] */
|
||||
|
||||
uint32_t b_end; /* skip the data */
|
||||
|
||||
uint64_t area_ptr;
|
||||
uint64_t prev_loc_ptr;
|
||||
uint64_t area_offset;
|
||||
uint64_t area_offset_ror;
|
||||
|
||||
uint8_t end[0];
|
||||
|
||||
} afl_log_code_asm_long_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef union {
|
||||
@ -85,6 +124,13 @@ typedef union {
|
||||
|
||||
} afl_log_code;
|
||||
|
||||
typedef union {
|
||||
|
||||
afl_log_code_asm_long_t code;
|
||||
uint8_t bytes[0];
|
||||
|
||||
} afl_log_code_long;
|
||||
|
||||
static const afl_log_code_asm_t template =
|
||||
{
|
||||
|
||||
@ -119,6 +165,46 @@ static const afl_log_code_asm_t template =
|
||||
|
||||
;
|
||||
|
||||
static const afl_log_code_asm_long_t template_long =
|
||||
{.b_imm8 = 0x1400001a,
|
||||
|
||||
.restoration_prolog = 0xa8c947f0, /* ldp x16, x17, [sp], #0x90 */
|
||||
|
||||
.stp_x0_x1 = 0xa93607e0, /* stp x0, x1, [sp, #-0xa0] */
|
||||
|
||||
.ldr_x0_p_prev_loc_1 = 0x58000220, /* ldr x0, #0xXXXX */
|
||||
.ldr_x1_ptr_x0 = 0xf9400001, /* ldr x1, [x0] */
|
||||
|
||||
.ldr_x0_p_area_offset = 0x58000220, /* ldr x0, #0xXXXX */
|
||||
.eor_x0_x1_x0 = 0xca000020, /* eor x0, x1, x0 */
|
||||
.ldr_x1_p_area_ptr = 0x58000161, /* ldr x1, #0xXXXX */
|
||||
.add_x0_x1_x0 = 0x8b000020, /* add x0, x1, x0 */
|
||||
|
||||
.ldrb_w1_x0 = 0x39400001, /* ldrb w1, [x0] */
|
||||
.add_w1_w1_1 = 0x11000421, /* add w1, w1, #1 */
|
||||
.add_w1_w1_w1_lsr_8 = 0x8b412021, /* add x1, x1, x1, lsr #8 */
|
||||
|
||||
.strb_w1_ptr_x0 = 0x39000001, /* strb w1, [x0] */
|
||||
|
||||
.ldr_x0_p_prev_loc_2 = 0x580000e0, /* ldr x0, #0xXXXX */
|
||||
.ldr_x1_p_area_offset_ror = 0x58000141, /* ldr x1, #0xXXXX */
|
||||
.str_x1_ptr_x0 = 0xf9000001, /* str x1, [x0] */
|
||||
|
||||
.ldp_x0_x1 = 0xa97607e0, /* ldp x0, x1, [sp, #-0xa0] */
|
||||
|
||||
.b_end = 0x14000009, /* skip the data */
|
||||
|
||||
.area_ptr = 0x0,
|
||||
.prev_loc_ptr = 0x0,
|
||||
.area_offset = 0x0,
|
||||
.area_offset_ror = 0x0,
|
||||
|
||||
.end = {}
|
||||
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
return true;
|
||||
@ -156,26 +242,55 @@ static gboolean instrument_is_deterministic(const cs_insn *from_insn) {
|
||||
|
||||
}
|
||||
|
||||
cs_insn *instrument_disassemble(gconstpointer address) {
|
||||
|
||||
csh capstone;
|
||||
cs_insn *insn = NULL;
|
||||
|
||||
cs_open(CS_ARCH_ARM64, GUM_DEFAULT_CS_ENDIAN, &capstone);
|
||||
cs_option(capstone, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
|
||||
cs_disasm(capstone, address, 16, GPOINTER_TO_SIZE(address), 1, &insn);
|
||||
|
||||
cs_close(&capstone);
|
||||
|
||||
return insn;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address,
|
||||
const cs_insn *from_insn,
|
||||
gpointer *target) {
|
||||
gpointer start_address, void *from_insn,
|
||||
gpointer *target) {
|
||||
|
||||
UNUSED_PARAMETER(self);
|
||||
UNUSED_PARAMETER(from_address);
|
||||
UNUSED_PARAMETER(start_address);
|
||||
|
||||
gsize fixup_offset;
|
||||
cs_insn *insn = NULL;
|
||||
gboolean deterministic = FALSE;
|
||||
gsize fixup_offset;
|
||||
|
||||
if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target)) &&
|
||||
!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target + 4))) {
|
||||
!g_hash_table_contains(coverage_blocks,
|
||||
GSIZE_TO_POINTER((guint8 *)*target + 4))) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (instrument_is_deterministic(from_insn)) { return; }
|
||||
insn = instrument_disassemble(from_insn);
|
||||
deterministic = instrument_is_deterministic(insn);
|
||||
cs_free(insn, 1);
|
||||
|
||||
/*
|
||||
* If the branch is deterministic, then we should start execution at the
|
||||
* begining of the block. From here, we will branch and skip the coverage
|
||||
* code and jump right to the target code of the instrumented block.
|
||||
* Otherwise, if the branch is non-deterministic, then we need to branch
|
||||
* part way into the block to where the coverage instrumentation starts.
|
||||
*/
|
||||
if (deterministic) { return; }
|
||||
|
||||
/*
|
||||
* Since each block is prefixed with a restoration prologue, we need to be
|
||||
@ -208,7 +323,7 @@ static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
*/
|
||||
fixup_offset = GUM_RESTORATION_PROLOG_SIZE +
|
||||
G_STRUCT_OFFSET(afl_log_code_asm_t, restoration_prolog);
|
||||
*target += fixup_offset;
|
||||
*target = (guint8 *)*target + fixup_offset;
|
||||
|
||||
}
|
||||
|
||||
@ -237,16 +352,22 @@ static gboolean instrument_coverage_in_range(gssize offset) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_patch_ardp(guint32 *patch, GumAddress insn,
|
||||
static bool instrument_patch_ardp(guint32 *patch, GumAddress insn,
|
||||
GumAddress target) {
|
||||
|
||||
if (!PAGE_ALIGNED(target)) { FATAL("Target not page aligned"); }
|
||||
if (!PAGE_ALIGNED(target)) {
|
||||
|
||||
FWARNF("Target not page aligned");
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
gssize distance = target - (GUM_ADDRESS(insn) & PAGE_MASK);
|
||||
if (!instrument_coverage_in_range(distance)) {
|
||||
|
||||
FATAL("Patch out of range 0x%016lX->0x%016lX = 0x%016lX", insn, target,
|
||||
distance);
|
||||
FVERBOSE("Patch out of range 0x%016lX->0x%016lX = 0x%016lX", insn, target,
|
||||
distance);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@ -254,6 +375,95 @@ static void instrument_patch_ardp(guint32 *patch, GumAddress insn,
|
||||
guint32 imm_high = ((distance >> 14) & 0x7FFFF) << 5;
|
||||
*patch |= imm_low;
|
||||
*patch |= imm_high;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool instrument_write_inline(GumArm64Writer *cw, GumAddress code_addr,
|
||||
guint64 area_offset, gsize area_offset_ror) {
|
||||
|
||||
afl_log_code code = {0};
|
||||
code.code = template;
|
||||
|
||||
/*
|
||||
* Given our map is allocated on a 64KB boundary and our map is a multiple of
|
||||
* 64KB in size, then it should also end on a 64 KB boundary. It is followed
|
||||
* by our previous_pc, so this too should be 64KB aligned.
|
||||
*/
|
||||
g_assert(PAGE_ALIGNED(instrument_previous_pc_addr));
|
||||
g_assert(PAGE_ALIGNED(__afl_area_ptr));
|
||||
|
||||
if (!instrument_patch_ardp(
|
||||
&code.code.adrp_x0_prev_loc1,
|
||||
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc1),
|
||||
GUM_ADDRESS(instrument_previous_pc_addr))) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
code.code.mov_x0_curr_loc |= area_offset << 5;
|
||||
|
||||
if (!instrument_patch_ardp(
|
||||
&code.code.adrp_x1_area_ptr,
|
||||
code_addr + offsetof(afl_log_code, code.adrp_x1_area_ptr),
|
||||
GUM_ADDRESS(__afl_area_ptr))) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (!instrument_patch_ardp(
|
||||
&code.code.adrp_x0_prev_loc2,
|
||||
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc2),
|
||||
GUM_ADDRESS(instrument_previous_pc_addr))) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5);
|
||||
|
||||
if (instrument_suppress) {
|
||||
|
||||
gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
|
||||
|
||||
} else {
|
||||
|
||||
size_t offset = offsetof(afl_log_code, code.stp_x0_x1);
|
||||
gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
|
||||
sizeof(afl_log_code) - offset);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool instrument_write_inline_long(GumArm64Writer *cw, GumAddress code_addr,
|
||||
guint64 area_offset, gsize area_offset_ror) {
|
||||
|
||||
afl_log_code_long code = {0};
|
||||
code.code = template_long;
|
||||
|
||||
code.code.area_ptr = GUM_ADDRESS(__afl_area_ptr);
|
||||
code.code.prev_loc_ptr = GUM_ADDRESS(instrument_previous_pc_addr);
|
||||
code.code.area_offset = area_offset;
|
||||
code.code.area_offset_ror = GUM_ADDRESS(area_offset_ror);
|
||||
|
||||
if (instrument_suppress) {
|
||||
|
||||
gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code_long));
|
||||
|
||||
} else {
|
||||
|
||||
size_t offset = offsetof(afl_log_code_long, code.stp_x0_x1);
|
||||
gum_arm64_writer_put_bytes(cw, &code.bytes[offset],
|
||||
sizeof(afl_log_code_long) - offset);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@ -283,8 +493,10 @@ void instrument_coverage_optimize(const cs_insn *instr,
|
||||
}
|
||||
|
||||
// gum_arm64_writer_put_brk_imm(cw, 0x0);
|
||||
// uint32_t jmp_dot = 0x14000000;
|
||||
// gum_arm64_writer_put_bytes(cw, (guint8 *)&jmp_dot, sizeof(jmp_dot));
|
||||
|
||||
instrument_coverage_suppress_init();
|
||||
if (instrument_suppress) { instrument_coverage_suppress_init(); }
|
||||
|
||||
code_addr = cw->pc;
|
||||
|
||||
@ -304,45 +516,31 @@ void instrument_coverage_optimize(const cs_insn *instr,
|
||||
block_start =
|
||||
GSIZE_TO_POINTER(GUM_ADDRESS(cw->code) - GUM_RESTORATION_PROLOG_SIZE);
|
||||
|
||||
if (!g_hash_table_add(coverage_blocks, block_start)) {
|
||||
if (instrument_suppress) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
if (!g_hash_table_add(coverage_blocks, block_start)) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
code.code = template;
|
||||
|
||||
/*
|
||||
* Given our map is allocated on a 64KB boundary and our map is a multiple of
|
||||
* 64KB in size, then it should also end on a 64 KB boundary. It is followed
|
||||
* by our previous_pc, so this too should be 64KB aligned.
|
||||
*/
|
||||
g_assert(PAGE_ALIGNED(instrument_previous_pc_addr));
|
||||
g_assert(PAGE_ALIGNED(__afl_area_ptr));
|
||||
|
||||
instrument_patch_ardp(
|
||||
&code.code.adrp_x0_prev_loc1,
|
||||
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc1),
|
||||
GUM_ADDRESS(instrument_previous_pc_addr));
|
||||
|
||||
code.code.mov_x0_curr_loc |= area_offset << 5;
|
||||
|
||||
instrument_patch_ardp(
|
||||
&code.code.adrp_x1_area_ptr,
|
||||
code_addr + offsetof(afl_log_code, code.adrp_x1_area_ptr),
|
||||
GUM_ADDRESS(__afl_area_ptr));
|
||||
|
||||
map_size_pow2 = util_log2(__afl_map_size);
|
||||
area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
|
||||
|
||||
instrument_patch_ardp(
|
||||
&code.code.adrp_x0_prev_loc2,
|
||||
code_addr + offsetof(afl_log_code, code.adrp_x0_prev_loc2),
|
||||
GUM_ADDRESS(instrument_previous_pc_addr));
|
||||
code.code = template;
|
||||
|
||||
code.code.mov_x1_curr_loc_shr_1 |= (area_offset_ror << 5);
|
||||
if (!instrument_write_inline(cw, code_addr, area_offset, area_offset_ror)) {
|
||||
|
||||
gum_arm64_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
|
||||
if (!instrument_write_inline_long(cw, code_addr, area_offset,
|
||||
area_offset_ror)) {
|
||||
|
||||
FATAL("Failed to write inline instrumentation");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -171,11 +171,11 @@ void instrument_coverage_optimize_init(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address,
|
||||
const cs_insn *from_insn,
|
||||
gpointer *target) {
|
||||
static void instrument_coverage_switch_insn(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address,
|
||||
const cs_insn *from_insn,
|
||||
gpointer *target) {
|
||||
|
||||
UNUSED_PARAMETER(self);
|
||||
UNUSED_PARAMETER(from_address);
|
||||
@ -224,6 +224,35 @@ static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
|
||||
}
|
||||
|
||||
cs_insn *instrument_disassemble(gconstpointer address) {
|
||||
|
||||
csh capstone;
|
||||
cs_insn *insn = NULL;
|
||||
|
||||
cs_open(CS_ARCH_X86, GUM_CPU_MODE, &capstone);
|
||||
cs_option(capstone, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
|
||||
cs_disasm(capstone, address, 16, GPOINTER_TO_SIZE(address), 1, &insn);
|
||||
|
||||
cs_close(&capstone);
|
||||
|
||||
return insn;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address, void *from_insn,
|
||||
gpointer *target) {
|
||||
|
||||
if (from_insn == NULL) { return; }
|
||||
cs_insn *insn = instrument_disassemble(from_insn);
|
||||
instrument_coverage_switch_insn(self, from_address, start_address, insn,
|
||||
target);
|
||||
cs_free(insn, 1);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_suppress_init(void) {
|
||||
|
||||
static gboolean initialized = false;
|
||||
@ -351,11 +380,15 @@ void instrument_coverage_optimize(const cs_insn *instr,
|
||||
|
||||
}
|
||||
|
||||
instrument_coverage_suppress_init();
|
||||
if (instrument_suppress) {
|
||||
|
||||
if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
|
||||
instrument_coverage_suppress_init();
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -83,11 +83,11 @@ gboolean instrument_is_coverage_optimize_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address,
|
||||
const cs_insn *from_insn,
|
||||
gpointer *target) {
|
||||
static void instrument_coverage_switch_insn(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address,
|
||||
const cs_insn *from_insn,
|
||||
gpointer *target) {
|
||||
|
||||
UNUSED_PARAMETER(self);
|
||||
UNUSED_PARAMETER(from_address);
|
||||
@ -130,6 +130,35 @@ static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
|
||||
}
|
||||
|
||||
cs_insn *instrument_disassemble(gconstpointer address) {
|
||||
|
||||
csh capstone;
|
||||
cs_insn *insn = NULL;
|
||||
|
||||
cs_open(CS_ARCH_X86, GUM_CPU_MODE, &capstone);
|
||||
cs_option(capstone, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
|
||||
cs_disasm(capstone, address, 16, GPOINTER_TO_SIZE(address), 1, &insn);
|
||||
|
||||
cs_close(&capstone);
|
||||
|
||||
return insn;
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_switch(GumStalkerObserver *self,
|
||||
gpointer from_address,
|
||||
gpointer start_address, void *from_insn,
|
||||
gpointer *target) {
|
||||
|
||||
if (from_insn == NULL) { return; }
|
||||
cs_insn *insn = instrument_disassemble(from_insn);
|
||||
instrument_coverage_switch_insn(self, from_address, start_address, insn,
|
||||
target);
|
||||
cs_free(insn, 1);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_coverage_suppress_init(void) {
|
||||
|
||||
static gboolean initialized = false;
|
||||
@ -174,13 +203,17 @@ void instrument_coverage_optimize(const cs_insn *instr,
|
||||
|
||||
code.code = template;
|
||||
|
||||
instrument_coverage_suppress_init();
|
||||
if (instrument_suppress) {
|
||||
|
||||
// gum_x86_writer_put_breakpoint(cw);
|
||||
instrument_coverage_suppress_init();
|
||||
|
||||
if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
|
||||
// gum_x86_writer_put_breakpoint(cw);
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
|
||||
|
||||
FATAL("Failed - g_hash_table_add");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -150,6 +150,12 @@ class Afl {
|
||||
static setInstrumentLibraries() {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_DYNAMIC_LOAD`
|
||||
*/
|
||||
static setInstrumentNoDynamicLoad() {
|
||||
Afl.jsApiSetInstrumentNoDynamicLoad();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
@ -170,6 +176,12 @@ class Afl {
|
||||
static setInstrumentSeed(seed) {
|
||||
Afl.jsApiSetInstrumentSeed(seed);
|
||||
}
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_NO_SUPPRESS`
|
||||
*/
|
||||
static setInstrumentSuppressDisable() {
|
||||
Afl.jsApiSetInstrumentSuppressDisable();
|
||||
}
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
|
||||
*/
|
||||
@ -336,9 +348,11 @@ Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_de
|
||||
Afl.jsApiSetInstrumentInstructions = Afl.jsApiGetFunction("js_api_set_instrument_instructions", "void", []);
|
||||
Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []);
|
||||
Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
|
||||
Afl.jsApiSetInstrumentNoDynamicLoad = Afl.jsApiGetFunction("js_api_set_instrument_no_dynamic_load", "void", []);
|
||||
Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []);
|
||||
Afl.jsApiSetInstrumentRegsFile = Afl.jsApiGetFunction("js_api_set_instrument_regs_file", "void", ["pointer"]);
|
||||
Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
|
||||
Afl.jsApiSetInstrumentSuppressDisable = Afl.jsApiGetFunction("js_api_set_instrument_suppress_disable", "void", []);
|
||||
Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
|
||||
Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
|
||||
Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]);
|
||||
|
@ -18,10 +18,8 @@ static GumScriptScheduler *scheduler;
|
||||
static GMainContext *context;
|
||||
static GMainLoop *main_loop;
|
||||
|
||||
static void js_msg(GumScript *script, const gchar *message, GBytes *data,
|
||||
gpointer user_data) {
|
||||
static void js_msg(const gchar *message, GBytes *data, gpointer user_data) {
|
||||
|
||||
UNUSED_PARAMETER(script);
|
||||
UNUSED_PARAMETER(data);
|
||||
UNUSED_PARAMETER(user_data);
|
||||
FOKF("%s", message);
|
||||
@ -124,8 +122,8 @@ void js_start(void) {
|
||||
main_loop = g_main_loop_new(context, true);
|
||||
g_main_context_push_thread_default(context);
|
||||
|
||||
gum_script_backend_create(backend, "example", source, cancellable, create_cb,
|
||||
&error);
|
||||
gum_script_backend_create(backend, "example", source, NULL, cancellable,
|
||||
create_cb, &error);
|
||||
|
||||
while (g_main_context_pending(context))
|
||||
g_main_context_iteration(context, FALSE);
|
||||
|
@ -156,6 +156,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_instructions(
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void
|
||||
js_api_set_instrument_no_dynamic_load(void) {
|
||||
|
||||
ranges_inst_dynamic_load = FALSE;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
|
||||
void) {
|
||||
|
||||
@ -289,6 +296,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_cache_size(
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void
|
||||
js_api_set_instrument_suppress_disable(void) {
|
||||
|
||||
instrument_suppress = false;
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void js_api_set_js_main_hook(
|
||||
const js_main_hook_t hook) {
|
||||
|
||||
|
@ -17,8 +17,8 @@ static gboolean lib_get_main_module(const GumModuleDetails *details,
|
||||
|
||||
GumDarwinModule **ret = (GumDarwinModule **)user_data;
|
||||
GumDarwinModule *module = gum_darwin_module_new_from_memory(
|
||||
details->path, mach_task_self(), details->range->base_address,
|
||||
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
|
||||
details->path, mach_task_self(), details->range->base_address,
|
||||
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
|
||||
|
||||
FVERBOSE("Found main module: %s", module->name);
|
||||
|
||||
|
@ -197,7 +197,7 @@ static void afl_print_env(void) {
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
void afl_frida_config(void) {
|
||||
|
||||
FOKF(cRED "**********************");
|
||||
FOKF(cRED "* " cYEL "******************" cRED " *");
|
||||
@ -225,9 +225,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
|
||||
js_start();
|
||||
|
||||
/* Initialize */
|
||||
output_init();
|
||||
|
||||
embedded_init();
|
||||
entry_init();
|
||||
instrument_init();
|
||||
@ -240,12 +238,35 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
ranges_init();
|
||||
stats_init();
|
||||
|
||||
/* Start */
|
||||
}
|
||||
|
||||
void afl_frida_run(void) {
|
||||
|
||||
stalker_start();
|
||||
entry_start();
|
||||
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void afl_frida_start(void) {
|
||||
|
||||
afl_frida_config();
|
||||
afl_frida_run();
|
||||
|
||||
}
|
||||
|
||||
typedef void *(*entry_func_t)(size_t a1, size_t a2, size_t a3, size_t a4,
|
||||
size_t a5, size_t a6);
|
||||
|
||||
static void *on_entry(size_t a1, size_t a2, size_t a3, size_t a4, size_t a5,
|
||||
size_t a6) {
|
||||
|
||||
intercept_unhook(GSIZE_TO_POINTER(entry_point));
|
||||
afl_frida_run();
|
||||
entry_func_t entry = (entry_func_t)entry_point;
|
||||
return entry(a1, a2, a3, a4, a5, a6);
|
||||
|
||||
}
|
||||
|
||||
static int on_main(int argc, char **argv, char **envp) {
|
||||
|
||||
int ret;
|
||||
@ -254,7 +275,17 @@ static int on_main(int argc, char **argv, char **envp) {
|
||||
|
||||
intercept_unhook_self();
|
||||
|
||||
afl_frida_start();
|
||||
afl_frida_config();
|
||||
|
||||
if (entry_point == 0) {
|
||||
|
||||
afl_frida_run();
|
||||
|
||||
} else {
|
||||
|
||||
intercept_hook(GSIZE_TO_POINTER(entry_point), on_entry, NULL);
|
||||
|
||||
}
|
||||
|
||||
if (js_main_hook != NULL) {
|
||||
|
||||
|
@ -29,7 +29,6 @@ gboolean prefetch_enable = TRUE;
|
||||
gboolean prefetch_backpatch = TRUE;
|
||||
|
||||
static prefetch_data_t *prefetch_data = NULL;
|
||||
static int prefetch_shm_id = -1;
|
||||
|
||||
static GHashTable *cant_prefetch = NULL;
|
||||
|
||||
|
@ -18,6 +18,7 @@ typedef struct {
|
||||
gboolean ranges_debug_maps = FALSE;
|
||||
gboolean ranges_inst_libs = FALSE;
|
||||
gboolean ranges_inst_jit = FALSE;
|
||||
gboolean ranges_inst_dynamic_load = TRUE;
|
||||
|
||||
static GArray *module_ranges = NULL;
|
||||
static GArray *libs_ranges = NULL;
|
||||
@ -25,6 +26,7 @@ static GArray *jit_ranges = NULL;
|
||||
static GArray *include_ranges = NULL;
|
||||
static GArray *exclude_ranges = NULL;
|
||||
static GArray *ranges = NULL;
|
||||
static GArray *whole_memory_ranges = NULL;
|
||||
|
||||
static void convert_address_token(gchar *token, GumMemoryRange *range) {
|
||||
|
||||
@ -387,6 +389,21 @@ static GArray *collect_jit_ranges(void) {
|
||||
|
||||
}
|
||||
|
||||
static GArray *collect_whole_mem_ranges(void) {
|
||||
|
||||
GArray *result;
|
||||
GumMemoryRange range;
|
||||
result = g_array_new(false, false, sizeof(GumMemoryRange));
|
||||
|
||||
range.base_address = 0;
|
||||
range.size = G_MAXULONG;
|
||||
|
||||
g_array_append_val(result, range);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra,
|
||||
GumMemoryRange *rb) {
|
||||
|
||||
@ -574,11 +591,17 @@ void ranges_config(void) {
|
||||
if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; }
|
||||
if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; }
|
||||
if (getenv("AFL_FRIDA_INST_JIT") != NULL) { ranges_inst_jit = TRUE; }
|
||||
if (getenv("AFL_FRIDA_INST_NO_DYNAMIC_LOAD") != NULL) {
|
||||
|
||||
ranges_inst_dynamic_load = FALSE;
|
||||
|
||||
}
|
||||
|
||||
if (ranges_debug_maps) { ranges_print_debug_maps(); }
|
||||
|
||||
include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
|
||||
exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
|
||||
whole_memory_ranges = collect_whole_mem_ranges();
|
||||
|
||||
}
|
||||
|
||||
@ -628,10 +651,20 @@ void ranges_init(void) {
|
||||
print_ranges("step4", step4);
|
||||
|
||||
/*
|
||||
* After step4, we have the total ranges to be instrumented, we now subtract
|
||||
* that from the original ranges of the modules to configure stalker.
|
||||
* After step 4 we have the total ranges to be instrumented, we now subtract
|
||||
* that either from the original ranges of the modules or from the whole
|
||||
* memory if AFL_INST_NO_DYNAMIC_LOAD to configure the stalker.
|
||||
*/
|
||||
step5 = subtract_ranges(module_ranges, step4);
|
||||
if (ranges_inst_dynamic_load) {
|
||||
|
||||
step5 = subtract_ranges(module_ranges, step4);
|
||||
|
||||
} else {
|
||||
|
||||
step5 = subtract_ranges(whole_memory_ranges, step4);
|
||||
|
||||
}
|
||||
|
||||
print_ranges("step5", step5);
|
||||
|
||||
ranges = merge_ranges(step5);
|
||||
|
@ -13,6 +13,7 @@ void starts_arch_init(void) {
|
||||
|
||||
void stats_write_arch(stats_data_t *data) {
|
||||
|
||||
UNUSED_PARAMETER(data);
|
||||
FFATAL("Stats not supported on this architecture");
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ PWD:=$(shell pwd)/
|
||||
ROOT:=$(PWD)../../../
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
TEST_CMPLOG_BASENAME=compcovtest
|
||||
TEST_CMPLOG_SRC=$(PWD)cmplog.c
|
||||
TEST_CMPLOG_OBJ=$(BUILD_DIR)compcovtest
|
||||
TEST_CMPLOG_OBJ=$(BUILD_DIR)$(TEST_CMPLOG_BASENAME)
|
||||
|
||||
TEST_BIN:=$(PWD)../../build/test
|
||||
|
||||
@ -13,7 +14,7 @@ CMP_LOG_INPUT:=$(TEST_DATA_DIR)in
|
||||
QEMU_OUT:=$(BUILD_DIR)qemu-out
|
||||
FRIDA_OUT:=$(BUILD_DIR)frida-out
|
||||
|
||||
.PHONY: all 32 clean qemu frida frida-nocmplog format
|
||||
.PHONY: all 32 clean qemu frida frida-nocmplog frida-unprefixedpath format
|
||||
|
||||
all: $(TEST_CMPLOG_OBJ)
|
||||
make -C $(ROOT)frida_mode/
|
||||
@ -64,6 +65,18 @@ frida-nocmplog: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
-- \
|
||||
$(TEST_CMPLOG_OBJ) @@
|
||||
|
||||
|
||||
frida-unprefixedpath: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
PATH=$(BUILD_DIR) $(ROOT)afl-fuzz \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-c 0 \
|
||||
-l 3AT \
|
||||
-Z \
|
||||
-- \
|
||||
$(TEST_CMPLOG_BASENAME) @@
|
||||
|
||||
debug: $(TEST_CMPLOG_OBJ) $(CMP_LOG_INPUT)
|
||||
gdb \
|
||||
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
|
||||
|
@ -19,6 +19,9 @@ frida:
|
||||
frida-nocmplog:
|
||||
@gmake frida-nocmplog
|
||||
|
||||
frida-unprefixedpath:
|
||||
@gmake frida-unprefixedpath
|
||||
|
||||
format:
|
||||
@gmake format
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// Author: Mateusz Jurczyk (mjurczyk@google.com)
|
||||
//
|
||||
// Copyright 2019-2022 Google LLC
|
||||
// Copyright 2019-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
@ -3,7 +3,7 @@
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2022 AFLplusplus Project. All rights reserved.
|
||||
Copyright 2019-2023 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:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user