about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2020-04-30 17:59:59 +0200
committervan Hauser <vh@thc.org>2020-04-30 17:59:59 +0200
commitefa9df24c2a5f97c212a5a22dda19dcbbab0b5de (patch)
tree3cbd58983e3ebd65729c363965a178c9657259c7
parenta37eca9df538a4184551244d435e027ca86ccb25 (diff)
downloadafl++-efa9df24c2a5f97c212a5a22dda19dcbbab0b5de.tar.gz
afl-untracer completed
-rw-r--r--docs/Changelog.md2
-rw-r--r--examples/afl_network_proxy/README.md11
-rw-r--r--examples/afl_network_proxy/afl-network-client.c23
-rw-r--r--examples/afl_network_proxy/afl-network-server.c2
-rw-r--r--examples/afl_untracer/Makefile2
-rw-r--r--examples/afl_untracer/README.md7
-rw-r--r--examples/afl_untracer/afl-untracer.c96
-rw-r--r--examples/afl_untracer/patches.txt23
-rw-r--r--include/forkserver.h1
-rw-r--r--src/afl-forkserver.c14
10 files changed, 106 insertions, 75 deletions
diff --git a/docs/Changelog.md b/docs/Changelog.md
index faf015c6..565bee72 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -31,6 +31,8 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
   - added examples/afl_network_proxy which allows to fuzz a target over the
     network (not fuzzing tcp/ip services but running afl-fuzz on one system
     and the target being on an embedded device)
+  - added examples/afl_untracer which does a binary-only fuzzing with the
+    modifications done in memory
   - added examples/afl_proxy which can be easily used to fuzz and instrument
     non-standard things
   - all:
diff --git a/examples/afl_network_proxy/README.md b/examples/afl_network_proxy/README.md
index 65012601..c33096be 100644
--- a/examples/afl_network_proxy/README.md
+++ b/examples/afl_network_proxy/README.md
@@ -20,6 +20,7 @@ e.g.:
 ```
 $ afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@
 ```
+
 ### on the fuzzing master
 
 Just run afl-fuzz with your normal options, however the target should be
@@ -42,3 +43,13 @@ either. Note that also the outgoing interface can be specified with a '%' for
 ## how to compile and install
 
 `make && sudo make install`
+
+## Future
+
+It would be much faster and more effective if `afl-network-server` does not
+send the map data back (64kb or more) but the checksum that `afl-fuzz` would
+generate. This change however would make it incompatible with existing
+afl spinoffs.
+
+But in the future this will be implemented and supported as a compile option.
+
diff --git a/examples/afl_network_proxy/afl-network-client.c b/examples/afl_network_proxy/afl-network-client.c
index ede2ff8d..53512286 100644
--- a/examples/afl_network_proxy/afl-network-client.c
+++ b/examples/afl_network_proxy/afl-network-client.c
@@ -175,7 +175,7 @@ static void __afl_start_forkserver(void) {
 
 static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
 
-  s32 status, res = 0xffffff;
+  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;
@@ -193,9 +193,7 @@ static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
 
 }
 
-static void __afl_end_testcase(void) {
-
-  int status = 0xffffff;
+static void __afl_end_testcase(int status) {
 
   if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
 
@@ -273,7 +271,7 @@ int main(int argc, char *argv[]) {
   __afl_map_shm();
   __afl_start_forkserver();
 
-  int i = 1, j;
+  int i = 1, j, status, ret;
   // fprintf(stderr, "Waiting for first testcase\n");
   while ((len = __afl_next_testcase(buf, max_len)) > 0) {
 
@@ -281,17 +279,25 @@ int main(int argc, char *argv[]) {
     if (send(s, &len, 4, 0) != 4) PFATAL("sending size data %d failed", len);
     if (send(s, buf, len, 0) != len) PFATAL("sending test data failed");
 
-    int received = 0, ret;
+    int 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");
+
+    int received = 0;
     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 valid data (%d, %d)", received, ret);
+      FATAL("did not receive coverage data (%d, %d)", received, ret);
     // fprintf(stderr, "Received coverage\n");
 
     /* report the test case is done and wait for the next */
-    __afl_end_testcase();
+    __afl_end_testcase(status);
     // fprintf(stderr, "Waiting for next testcase %d\n", ++i);
 
   }
@@ -299,4 +305,3 @@ int main(int argc, char *argv[]) {
   return 0;
 
 }
-
diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c
index 1bd37560..2f9f8c8e 100644
--- a/examples/afl_network_proxy/afl-network-server.c
+++ b/examples/afl_network_proxy/afl-network-server.c
@@ -579,6 +579,8 @@ int main(int argc, char **argv_orig, char **envp) {
     // fprintf(stderr, "received %u\n", in_len);
     run_target(fsrv, use_argv, in_data, in_len, 1);
 
+    if (send(s, fsrv->child_status, 4, 0) != 4)
+      FATAL("could not send waitpid data");
     if (send(s, fsrv->trace_bits, fsrv->map_size, 0) != fsrv->map_size)
       FATAL("could not send coverage data");
     // fprintf(stderr, "sent result\n");
diff --git a/examples/afl_untracer/Makefile b/examples/afl_untracer/Makefile
index bfef8fa8..5c525877 100644
--- a/examples/afl_untracer/Makefile
+++ b/examples/afl_untracer/Makefile
@@ -7,4 +7,4 @@ libtestinstr.so:	libtestinstr.c
 	$(CC) -fPIC -o libtestinstr.so -shared libtestinstr.c
 
 clean:
-	rm -f afl-untracer *~ core
+	rm -f afl-untracer libtestinstr.so *~ core
diff --git a/examples/afl_untracer/README.md b/examples/afl_untracer/README.md
index 90798028..d3af0ceb 100644
--- a/examples/afl_untracer/README.md
+++ b/examples/afl_untracer/README.md
@@ -3,9 +3,14 @@
 afl-untracer is an example skeleton file which can easily be used to fuzz
 a closed source library.
 
-It is faster and requires less memory than qemu_mode however it is way
+It requires less memory than qemu_mode however it is way
 more course grained and does not provide interesting features like compcov
 or cmplog.
 
 Read and modify afl-untracer.c then `make` and use it as the afl-fuzz target
 (or even remote via afl-network-proxy).
+
+This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL)
+and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz).
+This implementation is slower because the traps are not patched out with each
+run, but on the other hand gives much better coverage information.
diff --git a/examples/afl_untracer/afl-untracer.c b/examples/afl_untracer/afl-untracer.c
index 420f09a2..4d512356 100644
--- a/examples/afl_untracer/afl-untracer.c
+++ b/examples/afl_untracer/afl-untracer.c
@@ -76,16 +76,9 @@ static u32 use_stdin = 1;
 /* This is were the testcase data is written into */
 static u8 buf[10000];  // this is the maximum size for a test case! set it!
 
-/* if you want to use fork() then uncomment the following line. Otherwise
-   threads are used. The differences:
-                        fork()		pthread()
-  speed	        	slow		fast
-  coverage    		detailed	minimal
-  memory leaks		no problem	an issue                       */
-//#define USE_FORK
-
-/* If you want to have debug output set this to 1 */
-static u32 debug = 1;
+/* If you want to have debug output set this to 1, can also be set with
+   AFL_DEBUG  */
+static u32 debug = 0;
 
 // END STEP 1
 
@@ -130,7 +123,7 @@ void read_library_information() {
   if (debug) fprintf(stderr, "Library list:\n");
   while (fgets(buf, sizeof(buf), f)) {
 
-    if (strstr(buf, " r-xp ")) {
+    if (strstr(buf, " r-x")) {
 
       if (liblist_cnt >= MAX_LIB_COUNT) {
 
@@ -159,9 +152,9 @@ void read_library_information() {
         liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16);
         liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16);
         if (debug)
-          fprintf(stderr, "%s:%x (%x-%x)\n", liblist[liblist_cnt].name,
+          fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name,
                   liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
-                  liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end);
+                  liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end - 1);
         liblist_cnt++;
 
       }
@@ -367,17 +360,13 @@ static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
   if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
   // fprintf(stderr, "write1 %d\n", do_exit);
 
-  if (!do_exit)
-    __afl_area_ptr[0] = 1;  // otherwise afl-fuzz will exit with NOINST
   return status;
 
 }
 
-static void __afl_end_testcase(void) {
+static void __afl_end_testcase(int status) {
 
-  s32 res = 0xffffff;
-  if (write(FORKSRV_FD + 1, &res, 4) != 4) do_exit = 1;
-  // if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
+  if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1;
   // fprintf(stderr, "write2 %d\n", do_exit);
   if (do_exit) exit(0);
 
@@ -455,6 +444,19 @@ void setup_trap_instrumentation() {
 
     // It's an offset, parse it and do the patching.
     unsigned long offset = strtoul(line, NULL, 16);
+
+    // I dont know what it is. /proc/<pid>/maps shows the right start address
+    // and the offsets generated by the python scripts are fine as well.
+    // And loading the library into gdb also shows the offsets generated
+    // by the script are correct. However when loaded via dlopen the first
+    // 0x1000 are skipped ...
+#if defined(__linux__)
+    if (offset >= 0x1000)
+      offset -= 0x1000;
+    else
+      fprintf(stderr, "Warning: offset is < 0x1000: %x\n", offset);
+#endif
+
     if (offset > lib_size)
       FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large",
             offset, lib_size);
@@ -482,11 +484,10 @@ void setup_trap_instrumentation() {
     // this will be ARM and AARCH64
     // for ARM we will need to identify if the code is in thumb or ARM
 #error "non x86_64 not supported yet"
-    //__arm__
+    //__arm__:
     // linux thumb: 0xde01
-    // linux thumb2: 0xf7f0a000
     // linux arm: 0xe7f001f0
-    //__aarch64__
+    //__aarch64__:
     // linux aarch64: 0xd4200000
 #endif
 
@@ -510,6 +511,8 @@ void setup_trap_instrumentation() {
 
 }
 
+/* the signal handler for the traps / debugging interrupts
+   No debug output here because this would cost speed      */
 static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
 
   uint64_t addr;
@@ -525,31 +528,25 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
 #error "Unsupported platform"
 #endif
 
-  if (debug)
-    fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr,
-            si->si_addr);
+  //fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, si->si_addr);
 
   // If the trap didn't come from our instrumentation, then we probably will
   // just segfault here
   uint8_t *faultaddr;
-  if (si->si_addr)
+  if (unlikely(si->si_addr))
     faultaddr = (u8 *)si->si_addr - 1;
   else
     faultaddr = (u8 *)addr;
-  // u8 *loc = SHADOW(faultaddr);
-  if (debug)
-    fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
+  //if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
   uint32_t shadow = *SHADOW(faultaddr);
   uint8_t  orig_byte = shadow & 0xff;
   uint32_t index = shadow >> 8;
 
-  if (debug)
-    fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", shadow,
-            orig_byte, index);
+  //if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", shadow, orig_byte, index);
 
   // Index zero is invalid so that it is still possible to catch actual trap
   // instructions in instrumented libraries.
-  if (index == 0) abort();
+  if (unlikely(index == 0)) abort();
 
   // Restore original instruction
   *faultaddr = orig_byte;
@@ -561,6 +558,7 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
 /* here you need to specify the parameter for the target function */
 static void *(*o_function)(u8 *buf, int len);
 
+/* the MAIN function */
 int main(int argc, char *argv[]) {
 
   pid = getpid();
@@ -579,11 +577,11 @@ int main(int argc, char *argv[]) {
   //         inclusive of the cleanup functions
   //         NOTE: above the main() you have to define the functions!
 
-  /* setup the target */
   void *dl = dlopen("./libtestinstr.so", RTLD_LAZY);
   if (!dl) FATAL("could not find target library");
   o_function = dlsym(dl, "testinstr");
   if (!o_function) FATAL("could not resolve target function from library");
+  if (debug) fprintf(stderr, "Function address: %p\n", o_function);
 
   // END STEP 2
 
@@ -595,42 +593,33 @@ int main(int argc, char *argv[]) {
 
   while (1) {
 
-#ifdef USE_FORK
     if ((pid = fork()) == -1) PFATAL("fork failed");
+
     if (pid) {
 
       u32 status;
       if (waitpid(pid, &status, 0) < 0) exit(1);
+      /* report the test case is done and wait for the next */
+      __afl_end_testcase(status);
 
     } else {
 
-#endif
 
       pid = getpid();
       while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) {
 
-#ifdef USE_FORK
+        // in this function the fuzz magic happens, this is STEP 3
         fuzz();
-#else
-      if (pthread_create(&__afl_thread, NULL, (void *)fuzz, NULL) != 0)
-        PFATAL("cannot create thread");
-      pthread_join(__afl_thread, NULL);
-#endif
 
-        /* report the test case is done and wait for the next */
-        __afl_end_testcase();
-#ifdef USE_FORK
-        exit(0);
-#endif
+        // we can use _exit which is faster because our target library
+        // was loaded via dlopen and there cannot have deconstructors
+        // registered.
+        _exit(0);
 
       }
 
-#ifdef USE_FORK
-
     }
 
-#endif
-
   }
 
   return 0;
@@ -651,9 +640,4 @@ static void fuzz() {
 
   // END STEP 3
 
-#ifndef USE_FORK
-  pthread_exit(NULL);
-#endif
-
 }
-
diff --git a/examples/afl_untracer/patches.txt b/examples/afl_untracer/patches.txt
new file mode 100644
index 00000000..b3063e3a
--- /dev/null
+++ b/examples/afl_untracer/patches.txt
@@ -0,0 +1,23 @@
+libtestinstr.so:0x2000L

+0x1050L

+0x1063L

+0x106fL

+0x1078L

+0x1080L

+0x10a4L

+0x10b0L

+0x10b8L

+0x10c0L

+0x10c9L

+0x10d7L

+0x10e3L

+0x10f8L

+0x1100L

+0x1105L

+0x111aL

+0x1135L

+0x1143L

+0x114eL

+0x115cL

+0x116aL

+0x116bL

diff --git a/include/forkserver.h b/include/forkserver.h
index 3c473572..7e7784f5 100644
--- a/include/forkserver.h
+++ b/include/forkserver.h
@@ -43,6 +43,7 @@ typedef struct afl_forkserver {
 
   s32 fsrv_pid,                         /* PID of the fork server           */
       child_pid,                        /* PID of the fuzzed program        */
+      child_status,                     /* waitpid result for the child     */
       out_dir_fd;                       /* FD of the lock file              */
 
   s32 out_fd,                           /* Persistent fd for fsrv->out_file */
diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c
index d5a60077..a9e2175d 100644
--- a/src/afl-forkserver.c
+++ b/src/afl-forkserver.c
@@ -790,8 +790,6 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
   s32 res;
   u32 exec_ms;
 
-  int status = 0;
-
   /* After this memset, fsrv->trace_bits[] are effectively volatile, so we
      must prevent any earlier operations from venturing into that
      territory. */
@@ -821,7 +819,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
   if (fsrv->child_pid <= 0) { FATAL("Fork server is misbehaving (OOM?)"); }
 
-  exec_ms = read_timed(fsrv->fsrv_st_fd, &status, 4, timeout, stop_soon_p);
+  exec_ms = read_timed(fsrv->fsrv_st_fd, &fsrv->child_status, 4, timeout, stop_soon_p);
 
   if (exec_ms > timeout) {
 
@@ -830,7 +828,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
     kill(fsrv->child_pid, SIGKILL);
     fsrv->last_run_timed_out = 1;
-    if (read(fsrv->fsrv_st_fd, &status, 4) < 4) { exec_ms = 0; }
+    if (read(fsrv->fsrv_st_fd, &fsrv->child_status, 4) < 4) { exec_ms = 0; }
 
   }
 
@@ -862,7 +860,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
   }
 
-  if (!WIFSTOPPED(status)) { fsrv->child_pid = 0; }
+  if (!WIFSTOPPED(fsrv->child_status)) { fsrv->child_pid = 0; }
 
   fsrv->total_execs++;
 
@@ -874,9 +872,9 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
 
   /* Report outcome to caller. */
 
-  if (WIFSIGNALED(status) && !*stop_soon_p) {
+  if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) {
 
-    fsrv->last_kill_signal = WTERMSIG(status);
+    fsrv->last_kill_signal = WTERMSIG(fsrv->child_status);
 
     if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL) {
 
@@ -891,7 +889,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
   /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
      must use a special exit code. */
 
-  if (fsrv->uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
+  if (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) {
 
     fsrv->last_kill_signal = 0;
     return FSRV_RUN_CRASH;