diff options
Diffstat (limited to 'utils/afl_network_proxy')
-rw-r--r-- | utils/afl_network_proxy/GNUmakefile | 43 | ||||
-rw-r--r-- | utils/afl_network_proxy/Makefile | 2 | ||||
-rw-r--r-- | utils/afl_network_proxy/README.md | 61 | ||||
-rw-r--r-- | utils/afl_network_proxy/afl-network-client.c | 415 | ||||
-rw-r--r-- | utils/afl_network_proxy/afl-network-server.c | 720 |
5 files changed, 1241 insertions, 0 deletions
diff --git a/utils/afl_network_proxy/GNUmakefile b/utils/afl_network_proxy/GNUmakefile new file mode 100644 index 00000000..25a3df82 --- /dev/null +++ b/utils/afl_network_proxy/GNUmakefile @@ -0,0 +1,43 @@ +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +DOC_PATH = $(PREFIX)/share/doc/afl + +PROGRAMS = afl-network-client afl-network-server + +HASH=\# + +CFLAGS += -Wno-pointer-sign + +ifdef STATIC + CFLAGS += -static +endif + +ifeq "$(shell echo '$(HASH)include <libdeflate.h>@int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" + CFLAGS += -DUSE_DEFLATE=1 + LDFLAGS += -ldeflate + $(info libdeflate-dev was detected, using compression) +else + $(warn did not find libdeflate-dev, cannot use compression) +endif + +all: $(PROGRAMS) + +help: + @echo make options: + @echo STATIC - build as static binaries + @echo COMPRESS_TESTCASES - compress test cases + +afl-network-client: afl-network-client.c + $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) + +afl-network-server: afl-network-server.c + $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) + +clean: + rm -f $(PROGRAMS) *~ core + +install: all + install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(DOC_PATH) + install -m 755 $(PROGRAMS) $${DESTDIR}$(BIN_PATH) + install -T -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.network_proxy.md + diff --git a/utils/afl_network_proxy/Makefile b/utils/afl_network_proxy/Makefile new file mode 100644 index 00000000..0b306dde --- /dev/null +++ b/utils/afl_network_proxy/Makefile @@ -0,0 +1,2 @@ +all: + @echo please use GNU make, thanks! diff --git a/utils/afl_network_proxy/README.md b/utils/afl_network_proxy/README.md new file mode 100644 index 00000000..a5ac3578 --- /dev/null +++ b/utils/afl_network_proxy/README.md @@ -0,0 +1,61 @@ +# afl-network-proxy + +If you want to run afl-fuzz over the network than this is what you need :) +Note that the impact on fuzzing speed will be huge, expect a loss of 90%. + +## When to use this + +1. when you have to fuzz a target that has to run on a system that cannot + contain the fuzzing output (e.g. /tmp too small and file system is read-only) +2. when the target instantly reboots on crashes +3. ... any other reason you would need this + +## how to get it running + +### Compiling + +Just type `make` and let the autodetection do everything for you. + +Note that you will get a 40-50% performance increase if you have libdeflate-dev +installed. The GNUmakefile will autodetect it if present. + +If your target has large test cases (10+kb) that are ascii only or large chunks +of zero blocks then set `CFLAGS=-DCOMPRESS_TESTCASES=1` to compress them. +For most targets this hurts performance though so it is disabled by default. + +### on the target + +Run `afl-network-server` with your target with the -m and -t values you need. +Important is the -i parameter which is the TCP port to listen on. +e.g.: +``` +afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@ +``` + +### on the (afl-fuzz) master + +Just run afl-fuzz with your normal options, however the target should be +`afl-network-client` with the IP and PORT of the `afl-network-server` and +increase the -t value: +``` +afl-fuzz -i in -o out -t 2000+ -- afl-network-client TARGET-IP 1111 +``` +Note the '+' on the -t parameter value. The afl-network-server will take +care of proper timeouts hence afl-fuzz should not. The '+' increases the +timeout and the value itself should be 500-1000 higher than the one on +afl-network-server. + +### networking + +The TARGET can be an IPv4 or IPv6 address, or a host name that resolves to +either. Note that also the outgoing interface can be specified with a '%' for +`afl-network-client`, e.g. `fe80::1234%eth0`. + +Also make sure your default TCP window size is larger than your MAP_SIZE +(130kb is a good value). +On Linux that is the middle value of `/proc/sys/net/ipv4/tcp_rmem` + +## how to compile and install + +`make && sudo make install` + diff --git a/utils/afl_network_proxy/afl-network-client.c b/utils/afl_network_proxy/afl-network-client.c new file mode 100644 index 00000000..a2451fdc --- /dev/null +++ b/utils/afl_network_proxy/afl-network-client.c @@ -0,0 +1,415 @@ +/* + american fuzzy lop++ - afl-network-client + --------------------------------------- + + Written by Marc Heuse <mh@mh-sec.de> + + Copyright 2019-2020 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: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "debug.h" + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <stdint.h> +#include <errno.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <arpa/inet.h> +#include <sys/mman.h> +#ifndef USEMMAP + #include <sys/shm.h> +#endif +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <fcntl.h> + +#ifdef USE_DEFLATE + #include <libdeflate.h> +#endif + +u8 *__afl_area_ptr; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +#else +__thread u32 __afl_map_size = MAP_SIZE; +#endif + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ + +static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + +} + +static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status, res = 0x0fffffff; // res is a dummy pid + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) return 0; + + /* we have a testcase - read it */ + status = read(0, buf, max_len); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; + + if (status < 1) + return 0; + else + return status; + +} + +static void __afl_end_testcase(int status) { + + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); + +} + +/* you just need to modify the while() loop in this main() */ + +int main(int argc, char *argv[]) { + + u8 * interface, *buf, *ptr; + s32 s = -1; + struct addrinfo hints, *hres, *aip; + u32 * lenptr, max_len = 65536; +#ifdef USE_DEFLATE + u8 * buf2; + u32 * lenptr1, *lenptr2, buf2_len, compress_len; + size_t decompress_len; +#endif + + if (argc < 3 || argc > 4) { + + printf("Syntax: %s host port [max-input-size]\n\n", argv[0]); + printf("Requires host and port of the remote afl-proxy-server instance.\n"); + printf( + "IPv4 and IPv6 are supported, also binding to an interface with " + "\"%%\"\n"); + printf("The max-input-size default is %u.\n", max_len); + printf( + "The default map size is %u and can be changed with setting " + "AFL_MAP_SIZE.\n", + __afl_map_size); + exit(-1); + + } + + if ((interface = strchr(argv[1], '%')) != NULL) *interface++ = 0; + + if (argc > 3) + if ((max_len = atoi(argv[3])) < 0) + FATAL("max-input-size may not be negative or larger than 2GB: %s", + argv[3]); + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) + if ((__afl_map_size = atoi(ptr)) < 8) + FATAL("illegal map size, may not be < 8 or >= 2^30: %s", ptr); + + if ((buf = malloc(max_len + 4)) == NULL) + PFATAL("can not allocate %u memory", max_len + 4); + lenptr = (u32 *)buf; + +#ifdef USE_DEFLATE + buf2_len = (max_len > __afl_map_size ? max_len : __afl_map_size); + if ((buf2 = malloc(buf2_len + 8)) == NULL) + PFATAL("can not allocate %u memory", buf2_len + 8); + lenptr1 = (u32 *)buf2; + lenptr2 = (u32 *)(buf2 + 4); +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + + if (getaddrinfo(argv[1], argv[2], &hints, &hres) != 0) + PFATAL("could not resolve target %s", argv[1]); + + for (aip = hres; aip != NULL && s == -1; aip = aip->ai_next) { + + if ((s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) >= 0) { + +#ifdef SO_BINDTODEVICE + if (interface != NULL) + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, interface, + strlen(interface) + 1) < 0) + fprintf(stderr, "Warning: could not bind to device %s\n", interface); +#else + fprintf(stderr, + "Warning: binding to interface is not supported for your OS\n"); +#endif + +#ifdef SO_PRIORITY + int priority = 7; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) { + + priority = 6; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, + sizeof(priority)) < 0) + WARNF("could not set priority on socket"); + + } + +#endif + + if (connect(s, aip->ai_addr, aip->ai_addrlen) == -1) s = -1; + + } + + } + +#ifdef USE_DEFLATE + struct libdeflate_compressor *compressor; + compressor = libdeflate_alloc_compressor(1); + struct libdeflate_decompressor *decompressor; + decompressor = libdeflate_alloc_decompressor(); + fprintf(stderr, "Compiled with compression support\n"); +#endif + + if (s == -1) + FATAL("could not connect to target tcp://%s:%s", argv[1], argv[2]); + else + fprintf(stderr, "Connected to target tcp://%s:%s\n", argv[1], argv[2]); + + /* we initialize the shared memory map and start the forkserver */ + __afl_map_shm(); + __afl_start_forkserver(); + + int i = 1, j, status, ret, received; + + // fprintf(stderr, "Waiting for first testcase\n"); + while ((*lenptr = __afl_next_testcase(buf + 4, max_len)) > 0) { + + // fprintf(stderr, "Sending testcase with len %u\n", *lenptr); +#ifdef USE_DEFLATE + #ifdef COMPRESS_TESTCASES + // we only compress the testcase if it does not fit in the TCP packet + if (*lenptr > 1500 - 20 - 32 - 4) { + + // set highest byte to signify compression + *lenptr1 = (*lenptr | 0xff000000); + *lenptr2 = (u32)libdeflate_deflate_compress(compressor, buf + 4, *lenptr, + buf2 + 8, buf2_len); + if (send(s, buf2, *lenptr2 + 8, 0) != *lenptr2 + 8) + PFATAL("sending test data failed"); + // fprintf(stderr, "COMPRESS (%u->%u):\n", *lenptr, *lenptr2); + // for (u32 i = 0; i < *lenptr; i++) + // fprintf(stderr, "%02x", buf[i + 4]); + // fprintf(stderr, "\n"); + // for (u32 i = 0; i < *lenptr2; i++) + // fprintf(stderr, "%02x", buf2[i + 8]); + // fprintf(stderr, "\n"); + + } else { + + #endif +#endif + if (send(s, buf, *lenptr + 4, 0) != *lenptr + 4) + PFATAL("sending test data failed"); +#ifdef USE_DEFLATE + #ifdef COMPRESS_TESTCASES + // fprintf(stderr, "unCOMPRESS (%u)\n", *lenptr); + + } + + #endif +#endif + + received = 0; + while (received < 4 && + (ret = recv(s, &status + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) + FATAL("did not receive waitpid data (%d, %d)", received, ret); + // fprintf(stderr, "Received status\n"); + + received = 0; +#ifdef USE_DEFLATE + while (received < 4 && + (ret = recv(s, &compress_len + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) + FATAL("did not receive compress_len (%d, %d)", received, ret); + // fprintf(stderr, "Received status\n"); + + received = 0; + while (received < compress_len && + (ret = recv(s, buf2 + received, buf2_len - received, 0)) > 0) + received += ret; + if (received != compress_len) + FATAL("did not receive coverage data (%d, %d)", received, ret); + + if (libdeflate_deflate_decompress(decompressor, buf2, compress_len, + __afl_area_ptr, __afl_map_size, + &decompress_len) != LIBDEFLATE_SUCCESS || + decompress_len != __afl_map_size) + FATAL("decompression failed"); + // fprintf(stderr, "DECOMPRESS (%u->%u): ", compress_len, decompress_len); + // for (u32 i = 0; i < __afl_map_size; i++) fprintf(stderr, "%02x", + // __afl_area_ptr[i]); fprintf(stderr, "\n"); +#else + while (received < __afl_map_size && + (ret = recv(s, __afl_area_ptr + received, __afl_map_size - received, + 0)) > 0) + received += ret; + if (received != __afl_map_size) + FATAL("did not receive coverage data (%d, %d)", received, ret); +#endif + // fprintf(stderr, "Received coverage\n"); + + /* report the test case is done and wait for the next */ + __afl_end_testcase(status); + // fprintf(stderr, "Waiting for next testcase %d\n", ++i); + + } + +#ifdef USE_DEFLATE + libdeflate_free_compressor(compressor); + libdeflate_free_decompressor(decompressor); +#endif + + return 0; + +} + diff --git a/utils/afl_network_proxy/afl-network-server.c b/utils/afl_network_proxy/afl-network-server.c new file mode 100644 index 00000000..513dc8f2 --- /dev/null +++ b/utils/afl_network_proxy/afl-network-server.c @@ -0,0 +1,720 @@ +/* + american fuzzy lop++ - network proxy server + ------------------------------------------- + + Originally written by Michal Zalewski + + Forkserver design by Jann Horn <jannhorn@googlemail.com> + + Now maintained by Marc Heuse <mh@mh-sec.de>, + Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and + Andrea Fioraldi <andreafioraldi@gmail.com> and + Dominik Maier <mail@dmnk.co> + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2020 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: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "hash.h" +#include "forkserver.h" +#include "sharedmem.h" +#include "common.h" + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <dirent.h> +#include <fcntl.h> + +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <arpa/inet.h> +#include <sys/mman.h> +#include <sys/shm.h> +#include <sys/socket.h> +#include <netdb.h> + +#ifdef USE_DEFLATE + #include <libdeflate.h> +struct libdeflate_compressor * compressor; +struct libdeflate_decompressor *decompressor; +#endif + +static u8 *in_file, /* Minimizer input test case */ + *out_file; + +static u8 *in_data; /* Input data for trimming */ +static u8 *buf2; + +static s32 in_len; +static s32 buf2_len; +static u32 map_size = MAP_SIZE; + +static volatile u8 stop_soon; /* Ctrl-C pressed? */ + +/* See if any bytes are set in the bitmap. */ + +static inline u8 anything_set(afl_forkserver_t *fsrv) { + + u32 *ptr = (u32 *)fsrv->trace_bits; + u32 i = (map_size >> 2); + + while (i--) { + + if (*(ptr++)) { return 1; } + + } + + return 0; + +} + +static void at_exit_handler(void) { + + afl_fsrv_killall(); + +} + +/* Write output file. */ + +static s32 write_to_file(u8 *path, u8 *mem, u32 len) { + + s32 ret; + + unlink(path); /* Ignore errors */ + + ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (ret < 0) { PFATAL("Unable to create '%s'", path); } + + ck_write(ret, mem, len, path); + + lseek(ret, 0, SEEK_SET); + + return ret; + +} + +/* Execute target application. Returns 0 if the changes are a dud, or + 1 if they should be kept. */ + +static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, + u8 first_run) { + + afl_fsrv_write_to_testcase(fsrv, mem, len); + + fsrv_run_result_t ret = + afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon); + + if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); } + + if (stop_soon) { + + SAYF(cRST cLRD "\n+++ aborted by user +++\n" cRST); + exit(1); + + } + + return ret; + +} + +/* Handle Ctrl-C and the like. */ + +static void handle_stop_sig(int sig) { + + stop_soon = 1; + afl_fsrv_killall(); + +} + +/* Do basic preparations - persistent fds, filenames, etc. */ + +static void set_up_environment(afl_forkserver_t *fsrv) { + + u8 *x; + + fsrv->dev_null_fd = open("/dev/null", O_RDWR); + if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + + if (!out_file) { + + u8 *use_dir = "."; + + if (access(use_dir, R_OK | W_OK | X_OK)) { + + use_dir = get_afl_env("TMPDIR"); + if (!use_dir) { use_dir = "/tmp"; } + + } + + out_file = alloc_printf("%s/.afl-input-temp-%u", use_dir, getpid()); + + } + + unlink(out_file); + + fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } + + /* Set sane defaults... */ + + x = get_afl_env("ASAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "abort_on_error=1")) { + + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + + } + + if (!strstr(x, "symbolize=0")) { + + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + } + + x = get_afl_env("MSAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) { + + FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY( + MSAN_ERROR) " - please fix!"); + + } + + if (!strstr(x, "symbolize=0")) { + + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + } + + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", + 0); + + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" + "msan_track_origins=0", 0); + + if (get_afl_env("AFL_PRELOAD")) { + + if (fsrv->qemu_mode) { + + u8 *qemu_preload = getenv("QEMU_SET_ENV"); + u8 *afl_preload = getenv("AFL_PRELOAD"); + u8 *buf; + + s32 i, afl_preload_size = strlen(afl_preload); + for (i = 0; i < afl_preload_size; ++i) { + + if (afl_preload[i] == ',') { + + PFATAL( + "Comma (',') is not allowed in AFL_PRELOAD when -Q is " + "specified!"); + + } + + } + + if (qemu_preload) { + + buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", + qemu_preload, afl_preload, afl_preload); + + } else { + + buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", + afl_preload, afl_preload); + + } + + setenv("QEMU_SET_ENV", buf, 1); + + afl_free(buf); + + } else { + + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + + } + + } + +} + +/* Setup signal handlers, duh. */ + +static void setup_signal_handlers(void) { + + struct sigaction sa; + + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; + + sigemptyset(&sa.sa_mask); + + /* Various ways of saying "stop". */ + + sa.sa_handler = handle_stop_sig; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + +} + +/* Display usage hints. */ + +static void usage(u8 *argv0) { + + SAYF( + "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + + "Required parameters:\n" + + " -i port - the port to listen for the client to connect to\n\n" + + "Execution control settings:\n" + + " -f file - input file read by the tested program (stdin)\n" + " -t msec - timeout for each run (%d ms)\n" + " -m megs - memory limit for child process (%d MB)\n" + " -Q - use binary-only instrumentation (QEMU mode)\n" + " -U - use unicorn-based instrumentation (Unicorn mode)\n" + " -W - use qemu-based instrumentation with Wine (Wine " + "mode)\n\n" + + "Environment variables used:\n" + "TMPDIR: directory to use for temporary input files\n" + "ASAN_OPTIONS: custom settings for ASAN\n" + " (must contain abort_on_error=1 and symbolize=0)\n" + "MSAN_OPTIONS: custom settings for MSAN\n" + " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" + " the target was compiled for\n" + "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" + + , argv0, EXEC_TIMEOUT, MEM_LIMIT); + + exit(1); + +} + +int recv_testcase(int s, void **buf) { + + u32 size; + s32 ret; + size_t received; + + received = 0; + while (received < 4 && (ret = recv(s, &size + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) FATAL("did not receive size information"); + if (size == 0) FATAL("did not receive valid size information"); + // fprintf(stderr, "received size information of %d\n", size); + + if ((size & 0xff000000) != 0xff000000) { + + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } + received = 0; + // fprintf(stderr, "unCOMPRESS (%u)\n", size); + while (received < size && + (ret = recv(s, ((char *)*buf) + received, size - received, 0)) > 0) + received += ret; + + } else { + +#ifdef USE_DEFLATE + u32 clen; + size -= 0xff000000; + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } + received = 0; + while (received < 4 && + (ret = recv(s, &clen + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) FATAL("did not receive clen1 information"); + // fprintf(stderr, "received clen information of %d\n", clen); + if (clen < 1) + FATAL("did not receive valid compressed len information: %u", clen); + buf2 = afl_realloc((void **)&buf2, clen); + buf2_len = clen; + if (unlikely(!buf2)) { PFATAL("Alloc"); } + received = 0; + while (received < clen && + (ret = recv(s, buf2 + received, clen - received, 0)) > 0) + received += ret; + if (received != clen) FATAL("did not receive compressed information"); + if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, + size, &received) != LIBDEFLATE_SUCCESS) + FATAL("decompression failed"); + // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); + // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); + // fprintf(stderr, "\n"); + // for (u32 i = 0; i < received; i++) fprintf(stderr, "%02x", + // ((u8*)(*buf))[i]); fprintf(stderr, "\n"); +#else + FATAL("Received compressed data but not compiled with compression support"); +#endif + + } + + // fprintf(stderr, "receiving testcase %p %p max %u\n", buf, *buf, *max_len); + if (received != size) + FATAL("did not receive testcase data %lu != %u, %d", received, size, ret); + // fprintf(stderr, "received testcase\n"); + return size; + +} + +/* Main entry point */ + +int main(int argc, char **argv_orig, char **envp) { + + s32 opt, s, sock, on = 1, port = -1; + u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; + char **use_argv; + struct sockaddr_in6 serveraddr, clientaddr; + int addrlen = sizeof(clientaddr); + char str[INET6_ADDRSTRLEN]; + char ** argv = argv_cpy_dup(argc, argv_orig); + u8 * send_buf; +#ifdef USE_DEFLATE + u32 *lenptr; +#endif + + afl_forkserver_t fsrv_var = {0}; + afl_forkserver_t *fsrv = &fsrv_var; + afl_fsrv_init(fsrv); + map_size = get_map_size(); + fsrv->map_size = map_size; + + if ((send_buf = malloc(map_size + 4)) == NULL) PFATAL("malloc"); + + while ((opt = getopt(argc, argv, "+i:f:m:t:QUWh")) > 0) { + + switch (opt) { + + case 'i': + + if (port > 0) { FATAL("Multiple -i options not supported"); } + port = atoi(optarg); + if (port < 1 || port > 65535) + FATAL("invalid port definition, must be between 1-65535: %s", optarg); + break; + + case 'f': + + if (out_file) { FATAL("Multiple -f options not supported"); } + fsrv->use_stdin = 0; + out_file = optarg; + break; + + case 'm': { + + u8 suffix = 'M'; + + if (mem_limit_given) { FATAL("Multiple -m options not supported"); } + mem_limit_given = 1; + + if (!optarg) { FATAL("Wrong usage of -m"); } + + if (!strcmp(optarg, "none")) { + + fsrv->mem_limit = 0; + break; + + } + + if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 || + optarg[0] == '-') { + + FATAL("Bad syntax used for -m"); + + } + + switch (suffix) { + + case 'T': + fsrv->mem_limit *= 1024 * 1024; + break; + case 'G': + fsrv->mem_limit *= 1024; + break; + case 'k': + fsrv->mem_limit /= 1024; + break; + case 'M': + break; + + default: + FATAL("Unsupported suffix or bad syntax for -m"); + + } + + if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); } + + if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) { + + FATAL("Value of -m out of range on 32-bit systems"); + + } + + } + + break; + + case 't': + + if (timeout_given) { FATAL("Multiple -t options not supported"); } + timeout_given = 1; + + if (!optarg) { FATAL("Wrong usage of -t"); } + + fsrv->exec_tmout = atoi(optarg); + + if (fsrv->exec_tmout < 10 || optarg[0] == '-') { + + FATAL("Dangerously low value of -t"); + + } + + break; + + case 'Q': + + if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } + if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } + + fsrv->qemu_mode = 1; + break; + + case 'U': + + if (unicorn_mode) { FATAL("Multiple -Q options not supported"); } + if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } + + unicorn_mode = 1; + break; + + case 'W': /* Wine+QEMU mode */ + + if (use_wine) { FATAL("Multiple -W options not supported"); } + fsrv->qemu_mode = 1; + use_wine = 1; + + if (!mem_limit_given) { fsrv->mem_limit = 0; } + + break; + + case 'h': + usage(argv[0]); + return -1; + break; + + default: + usage(argv[0]); + + } + + } + + if (optind == argc || port < 1) { usage(argv[0]); } + + check_environment_vars(envp); + + sharedmem_t shm = {0}; + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); + + in_data = afl_realloc((void **)&in_data, 65536); + if (unlikely(!in_data)) { PFATAL("Alloc"); } + + atexit(at_exit_handler); + setup_signal_handlers(); + + set_up_environment(fsrv); + + fsrv->target_path = find_binary(argv[optind]); + detect_file_args(argv + optind, out_file, &fsrv->use_stdin); + + if (fsrv->qemu_mode) { + + if (use_wine) { + + use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind, + argv + optind); + + } else { + + use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind, + argv + optind); + + } + + } else { + + use_argv = argv + optind; + + } + + if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) PFATAL("socket() failed"); + +#ifdef SO_REUSEADDR + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { + + WARNF("setsockopt(SO_REUSEADDR) failed"); + + } + +#endif + +#ifdef SO_PRIORITY + int priority = 7; + if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) { + + priority = 6; + if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) + WARNF("could not set priority on socket"); + + } + +#endif + + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin6_family = AF_INET6; + serveraddr.sin6_port = htons(port); + serveraddr.sin6_addr = in6addr_any; + + if (bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) + PFATAL("bind() failed"); + + if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } + + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + +#ifdef USE_DEFLATE + compressor = libdeflate_alloc_compressor(1); + decompressor = libdeflate_alloc_decompressor(); + buf2 = afl_realloc((void **)&buf2, map_size + 16); + buf2_len = map_size + 16; + if (unlikely(!buf2)) { PFATAL("alloc"); } + lenptr = (u32 *)(buf2 + 4); + fprintf(stderr, "Compiled with compression support\n"); +#endif + + fprintf(stderr, + "Waiting for incoming connection from afl-network-client on port %d " + "...\n", + port); + + if ((s = accept(sock, NULL, NULL)) < 0) { PFATAL("accept() failed"); } + fprintf(stderr, "Received connection, starting ...\n"); + +#ifdef SO_PRIORITY + priority = 7; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { + + priority = 6; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) + WARNF("could not set priority on socket"); + + } + +#endif + + while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { + + // fprintf(stderr, "received %u\n", in_len); + (void)run_target(fsrv, use_argv, in_data, in_len, 1); + + memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size); + +#ifdef USE_DEFLATE + memcpy(buf2, &fsrv->child_status, 4); + *lenptr = (u32)libdeflate_deflate_compress( + compressor, send_buf + 4, fsrv->map_size, buf2 + 8, buf2_len - 8); + // fprintf(stderr, "COMPRESS (%u->%u): ", fsrv->map_size, *lenptr); + // for (u32 i = 0; i < fsrv->map_size; i++) fprintf(stderr, "%02x", + // fsrv->trace_bits[i]); fprintf(stderr, "\n"); + if (send(s, buf2, *lenptr + 8, 0) != 8 + *lenptr) + FATAL("could not send data"); +#else + memcpy(send_buf, &fsrv->child_status, 4); + if (send(s, send_buf, fsrv->map_size + 4, 0) != 4 + fsrv->map_size) + FATAL("could not send data"); +#endif + + // fprintf(stderr, "sent result\n"); + + } + + unlink(out_file); + if (out_file) { ck_free(out_file); } + out_file = NULL; + + afl_shm_deinit(&shm); + afl_fsrv_deinit(fsrv); + if (fsrv->target_path) { ck_free(fsrv->target_path); } + afl_free(in_data); +#if USE_DEFLATE + afl_free(buf2); + libdeflate_free_compressor(compressor); + libdeflate_free_decompressor(decompressor); +#endif + + argv_cpy_free(argv); + + exit(0); + +} + |