aboutsummaryrefslogtreecommitdiff
path: root/custom_mutators
diff options
context:
space:
mode:
authorAlexander Shvedov <60114847+a-shvedov@users.noreply.github.com>2024-05-30 10:43:01 +0300
committerGitHub <noreply@github.com>2024-05-30 10:43:01 +0300
commitf8a5f1cd9ea907654f42fa06ce6b6bfd4b8c1b13 (patch)
tree7aec2a095a30ed609ce96f85ec3c4e0a8b8eb74c /custom_mutators
parent629edb1e78d791894ce9ee6d53259f95fe1a29af (diff)
parente7d871c8bf64962a658e447b90a1a3b43aaddc28 (diff)
downloadafl++-f8a5f1cd9ea907654f42fa06ce6b6bfd4b8c1b13.tar.gz
Merge branch 'AFLplusplus:stable' into stable
Diffstat (limited to 'custom_mutators')
-rw-r--r--custom_mutators/README.md11
-rw-r--r--custom_mutators/aflpp/Makefile10
-rw-r--r--custom_mutators/aflpp/README.md8
-rw-r--r--custom_mutators/aflpp/aflpp.c90
-rw-r--r--custom_mutators/aflpp/standalone/Makefile10
-rw-r--r--custom_mutators/aflpp/standalone/README.md10
-rw-r--r--custom_mutators/aflpp/standalone/aflpp-standalone.c163
-rw-r--r--custom_mutators/aflpp_tritondse/README.md9
-rw-r--r--custom_mutators/aflpp_tritondse/aflpp_tritondse.py80
-rw-r--r--custom_mutators/examples/README.md3
-rw-r--r--custom_mutators/examples/custom_post_run.c53
-rw-r--r--custom_mutators/examples/elf_header_mutator.c679
-rw-r--r--custom_mutators/examples/example.py5
-rw-r--r--custom_mutators/grammar_mutator/GRAMMAR_VERSION2
m---------custom_mutators/grammar_mutator/grammar_mutator0
-rw-r--r--custom_mutators/symcc/README.md2
-rw-r--r--custom_mutators/symqemu/Makefile14
-rw-r--r--custom_mutators/symqemu/README.md19
-rw-r--r--custom_mutators/symqemu/symqemu.c424
19 files changed, 1581 insertions, 11 deletions
diff --git a/custom_mutators/README.md b/custom_mutators/README.md
index a5a572c0..2d1220b3 100644
--- a/custom_mutators/README.md
+++ b/custom_mutators/README.md
@@ -70,14 +70,17 @@ requires cmake (among other things):
### libprotobuf Mutators
-There are two WIP protobuf projects, that require work to be working though:
+There are three WIP protobuf projects, that require work to be working though:
+
+ASN.1 example:
+[https://github.com/airbus-seclab/AFLplusplus-blogpost/tree/main/src/mutator](https://github.com/airbus-seclab/AFLplusplus-blogpost/tree/main/src/mutator)
transforms protobuf raw:
-https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator
+[https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator](https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator)
has a transform function you need to fill for your protobuf format, however
needs to be ported to the updated AFL++ custom mutator API (not much work):
-https://github.com/thebabush/afl-libprotobuf-mutator
+[https://github.com/thebabush/afl-libprotobuf-mutator](https://github.com/thebabush/afl-libprotobuf-mutator)
same as above but is for current AFL++:
-https://github.com/P1umer/AFLplusplus-protobuf-mutator
+[https://github.com/P1umer/AFLplusplus-protobuf-mutator](https://github.com/P1umer/AFLplusplus-protobuf-mutator) \ No newline at end of file
diff --git a/custom_mutators/aflpp/Makefile b/custom_mutators/aflpp/Makefile
new file mode 100644
index 00000000..8efdf3e4
--- /dev/null
+++ b/custom_mutators/aflpp/Makefile
@@ -0,0 +1,10 @@
+
+CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic
+
+all: aflpp-mutator.so
+
+aflpp-mutator.so: aflpp.c
+ $(CC) $(CFLAGS) -I../../include -I. -shared -o aflpp-mutator.so aflpp.c ../../src/afl-performance.c
+
+clean:
+ rm -f *.o *~ *.so core
diff --git a/custom_mutators/aflpp/README.md b/custom_mutators/aflpp/README.md
new file mode 100644
index 00000000..04d605c1
--- /dev/null
+++ b/custom_mutators/aflpp/README.md
@@ -0,0 +1,8 @@
+# custum mutator: AFL++
+
+this is the AFL++ havoc mutator as a custom mutator module for AFL++.
+
+just type `make` to build
+
+```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/aflpp/aflpp-mutator.so afl-fuzz ...```
+
diff --git a/custom_mutators/aflpp/aflpp.c b/custom_mutators/aflpp/aflpp.c
new file mode 100644
index 00000000..0b236f76
--- /dev/null
+++ b/custom_mutators/aflpp/aflpp.c
@@ -0,0 +1,90 @@
+#include "afl-fuzz.h"
+#include "afl-mutations.h"
+
+typedef struct my_mutator {
+
+ afl_state_t *afl;
+ u8 *buf;
+ u32 buf_size;
+
+} my_mutator_t;
+
+my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
+
+ (void)seed;
+
+ my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
+ if (!data) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ }
+
+ if ((data->buf = malloc(MAX_FILE)) == NULL) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ } else {
+
+ data->buf_size = MAX_FILE;
+
+ }
+
+ data->afl = afl;
+
+ return data;
+
+}
+
+/* here we run the AFL++ mutator, which is the best! */
+
+size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
+ u8 **out_buf, uint8_t *add_buf, size_t add_buf_size,
+ size_t max_size) {
+
+ if (max_size > data->buf_size) {
+
+ u8 *ptr = realloc(data->buf, max_size);
+
+ if (ptr) {
+
+ return 0;
+
+ } else {
+
+ data->buf = ptr;
+ data->buf_size = max_size;
+
+ }
+
+ }
+
+ u32 havoc_steps = 1 + rand_below(data->afl, 16);
+
+ /* set everything up, costly ... :( */
+ memcpy(data->buf, buf, buf_size);
+
+ /* the mutation */
+ u32 out_buf_len = afl_mutate(data->afl, data->buf, buf_size, havoc_steps,
+ false, true, add_buf, add_buf_size, max_size);
+
+ /* return size of mutated data */
+ *out_buf = data->buf;
+ return out_buf_len;
+
+}
+
+/**
+ * Deinitialize everything
+ *
+ * @param data The data ptr from afl_custom_init
+ */
+void afl_custom_deinit(my_mutator_t *data) {
+
+ free(data->buf);
+ free(data);
+
+}
+
diff --git a/custom_mutators/aflpp/standalone/Makefile b/custom_mutators/aflpp/standalone/Makefile
new file mode 100644
index 00000000..f1e99445
--- /dev/null
+++ b/custom_mutators/aflpp/standalone/Makefile
@@ -0,0 +1,10 @@
+
+CFLAGS = -O3 -funroll-loops -fPIC
+
+all: aflpp-standalone
+
+aflpp-standalone: aflpp-standalone.c
+ $(CC) $(CFLAGS) -I../../../include -I. -o aflpp-standalone aflpp-standalone.c ../../../src/afl-performance.c
+
+clean:
+ rm -f *.o *~ aflpp-standalone core
diff --git a/custom_mutators/aflpp/standalone/README.md b/custom_mutators/aflpp/standalone/README.md
new file mode 100644
index 00000000..a1ffb5f9
--- /dev/null
+++ b/custom_mutators/aflpp/standalone/README.md
@@ -0,0 +1,10 @@
+# AFL++ standalone mutator
+
+this is the AFL++ havoc mutator as a standalone mutator
+
+just type `make` to build.
+
+```
+aflpp-standalone inputfile outputfile [splicefile]
+```
+
diff --git a/custom_mutators/aflpp/standalone/aflpp-standalone.c b/custom_mutators/aflpp/standalone/aflpp-standalone.c
new file mode 100644
index 00000000..3a2cbc2f
--- /dev/null
+++ b/custom_mutators/aflpp/standalone/aflpp-standalone.c
@@ -0,0 +1,163 @@
+#include "afl-fuzz.h"
+#include "afl-mutations.h"
+
+typedef struct my_mutator {
+
+ afl_state_t *afl;
+ u8 *buf;
+ u32 buf_size;
+
+} my_mutator_t;
+
+my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
+
+ (void)seed;
+
+ my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
+ if (!data) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ }
+
+ if ((data->buf = malloc(1024*1024)) == NULL) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ } else {
+
+ data->buf_size = 1024*1024;
+
+ }
+
+ /* fake AFL++ state */
+ data->afl = calloc(1, sizeof(afl_state_t));
+ data->afl->queue_cycle = 1;
+ data->afl->fsrv.dev_urandom_fd = open("/dev/urandom", O_RDONLY);
+ if (data->afl->fsrv.dev_urandom_fd < 0) { PFATAL("Unable to open /dev/urandom"); }
+ rand_set_seed(data->afl, getpid());
+
+ return data;
+
+}
+
+/* here we run the AFL++ mutator, which is the best! */
+
+size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
+ u8 **out_buf, uint8_t *add_buf, size_t add_buf_size,
+ size_t max_size) {
+
+ if (max_size > data->buf_size) {
+
+ u8 *ptr = realloc(data->buf, max_size);
+
+ if (ptr) {
+
+ return 0;
+
+ } else {
+
+ data->buf = ptr;
+ data->buf_size = max_size;
+
+ }
+
+ }
+
+ u32 havoc_steps = 1 + rand_below(data->afl, 16);
+
+ /* set everything up, costly ... :( */
+ memcpy(data->buf, buf, buf_size);
+
+ /* the mutation */
+ u32 out_buf_len = afl_mutate(data->afl, data->buf, buf_size, havoc_steps,
+ false, true, add_buf, add_buf_size, max_size);
+
+ /* return size of mutated data */
+ *out_buf = data->buf;
+ return out_buf_len;
+
+}
+
+int main(int argc, char *argv[]) {
+
+ if (argc > 1 && strncmp(argv[1], "-h", 2) == 0) {
+ printf("Syntax: %s [-v] [inputfile [outputfile [splicefile]]]\n\n", argv[0]);
+ printf("Reads a testcase from stdin when no input file (or '-') is specified,\n");
+ printf("mutates according to AFL++'s mutation engine, and write to stdout when '-' or\n");
+ printf("no output filename is given. As an optional third parameter you can give a file\n");
+ printf("for splicing. Maximum input and output length is 1MB.\n");
+ printf("The -v verbose option prints debug output to stderr.\n");
+ return 0;
+ }
+
+ FILE *in = stdin, *out = stdout, *splice = NULL;
+ unsigned char *inbuf = malloc(1024 * 1024), *outbuf, *splicebuf = NULL;
+ int verbose = 0, splicelen = 0;
+
+ if (argc > 1 && strcmp(argv[1], "-v") == 0) {
+ verbose = 1;
+ argc--;
+ argv++;
+ fprintf(stderr, "Verbose active\n");
+ }
+
+ my_mutator_t *data = afl_custom_init(NULL, 0);
+
+ if (argc > 1 && strcmp(argv[1], "-") != 0) {
+ if ((in = fopen(argv[1], "r")) == NULL) {
+ perror(argv[1]);
+ return -1;
+ }
+ if (verbose) fprintf(stderr, "Input: %s\n", argv[1]);
+ }
+
+ size_t inlen = fread(inbuf, 1, 1024*1024, in);
+
+ if (!inlen) {
+ fprintf(stderr, "Error: empty file %s\n", argv[1] ? argv[1] : "stdin");
+ return -1;
+ }
+
+ if (argc > 2 && strcmp(argv[2], "-") != 0) {
+ if ((out = fopen(argv[2], "w")) == NULL) {
+ perror(argv[2]);
+ return -1;
+ }
+ if (verbose) fprintf(stderr, "Output: %s\n", argv[2]);
+ }
+
+ if (argc > 3) {
+ if ((splice = fopen(argv[3], "r")) == NULL) {
+ perror(argv[3]);
+ return -1;
+ }
+ if (verbose) fprintf(stderr, "Splice: %s\n", argv[3]);
+ splicebuf = malloc(1024*1024);
+ size_t splicelen = fread(splicebuf, 1, 1024*1024, splice);
+ if (!splicelen) {
+ fprintf(stderr, "Error: empty file %s\n", argv[3]);
+ return -1;
+ }
+ if (verbose) fprintf(stderr, "Mutation splice length: %zu\n", splicelen);
+ }
+
+ if (verbose) fprintf(stderr, "Mutation input length: %zu\n", inlen);
+ unsigned int outlen = afl_custom_fuzz(data, inbuf, inlen, &outbuf, splicebuf, splicelen, 1024*1024);
+
+ if (outlen == 0 || !outbuf) {
+ fprintf(stderr, "Error: no mutation data returned.\n");
+ return -1;
+ }
+
+ if (verbose) fprintf(stderr, "Mutation output length: %u\n", outlen);
+
+ if (fwrite(outbuf, 1, outlen, out) != outlen) {
+ fprintf(stderr, "Warning: incomplete write.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/custom_mutators/aflpp_tritondse/README.md b/custom_mutators/aflpp_tritondse/README.md
index 8a5dd02b..033655d2 100644
--- a/custom_mutators/aflpp_tritondse/README.md
+++ b/custom_mutators/aflpp_tritondse/README.md
@@ -10,8 +10,13 @@
../../afl-cc -o ../../test-instr ../../test-instr.c
mkdir -p in
echo aaaa > in/in
-TRITON_DSE_TARGET=../../test-instr AFL_CUSTOM_MUTATOR_ONLY=1 AFL_SYNC_TIME=1 AFL_PYTHON_MODULE=aflpp_tritondse PYTHONPATH=. ../../afl-fuzz -i in -o out -- ../../test-instr
+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.
+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`)
diff --git a/custom_mutators/aflpp_tritondse/aflpp_tritondse.py b/custom_mutators/aflpp_tritondse/aflpp_tritondse.py
index e0219f0b..58739696 100644
--- a/custom_mutators/aflpp_tritondse/aflpp_tritondse.py
+++ b/custom_mutators/aflpp_tritondse/aflpp_tritondse.py
@@ -22,14 +22,17 @@ 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:
@@ -47,6 +50,59 @@ def pre_exec_hook(se: SymbolicExecutor, state: ProcessState):
# 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
@@ -64,6 +120,10 @@ def init(seed):
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'
@@ -104,7 +164,7 @@ def init(seed):
format = SeedFormat.COMPOSITE
# Now set up TritonDSE
config = Config(coverage_strategy = CoverageStrategy.PATH,
- debug = is_debug,
+ # debug = is_debug,
pipe_stdout = is_debug,
pipe_stderr = is_debug,
execution_timeout = 1,
@@ -115,10 +175,16 @@ def init(seed):
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):
-# return b""
+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):
@@ -141,8 +207,14 @@ def queue_new_entry(filename_new_queue, filename_orig_queue):
dse.add_input_seed(seed)
# Start exploration!
#dse.step()
- dse.explore()
+ #dse.explore()
pass
+
+# we simulate just doing one single fuzz in the custom mutator
+def fuzz_count(buf):
+ return 1
+
+
def splice_optout():
pass
diff --git a/custom_mutators/examples/README.md b/custom_mutators/examples/README.md
index 655f7a5e..112db243 100644
--- a/custom_mutators/examples/README.md
+++ b/custom_mutators/examples/README.md
@@ -33,3 +33,6 @@ like surgical_havoc_mutate() that allow to perform a randomly chosen
mutation from a subset of the havoc mutations.
If you do so, you have to specify -I /path/to/AFLplusplus/include when
compiling.
+
+elf_header_mutator.c - example ELF header mutator based on
+ [LibGolf](https://github.com/xcellerator/libgolf/)
diff --git a/custom_mutators/examples/custom_post_run.c b/custom_mutators/examples/custom_post_run.c
new file mode 100644
index 00000000..828216ea
--- /dev/null
+++ b/custom_mutators/examples/custom_post_run.c
@@ -0,0 +1,53 @@
+//
+// This is an example on how to use afl_custom_post_run
+// It executes custom code each time after AFL++ executes the target
+//
+// cc -O3 -fPIC -shared -g -o custom_post_run.so -I../../include custom_post_run.c
+// cd ../..
+// afl-cc -o test-instr test-instr.c
+// AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/examples/custom_post_run.so \
+// afl-fuzz -i in -o out -- ./test-instr -f /tmp/foo
+//
+
+
+#include "afl-fuzz.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.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_post_run(my_mutator_t *data) {
+
+ printf("hello from afl_custom_post_run\n");
+ return;
+}
+
+
+void afl_custom_deinit(my_mutator_t *data) {
+
+ free(data);
+
+} \ No newline at end of file
diff --git a/custom_mutators/examples/elf_header_mutator.c b/custom_mutators/examples/elf_header_mutator.c
new file mode 100644
index 00000000..b985257a
--- /dev/null
+++ b/custom_mutators/examples/elf_header_mutator.c
@@ -0,0 +1,679 @@
+/*
+ AFL++ Custom Mutator for ELF Headers
+ Written by @echel0n <melih.sahin@protonmail.com>
+ based on libgolf.h by @xcellerator
+ $ gcc -O3 -fPIC -shared -o elf_mutator.so -I ~/AFLplusplus/include/
+ */
+#include "afl-fuzz.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <linux/elf.h>
+
+/* EI_ABIVERSION isn't used anymore and elf.h defines EI_PAD to be 0x09 */
+#define EI_ABIVERSION 0x08
+#define EI_PAD 0x09
+/* Define the Architecture and ISA constants to match those in <linux/elf.h> */
+#define X86_64 EM_X86_64
+#define ARM32 EM_ARM
+#define AARCH64 EM_AARCH64
+#define uchar unsigned char
+#define DATA_SIZE 0x100
+
+/*
+ * The ELF and Program headers are different sizes depending on 32- and 64-bit
+ * architectures
+ * taken from libgolf.h
+ */
+#define EHDR_T(x) Elf##x##_Ehdr
+#define PHDR_T(x) Elf##x##_Phdr
+#define EHDR(x) ehdr##x
+#define PHDR(x) phdr##x
+#define GET_EHDR(x) (&(elf_ptr->EHDR(x)));
+#define GET_PHDR(x) (&(elf_ptr->PHDR(x)));
+#define REF_EHDR(b, x) ((Elf##b##_Ehdr *)ehdr)->x
+#define REF_PHDR(b, x) ((Elf##b##_Phdr *)phdr)->x
+int ehdr_size;
+int phdr_size;
+/*
+ * This struct holds the bytes that will be executed, and the size.
+ */
+typedef struct text_segment {
+
+ size_t text_size;
+ unsigned char *text_segment;
+
+} TextSegment;
+
+// example shellcode that exits
+// taken from libgolf.h
+unsigned char buf[] = {0xb0, 0x3c, 0x31, 0xff, 0x0f, 0x05};
+
+/*
+ * This is the raw ELF file
+ * - EHDR(xx) is the ELF header
+ * - PHDR(xx) is the program header
+ * - text is the text segment
+ * - filename is the name of the golf'd binary
+ * - isa is the target architecture (X86_64, ARM32, AARCH64)
+ * taken from libgolf.h
+ */
+typedef struct rawbinary_t {
+
+ EHDR_T(32) EHDR(32);
+ PHDR_T(32) PHDR(32);
+ EHDR_T(64) EHDR(64);
+ PHDR_T(64) PHDR(64);
+ TextSegment text;
+ char *filename;
+ int isa;
+
+} RawBinary;
+
+/*
+ * Copy an E_IDENT array into the corresponding fields in the ELF header
+ * Called by populate_ehdr()
+ * taken from libgolf.h
+ */
+int populate_e_ident(RawBinary *elf_ptr, unsigned char e_ident[]) {
+
+ int i;
+ /* Depending on whether the target ISA is 32- or 64-bit, set e_ident */
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ case AARCH64:
+ for (i = 0; i < EI_NIDENT; i++)
+ elf_ptr->EHDR(64).e_ident[i] = e_ident[i];
+ break;
+ case ARM32:
+ for (i = 0; i < EI_NIDENT; i++)
+ elf_ptr->EHDR(32).e_ident[i] = e_ident[i];
+ break;
+ default:
+ exit(1);
+
+ }
+
+ return 0;
+
+}
+
+/*
+ * Copy bytes from buf[] array into text_segment in ELF struct
+ * taken from libgolf.h
+ */
+int copy_text_segment(RawBinary *elf_ptr, unsigned char buf[], int text_size) {
+
+ int i;
+
+ /* Set size of text segment and allocate the buffer */
+ elf_ptr->text.text_size = text_size;
+ elf_ptr->text.text_segment =
+ malloc(elf_ptr->text.text_size * sizeof(unsigned char));
+
+ /* Copy the bytes into the text segment buffer */
+ for (i = 0; i < elf_ptr->text.text_size; i++) {
+
+ elf_ptr->text.text_segment[i] = buf[i];
+
+ }
+
+}
+
+/*
+ * Populate the ELF Header with sane values
+ * Returns a pointer to an EHDR struct
+ * taken from libgolf.h
+ */
+void *populate_ehdr(RawBinary *elf_ptr) {
+
+ /*
+ * Set ehdr_size and phdr_size. Determined by whether target ISA is 32- or
+ * 64-bit.
+ */
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ case AARCH64:
+ ehdr_size = sizeof(EHDR_T(64));
+ phdr_size = sizeof(PHDR_T(64));
+ break;
+ case ARM32:
+ ehdr_size = sizeof(EHDR_T(32));
+ phdr_size = sizeof(PHDR_T(32));
+ break;
+ default:
+ exit(1);
+
+ };
+
+ /* Start with the E_IDENT area at the top of the file */
+ unsigned char e_ident[EI_NIDENT] = {0};
+
+ /* Magic Bytes */
+ e_ident[EI_MAG0] = 0x7F;
+ e_ident[EI_MAG1] = 0x45; // E
+ e_ident[EI_MAG2] = 0x4C; // L
+ e_ident[EI_MAG3] = 0x46; // F
+
+ /*
+ * EI_CLASS denotes the architecture:
+ * ELFCLASS32: 0x01
+ * ELFCLASS64: 0x02
+ */
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ case AARCH64:
+ e_ident[EI_CLASS] = ELFCLASS64;
+ break;
+ case ARM32:
+ e_ident[EI_CLASS] = ELFCLASS32;
+ break;
+ default:
+ exit(1);
+
+ }
+
+ /*
+ * EI_DATA denotes the endianness:
+ * ELFDATA2LSB: 0x01
+ * ELFDATA2MSB: 0x02
+ */
+ e_ident[EI_DATA] = ELFDATA2LSB;
+
+ /* EI_VERSION is always 0x01 */
+ e_ident[EI_VERSION] = EV_CURRENT;
+
+ /*
+ * EI_OSABI defines the target OS. Ignored by most modern ELF parsers.
+ */
+ e_ident[EI_OSABI] = ELFOSABI_NONE;
+
+ /* EI_ABIVERSION was for sub-classification. Un-defined since Linux 2.6 */
+ e_ident[EI_ABIVERSION] = 0x00;
+
+ /* EI_PAD is currently unused */
+ e_ident[EI_PAD] = 0x00;
+
+ /* Copy the E_IDENT section to the ELF struct */
+ populate_e_ident(elf_ptr, e_ident);
+
+ /*
+ * The remainder of the ELF header following E_IDENT follows.
+ *
+ * ehdr is a pointer to either an Elf32_Edhr, or Elf64_Ehdr struct.
+ */
+ void *ehdr = NULL;
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ case AARCH64:
+ ehdr = (&(elf_ptr->EHDR(64)));
+ break;
+ case ARM32:
+ ehdr = (&(elf_ptr->EHDR(32)));
+ break;
+ default:
+ exit(1);
+
+ }
+
+ /*
+ * Depending on whether the ISA is 32- or 64-bit determines the size of
+ * many of the fields in the ELF Header. This switch case deals with it.
+ */
+ switch (elf_ptr->isa) {
+
+ // 64-Bit ISAs
+ case X86_64:
+ case AARCH64:
+ /*
+ * e_type specifies what kind of ELF file this is:
+ * ET_NONE: 0x00 // Unknown Type
+ * ET_REL: 0x01 // Relocatable
+ * ET_EXEC: 0x02 // Executable File
+ * ET_DYN: 0x03 // Shared Object
+ * ET_CORE: 0x04 // Core Dump
+ */
+ REF_EHDR(64, e_type) = ET_EXEC; // 0x0002
+
+ /* e_machine specifies the target ISA */
+ REF_EHDR(64, e_machine) = elf_ptr->isa;
+
+ /* e_version is always set of 0x01 for the original ELF spec */
+ REF_EHDR(64, e_version) = EV_CURRENT; // 0x00000001
+
+ /*
+ * e_entry is the memory address of the entry point
+ * Set by set_entry_point() after p_vaddr is set in the phdr
+ */
+ REF_EHDR(64, e_entry) = 0x0;
+
+ /*
+ * e_phoff points to the start of the program header, which
+ * immediately follows the ELF header
+ */
+ REF_EHDR(64, e_phoff) = ehdr_size;
+
+ /* e_shoff points to the start of the section header table */
+ REF_EHDR(64, e_shoff) = 0x00;
+
+ /* e_flags is architecture dependent */
+ REF_EHDR(64, e_flags) = 0x0;
+
+ /* e_ehsize contains the size of the ELF header */
+ REF_EHDR(64, e_ehsize) = ehdr_size;
+
+ /* e_phentsize is the size of the program header */
+ REF_EHDR(64, e_phentsize) = phdr_size;
+
+ /*
+ * e_phnum contains the number of entries in the program header
+ * e_phnum * e_phentsize = size of program header table
+ */
+ REF_EHDR(64, e_phnum) = 0x1;
+
+ /* e_shentsize contains the size of a section header entry */
+ REF_EHDR(64, e_shentsize) = 0x0;
+
+ /*
+ * e_shnum contains the number of entries in the section header
+ * e_shnum * e_shentsize = size of section header table
+ */
+ REF_EHDR(64, e_shnum) = 0x0;
+
+ /*
+ * e_shstrndx contains the index of the section header table that
+ * contains the section names
+ */
+ REF_EHDR(64, e_shstrndx) = 0x0;
+
+ break;
+ // 32-Bit ISAs
+ case ARM32:
+ /*
+ * e_type specifies what kind of ELF file this is:
+ * ET_NONE: 0x00 // Unknown Type
+ * ET_REL: 0x01 // Relocatable
+ * ET_EXEC: 0x02 // Executable File
+ * ET_DYN: 0x03 // Shared Object
+ * ET_CORE: 0x04 // Core Dump
+ */
+ REF_EHDR(32, e_type) = ET_EXEC; // 0x0002
+
+ /* e_machine specifies the target ISA */
+ REF_EHDR(32, e_machine) = elf_ptr->isa;
+
+ /* e_version is always set of 0x01 for the original ELF spec */
+ REF_EHDR(32, e_version) = EV_CURRENT; // 0x00000001
+
+ /*
+ * e_entry is the memory address of the entry point
+ * Set by set_entry_point() after p_vaddr is set in the phdr
+ */
+ REF_EHDR(32, e_entry) = 0x0;
+
+ /*
+ * e_phoff points to the start of the program header, which
+ * immediately follows the ELF header
+ */
+ REF_EHDR(32, e_phoff) = ehdr_size;
+
+ /* e_shoff points to the start of the section header table */
+ REF_EHDR(32, e_shoff) = 0x0i;
+
+ /* e_flags is architecture dependent */
+ REF_EHDR(32, e_flags) = 0x0;
+
+ /* e_ehsize contains the size of the ELF header */
+ REF_EHDR(32, e_ehsize) = ehdr_size;
+
+ /* e_phentsize is the size of the program header */
+ REF_EHDR(32, e_phentsize) = phdr_size;
+
+ /*
+ * e_phnum contains the number of entries in the program header
+ * e_phnum * e_phentsize = size of program header table
+ */
+ REF_EHDR(32, e_phnum) = 0x1;
+
+ /* e_shentsize contains the size of a section header entry */
+ REF_EHDR(32, e_shentsize) = 0x0;
+
+ /*
+ * e_shnum contains the number of entries in the section header
+ * e_shnum * e_shentsize = size of section header table
+ */
+ REF_EHDR(32, e_shnum) = 0x0;
+
+ /*
+ * e_shstrndx contains the index of the section header table that
+ * contains the section names
+ */
+ REF_EHDR(32, e_shnum) = 0x0;
+
+ break;
+
+ }
+
+ return ehdr;
+
+}
+
+/*
+ * Populate the program headers with sane values
+ * Returns a pointer to a PHDR struct
+ * taken from libgolf.h
+ */
+void *populate_phdr(RawBinary *elf_ptr) {
+
+ /*
+ * All offsets are relative to the start of the program header (0x40)
+ *
+ * phdr is a pointer to either an Elf32_Phdr, or Elf64_Phdr struct.
+ */
+ void *phdr = NULL;
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ case AARCH64:
+ phdr = (&(elf_ptr->PHDR(64)));
+ break;
+ case ARM32:
+ phdr = (&(elf_ptr->PHDR(32)));
+ break;
+ default:
+ exit(1);
+
+ }
+
+ /*
+ * Depending on whether the ISA is 32- or 64-bit determines the size of
+ * many of the fields in the Progra Header. This switch case deals with it.
+ */
+ switch (elf_ptr->isa) {
+
+ // 64-Bit ISAs
+ case X86_64:
+ case AARCH64:
+ /*
+ * p_type identifies what type of segment this is
+ * PT_NULL: 0x0 // Unused
+ * PT_LOAD: 0x1 // Loadable Segment
+ * PT_DYNAMIC: 0x2 // Dynamic Linker Information
+ * PT_INTERP: 0x3 // Interpreter Information
+ * PT_NOTE: 0x4 // Auxiliary Information
+ * PT_SHLIB: 0x5 // Reserved
+ * PT_PHDR: 0x6 // Segment with Program Header
+ * PT_TLS: 0x7 // Thread Local Storage
+ */
+ REF_PHDR(64, p_type) = PT_LOAD; // 0x1
+
+ /*
+ * p_flags defines permissions for this section
+ * PF_R: 0x4 // Read
+ * PF_W: 0x2 // Write
+ * PF_X: 0x1 // Execute
+ */
+ REF_PHDR(64, p_flags) = PF_R | PF_X; // 0x5
+
+ /*
+ * p_offset is the offset in the file image (relative to the start
+ * of the program header) for this segment.
+ */
+ REF_PHDR(64, p_offset) = 0x0;
+
+ /*
+ * p_vaddr is the virtual address where this segment should be loaded
+ * p_paddr is for the physical address (unused by System V)
+ */
+ REF_PHDR(64, p_vaddr) = 0x400000;
+ REF_PHDR(64, p_paddr) = 0x400000;
+
+ /*
+ * p_filesz is the size of the segment in the file image
+ * p_memsz is the size of the segment in memory
+ *
+ * Note: p_filesz doesn't have to equal p_memsz
+ */
+ REF_PHDR(64, p_filesz) = elf_ptr->text.text_size;
+ REF_PHDR(64, p_memsz) = elf_ptr->text.text_size;
+
+ break;
+ // 32-Bit ISAs
+ case ARM32:
+ /*
+ * p_type identifies what type of segment this is
+ * PT_NULL: 0x0 // Unused
+ * PT_LOAD: 0x1 // Loadable Segment
+ * PT_DYNAMIC: 0x2 // Dynamic Linker Information
+ * PT_INTERP: 0x3 // Interpreter Information
+ * PT_NOTE: 0x4 // Auxiliary Information
+ * PT_SHLIB: 0x5 // Reserved
+ * PT_PHDR: 0x6 // Segment with Program Header
+ * PT_TLS: 0x7 // Thread Local Storage
+ */
+ REF_PHDR(32, p_type) = PT_LOAD; // 0x1
+
+ /*
+ * p_flags defines permissions for this section
+ * PF_R: 0x4 // Read
+ * PF_W: 0x2 // Write
+ * PF_X: 0x1 // Execute
+ */
+ REF_PHDR(32, p_flags) = PF_R | PF_X; // 0x5
+
+ /*
+ * p_offset is the offset in the file image (relative to the start
+ * of the program header) for this segment.
+ */
+ REF_PHDR(32, p_offset) = 0x0;
+
+ /*
+ * p_vaddr is the virtual address where this segment should be loaded
+ * p_paddr is for the physical address (unused by System V)
+ */
+ REF_PHDR(32, p_vaddr) = 0x10000;
+ REF_PHDR(32, p_paddr) = 0x10000;
+
+ /*
+ * p_filesz is the size of the segment in the file image
+ * p_memsz is the size of the segment in memory
+ *
+ * Note: p_filesz doesn't have to equal p_memsz
+ */
+ REF_PHDR(32, p_filesz) = elf_ptr->text.text_size;
+ REF_PHDR(32, p_memsz) = elf_ptr->text.text_size;
+
+ break;
+ default:
+ exit(1);
+
+ }
+
+ /*
+ * p_align is the memory alignment
+ *
+ * Note: p_vaddr = p_offset % p_align
+ */
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ REF_PHDR(64, p_align) = 0x400000;
+ break;
+ case ARM32:
+ REF_PHDR(32, p_align) = 0x10000;
+ break;
+ case AARCH64:
+ REF_PHDR(64, p_align) = 0x400000;
+ break;
+
+ }
+
+ return phdr;
+
+}
+
+/*
+ * e_entry depends on p_vaddr, so has to be set after populate_ehdr()
+ * and populate_phdr() have been called.
+ * taken from libgolf.h
+ */
+int set_entry_point(RawBinary *elf_ptr) {
+
+ /*
+ * Once the whole ELF file is copied into memory, control is handed to
+ * e_entry. Relative to the process's virtual memory address, the .text
+ * segment will be located immediately after the ELF and program header.
+ *
+ * ehdr and phdr are pointers to the ELF and Program headers respectively.
+ * The switch case casts and assigns them to the correct fields of the ELF
+ * struct, then sets ehdr->e_entry.
+ */
+ void *ehdr, *phdr;
+
+ switch (elf_ptr->isa) {
+
+ case X86_64:
+ case AARCH64:
+ ehdr = GET_EHDR(64);
+ phdr = GET_PHDR(64);
+ REF_EHDR(64, e_entry) = REF_PHDR(64, p_vaddr) + ehdr_size + phdr_size;
+ break;
+ case ARM32:
+ ehdr = GET_EHDR(32);
+ phdr = GET_PHDR(32);
+ REF_EHDR(32, e_entry) = REF_PHDR(32, p_vaddr) + ehdr_size + phdr_size;
+ break;
+ default:
+ exit(1);
+
+ }
+
+ return 0;
+
+}
+
+typedef struct my_mutator {
+
+ afl_state_t *afl;
+ size_t trim_size_current;
+ int trimmming_steps;
+ int cur_step;
+ u8 *mutated_out, *post_process_buf, *trim_buf;
+
+} my_mutator_t;
+
+my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {
+
+ srand(seed); // needed also by surgical_havoc_mutate()
+ my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
+ if (!data) {
+
+ perror("afl_custom_init alloc");
+ return NULL;
+
+ }
+
+ 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;
+
+}
+
+size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *in_buf, size_t buf_size,
+ u8 **out_buf, uint8_t *add_buf,
+ size_t add_buf_size, // add_buf can be NULL
+ size_t max_size) {
+
+ RawBinary elf_obj;
+ RawBinary *elf = &elf_obj;
+ elf->isa = 62;
+ Elf64_Ehdr *ehdr;
+ Elf64_Phdr *phdr;
+ copy_text_segment(elf, buf, sizeof(buf));
+ ehdr = populate_ehdr(elf);
+ phdr = populate_phdr(elf);
+ set_entry_point(elf);
+
+ size_t mutated_size = ehdr_size + phdr_size + elf->text.text_size;
+ int pos = 0;
+ // example fields
+ ehdr->e_ident[EI_CLASS] = (uint8_t *)(in_buf + pos++);
+ ehdr->e_ident[EI_DATA] = (uint8_t *)(in_buf + pos++);
+ ehdr->e_ident[EI_VERSION] = (uint8_t *)(in_buf + pos++);
+ ehdr->e_ident[EI_OSABI] = (uint8_t *)(in_buf + pos++);
+ for (int i = 0x8; i < 0x10; ++i) {
+
+ (ehdr->e_ident)[i] = (uint8_t *)(in_buf + pos++);
+
+ }
+
+ ehdr->e_version = (uint32_t *)(in_buf + pos);
+ pos += 4;
+ // sections headers
+ ehdr->e_shoff = (uint64_t *)(in_buf + pos);
+ pos += 8;
+ ehdr->e_shentsize = (uint16_t *)(in_buf + pos);
+ pos += 2;
+ ehdr->e_shnum = (uint16_t *)(in_buf + pos);
+ pos += 2;
+ ehdr->e_shstrndx = (uint16_t *)(in_buf + pos);
+ pos += 2;
+ ehdr->e_flags = (uint32_t *)(in_buf + pos);
+ pos += 4;
+ // physical addr
+ phdr->p_paddr = (uint64_t *)(in_buf + pos);
+ pos += 8;
+ phdr->p_align = (uint64_t *)(in_buf + pos);
+ pos += 8;
+
+ /* mimic GEN_ELF()
+ * Write:
+ * - ELF Header
+ * - Program Header
+ * - Text Segment
+ */
+ memcpy(data->mutated_out, ehdr, ehdr_size);
+ memcpy(data->mutated_out + ehdr_size, phdr, phdr_size);
+ memcpy(data->mutated_out + ehdr_size + phdr_size, elf->text.text_segment,
+ elf->text.text_size);
+
+ *out_buf = data->mutated_out;
+ return mutated_size;
+
+}
+
+void afl_custom_deinit(my_mutator_t *data) {
+
+ free(data->post_process_buf);
+ free(data->mutated_out);
+ free(data->trim_buf);
+ free(data);
+
+}
+
diff --git a/custom_mutators/examples/example.py b/custom_mutators/examples/example.py
index 3a6d22e4..830f302f 100644
--- a/custom_mutators/examples/example.py
+++ b/custom_mutators/examples/example.py
@@ -133,6 +133,11 @@ def fuzz(buf, add_buf, max_size):
# @return: The buffer containing the test case after
# '''
# return buf
+# def post_run():
+# '''
+# Called after each time the execution of the target program by AFL++
+# '''
+# pass
#
# def havoc_mutation(buf, max_size):
# '''
diff --git a/custom_mutators/grammar_mutator/GRAMMAR_VERSION b/custom_mutators/grammar_mutator/GRAMMAR_VERSION
index 2568c6a5..3a019448 100644
--- a/custom_mutators/grammar_mutator/GRAMMAR_VERSION
+++ b/custom_mutators/grammar_mutator/GRAMMAR_VERSION
@@ -1 +1 @@
-ff4e5a2
+5ed4f8d
diff --git a/custom_mutators/grammar_mutator/grammar_mutator b/custom_mutators/grammar_mutator/grammar_mutator
-Subproject ff4e5a265daf5d88c4a636fb6a2c22b1d733db0
+Subproject 5ed4f8d6e6524df9670af6b411b13031833d67d
diff --git a/custom_mutators/symcc/README.md b/custom_mutators/symcc/README.md
index 364a348e..a6839a37 100644
--- a/custom_mutators/symcc/README.md
+++ b/custom_mutators/symcc/README.md
@@ -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
diff --git a/custom_mutators/symqemu/Makefile b/custom_mutators/symqemu/Makefile
new file mode 100644
index 00000000..958aec19
--- /dev/null
+++ b/custom_mutators/symqemu/Makefile
@@ -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
diff --git a/custom_mutators/symqemu/README.md b/custom_mutators/symqemu/README.md
new file mode 100644
index 00000000..c3071afc
--- /dev/null
+++ b/custom_mutators/symqemu/README.md
@@ -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.
diff --git a/custom_mutators/symqemu/symqemu.c b/custom_mutators/symqemu/symqemu.c
new file mode 100644
index 00000000..73a1640a
--- /dev/null
+++ b/custom_mutators/symqemu/symqemu.c
@@ -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);
+
+}
+