diff options
Diffstat (limited to 'custom_mutators')
-rw-r--r-- | custom_mutators/README.md | 11 | ||||
-rw-r--r-- | custom_mutators/aflpp/Makefile | 10 | ||||
-rw-r--r-- | custom_mutators/aflpp/README.md | 8 | ||||
-rw-r--r-- | custom_mutators/aflpp/aflpp.c | 90 | ||||
-rw-r--r-- | custom_mutators/aflpp/standalone/Makefile | 10 | ||||
-rw-r--r-- | custom_mutators/aflpp/standalone/README.md | 10 | ||||
-rw-r--r-- | custom_mutators/aflpp/standalone/aflpp-standalone.c | 163 | ||||
-rw-r--r-- | custom_mutators/aflpp_tritondse/README.md | 9 | ||||
-rw-r--r-- | custom_mutators/aflpp_tritondse/aflpp_tritondse.py | 80 | ||||
-rw-r--r-- | custom_mutators/examples/README.md | 3 | ||||
-rw-r--r-- | custom_mutators/examples/custom_post_run.c | 53 | ||||
-rw-r--r-- | custom_mutators/examples/elf_header_mutator.c | 679 | ||||
-rw-r--r-- | custom_mutators/examples/example.py | 5 | ||||
-rw-r--r-- | custom_mutators/grammar_mutator/GRAMMAR_VERSION | 2 | ||||
m--------- | custom_mutators/grammar_mutator/grammar_mutator | 0 | ||||
-rw-r--r-- | custom_mutators/symcc/README.md | 2 | ||||
-rw-r--r-- | custom_mutators/symqemu/Makefile | 14 | ||||
-rw-r--r-- | custom_mutators/symqemu/README.md | 19 | ||||
-rw-r--r-- | custom_mutators/symqemu/symqemu.c | 424 |
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); + +} + |