about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
-rw-r--r--Makefile8
-rw-r--r--qemu_mode/libcompcov/Makefile4
-rw-r--r--qemu_mode/unsigaction/Makefile2
-rwxr-xr-xtest/test.sh70
-rw-r--r--unicorn_mode/README.md36
-rwxr-xr-xunicorn_mode/build_unicorn_support.sh97
-rwxr-xr-xunicorn_mode/samples/compcov_x64/compcov_target.elfbin13200 -> 13896 bytes
-rw-r--r--unicorn_mode/samples/compcov_x64/compcov_test_harness.py63
-rw-r--r--unicorn_mode/samples/simple/simple_test_harness.py69
-rw-r--r--unicorn_mode/samples/simple/simple_test_harness_alt.py179
m---------unicorn_mode/unicorn0
13 files changed, 321 insertions, 214 deletions
diff --git a/.gitignore b/.gitignore
index ac3a653b..1e653a08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,4 @@ afl-whatsup.8
 qemu_mode/libcompcov/compcovtest
 as
 qemu_mode/qemu-*
-unicorn_mode/unicorn
-unicorn_mode/unicorn-*
-unicorn_mode/*.tar.gz
+core\.*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..05bd3b04
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "unicorn_mode/unicorn"]
+	path = unicorn_mode/unicorn
+	url = https://github.com/vanhauser-thc/unicorn.git
diff --git a/Makefile b/Makefile
index 880dfc67..1f75e45d 100644
--- a/Makefile
+++ b/Makefile
@@ -162,7 +162,7 @@ help:
 	@echo "distrib: everything (for both binary-only and source code fuzzing)"
 	@echo "man: creates simple man pages from the help option of the programs"
 	@echo "install: installs everything you have compiled with the build option above"
-	@echo "clean: cleans everything. for qemu_mode and unicorn_mode it means it deletes all downloads as well"
+	@echo "clean: cleans everything. for qemu_mode it means it deletes all downloads as well"
 	@echo "code-format: format the code, do this before you commit and send a PR please!"
 	@echo "tests: this runs the test framework. It is more catered for the developers, but if you run into problems this helps pinpointing the problem"
 	@echo "document: creates afl-fuzz-document which will only do one run and save all manipulated inputs into out/queue/mutations"
@@ -278,7 +278,6 @@ code-format:
 	./.custom-format.py -i qemu_mode/libcompcov/*.c
 	./.custom-format.py -i qemu_mode/libcompcov/*.cc
 	./.custom-format.py -i qemu_mode/libcompcov/*.h
-	./.custom-format.py -i unicorn_mode/patches/*.h
 	./.custom-format.py -i *.h
 	./.custom-format.py -i *.c
 
@@ -311,8 +310,8 @@ all_done: test_build
 .NOTPARALLEL: clean
 
 clean:
-	rm -f $(PROGS) libradamsa.so afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so unicorn_mode/24f55a7973278f20f0de21b904851d99d4716263.tar.gz *.8
-	rm -rf out_dir qemu_mode/qemu-3.1.1 unicorn_mode/unicorn *.dSYM */*.dSYM
+	rm -f $(PROGS) libradamsa.so afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.1.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast *.so *.8
+	rm -rf out_dir qemu_mode/qemu-3.1.1 *.dSYM */*.dSYM
 	-$(MAKE) -C llvm_mode clean
 	-$(MAKE) -C gcc_plugin clean
 	$(MAKE) -C libdislocator clean
@@ -320,6 +319,7 @@ clean:
 	$(MAKE) -C qemu_mode/unsigaction clean
 	$(MAKE) -C qemu_mode/libcompcov clean
 	$(MAKE) -C src/third_party/libradamsa/ clean
+	$(MAKE) -C unicorn_mode/unicorn clean
 
 distrib: all radamsa
 	-$(MAKE) -C llvm_mode
diff --git a/qemu_mode/libcompcov/Makefile b/qemu_mode/libcompcov/Makefile
index d078ae06..e827cbd8 100644
--- a/qemu_mode/libcompcov/Makefile
+++ b/qemu_mode/libcompcov/Makefile
@@ -22,7 +22,7 @@ CFLAGS      ?= -O3 -funroll-loops -I ../../include/
 CFLAGS      += -Wall -Wno-unused-result -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
 LDFLAGS     += -ldl
 
-all: libcompcov.so compcovtest
+all: libcompcov.so
 
 libcompcov.so: libcompcov.so.c ../../config.h
 	$(CC) $(CFLAGS) -shared -fPIC $< -o ../../$@ $(LDFLAGS)
@@ -34,7 +34,7 @@ clean:
 	rm -f ../../libcompcov.so compcovtest
 
 compcovtest:	compcovtest.cc
-	$(CXX) $< -o $@ 
+	$(CXX) -std=c++11 $< -o $@ 
 
 install: all
 	install -m 755 ../../libcompcov.so $${DESTDIR}$(HELPER_PATH)
diff --git a/qemu_mode/unsigaction/Makefile b/qemu_mode/unsigaction/Makefile
index 9aa96330..02dc2c79 100644
--- a/qemu_mode/unsigaction/Makefile
+++ b/qemu_mode/unsigaction/Makefile
@@ -18,7 +18,7 @@ ifndef AFL_NO_X86
 all: lib_i386 lib_amd64
 
 lib_i386:
-	$(CC) -m32 -fPIC -shared unsigaction.c -o unsigaction32.so
+	$(CC) -m32 -fPIC -shared unsigaction.c -o unsigaction32.so || echo "Cannot build unsigation32"
 
 lib_amd64:
 	$(CC) -fPIC -shared unsigaction.c -o unsigaction64.so
diff --git a/test/test.sh b/test/test.sh
index ee497298..a75f991d 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -550,44 +550,50 @@ test -d ../unicorn_mode/unicorn && {
   test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && {
     {
       # travis workaround
-      PY=`which python2.7`
-      test "$PY" = "/opt/pyenv/shims/python2.7" -a -x /usr/bin/python2.7 && PY=/usr/bin/python2.7
+      PY=`which python`
+      test "$PY" = "/opt/pyenv/shims/python" -a -x /usr/bin/python && PY=/usr/bin/python
       mkdir -p in
       echo 0 > in/in
       $ECHO "$GREY[*] Using python binary $PY"
-      $ECHO "$GREY[*] running afl-fuzz for unicorn_mode, this will take approx 25 seconds"
+      if ! $PY -c 'import unicornafl' 2> /dev/null ; then
+        $ECHO "$YELLOW[-] we cannot test unicorn_mode because it is not present"
+      else
       {
-        ../afl-fuzz -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/simple/simple_test_harness.py @@ >>errors 2>&1
-      } >>errors 2>&1
-      test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
-        $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode"
-      } || {
-        echo CUT------------------------------------------------------------------CUT
-        cat errors
-        echo CUT------------------------------------------------------------------CUT
-        $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode"
-        CODE=1
-      }
-      rm -f errors
+        $ECHO "$GREY[*] running afl-fuzz for unicorn_mode, this will take approx 25 seconds"
+        {
+          ../afl-fuzz -V25 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/simple/simple_test_harness.py @@ >>errors 2>&1
+        } >>errors 2>&1
+        test -n "$( ls out/queue/id:000002* 2> /dev/null )" && {
+          $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode"
+        } || {
+          echo CUT------------------------------------------------------------------CUT
+          cat errors
+          echo CUT------------------------------------------------------------------CUT
+          $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode"
+          CODE=1
+        }
+        rm -f errors
 
-      printf '\x01\x01' > in/in
-      # This seed is close to the first byte of the comparison.
-      # If CompCov works, a new tuple will appear in the map => new input in queue
-      $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds"
-      {
-        export AFL_COMPCOV_LEVEL=2
-        ../afl-fuzz -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1
-      } >>errors 2>&1
-      test -n "$( ls out/queue/id:000001* 2> /dev/null )" && {
-        $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode compcov"
-      } || {
-        echo CUT------------------------------------------------------------------CUT
-        cat errors
-        echo CUT------------------------------------------------------------------CUT
-        $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode compcov"
-        CODE=1
+        printf '\x01\x01' > in/in
+        # This seed is close to the first byte of the comparison.
+        # If CompCov works, a new tuple will appear in the map => new input in queue
+        $ECHO "$GREY[*] running afl-fuzz for unicorn_mode compcov, this will take approx 35 seconds"
+        {
+          export AFL_COMPCOV_LEVEL=2
+          ../afl-fuzz -V35 -U -i in -o out -d -- "$PY" ../unicorn_mode/samples/compcov_x64/compcov_test_harness.py @@ >>errors 2>&1
+        } >>errors 2>&1
+        test -n "$( ls out/queue/id:000001* 2> /dev/null )" && {
+          $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode compcov"
+        } || {
+          echo CUT------------------------------------------------------------------CUT
+          cat errors
+          echo CUT------------------------------------------------------------------CUT
+          $ECHO "$RED[!] afl-fuzz is not working correctly with unicorn_mode compcov"
+          CODE=1
+        }
+        rm -rf in out errors
       }
-      rm -rf in out errors
+      fi
     }
   } || {
     $ECHO "$RED[-] missing sample binaries in unicorn_mode/samples/ - what is going on??"
diff --git a/unicorn_mode/README.md b/unicorn_mode/README.md
index 8f381b59..904ea624 100644
--- a/unicorn_mode/README.md
+++ b/unicorn_mode/README.md
@@ -20,7 +20,7 @@ but at least we're able to use AFL on these binaries, right?
 
 ## 2) How to use
 
-Requirements: you need an installed python2 environment.
+Requirements: you need an installed python environment.
 
 ### Building AFL's Unicorn Mode
 
@@ -31,11 +31,8 @@ features:
   $ cd unicorn_mode
   $ ./build_unicorn_support.sh
 
-NOTE: This script downloads a Unicorn Engine commit that has been tested 
-and is stable-ish from the Unicorn github page. If you are offline, you'll need 
-to hack up this script a little bit and supply your own copy of Unicorn's latest 
-stable release. It's not very hard, just check out the beginning of the 
-build_unicorn_support.sh script and adjust as necessary.
+NOTE: This script checks out a Unicorn Engine fork as submodule that has been tested 
+and is stable-ish, based on the unicorn engine master. 
 
 Building Unicorn will take a little bit (~5-10 minutes). Once it completes 
 it automatically compiles a sample application and verify that it works.
@@ -51,11 +48,10 @@ To really use unicorn-mode effectively you need to prepare the following:
 		+ Quality/speed of results will depend greatly on quality of starting 
 		  samples
 		+ See AFL's guidance on how to create a sample corpus
-	* Unicorn-based test harness which:
+	* Unicornafl-based test harness which:
 		+ Adds memory map regions
 		+ Loads binary code into memory		
-		+ Emulates at least one instruction*
-			+ Yeah, this is lame. See 'Gotchas' section below for more info		
+		+ Calls uc.afl_fuzz() / uc.afl_start_forkserver
 		+ Loads and verifies data to fuzz from a command-line specified file
 			+ AFL will provide mutated inputs by changing the file passed to 
 			  the test harness
@@ -103,16 +99,20 @@ for the x86, x86_64 and ARM targets.
 
 ## 4) Gotchas, feedback, bugs
 
-To make sure that AFL's fork server starts up correctly the Unicorn test 
-harness script must emulate at least one instruction before loading the
-data that will be fuzzed from the input file. It doesn't matter what the
-instruction is, nor if it is valid. This is an artifact of how the fork-server
-is started and could likely be fixed with some clever re-arranging of the
-patches applied to Unicorn.
+Running the build script builds Unicornafl and its python bindings and installs 
+them on your system. 
+This installation will leave any existing Unicorn installations untouched.
+If you want to use unicornafl instead of unicorn in a script,
+replace all `unicorn` imports with `unicornafl` inputs, everything else should "just work".
+If you use 3rd party code depending on unicorn, you can use unicornafl monkeypatching:
+Before importing anything that depends on unicorn, do:
 
-Running the build script builds Unicorn and its python bindings and installs 
-them on your system. This installation will supersede any existing Unicorn
-installation with the patched afl-unicorn version.
+```python
+import unicornafl
+unicornafl.monkeypatch()
+```
+
+This will replace all unicorn imports with unicornafl inputs.
 
 Refer to the unicorn_mode/samples/arm_example/arm_tester.c for an example
 of how to do this properly! If you don't get this right, AFL will not 
diff --git a/unicorn_mode/build_unicorn_support.sh b/unicorn_mode/build_unicorn_support.sh
index e987e15a..707e47bb 100755
--- a/unicorn_mode/build_unicorn_support.sh
+++ b/unicorn_mode/build_unicorn_support.sh
@@ -33,9 +33,6 @@
 # You must make sure that Unicorn Engine is not already installed before
 # running this script. If it is, please uninstall it first.
 
-UNICORN_URL="https://github.com/unicorn-engine/unicorn/archive/24f55a7973278f20f0de21b904851d99d4716263.tar.gz"
-UNICORN_SHA384="7180d47ca52c99b4c073a343a2ead91da1a829fdc3809f3ceada5d872e162962eab98873a8bc7971449d5f34f41fdb93"
-
 echo "================================================="
 echo "Unicorn-AFL build script"
 echo "================================================="
@@ -52,7 +49,7 @@ if [ ! "$PLT" = "Linux" ] && [ ! "$PLT" = "Darwin" ] && [ ! "$PLT" = "FreeBSD" ]
   
 fi
 
-if [ ! -f "patches/afl-unicorn-cpu-inl.h" -o ! -f "../config.h" ]; then
+if [ ! -f "../config.h" ]; then
 
   echo "[-] Error: key files not found - wrong working directory?"
   exit 1
@@ -66,40 +63,30 @@ if [ ! -f "../afl-showmap" ]; then
 
 fi
 
+PYTHONBIN=python
+MAKECMD=make
+EASY_INSTALL='easy_install'
+TARCMD=tar
+
 if [ "$PLT" = "Linux" ]; then
-  CKSUMCMD='sha384sum --'
-  PYTHONBIN=python2
-  MAKECMD=make
   CORES=`nproc`
-  TARCMD=tar
-  EASY_INSTALL=easy_install
 fi
 
 if [ "$PLT" = "Darwin" ]; then
-  CKSUMCMD="shasum -a 384"
-  PYTHONBIN=python2.7
-  MAKECMD=make
   CORES=`sysctl hw.ncpu | cut -d' ' -f2`
   TARCMD=tar
-  EASY_INSTALL=easy_install-2.7
 fi
 
 if [ "$PLT" = "FreeBSD" ]; then
-  CKSUMCMD="sha384 -q"
-  PYTHONBIN=python2.7
   MAKECMD=gmake
   CORES=`sysctl hw.ncpu | cut -d' ' -f2`
   TARCMD=gtar
-  EASY_INSTALL=easy_install-2.7
 fi
 
 if [ "$PLT" = "NetBSD" ] || [ "$PLT" = "OpenBSD" ]; then
-  CKSUMCMD="cksum -a sha384 -q"
-  PYTHONBIN=python2.7
   MAKECMD=gmake
   CORES=`sysctl hw.ncpu | cut -d'=' -f2`
   TARCMD=gtar
-  EASY_INSTALL=easy_install-2.7
 fi
 
 for i in wget $PYTHONBIN automake autoconf $MAKECMD $TARCMD; do
@@ -108,7 +95,7 @@ for i in wget $PYTHONBIN automake autoconf $MAKECMD $TARCMD; do
 
   if [ "$T" = "" ]; then
 
-    echo "[-] Error: '$i' not found. Run 'sudo apt-get install $i'."
+    echo "[-] Error: '$i' not found. Run 'sudo apt-get install $i' or similar."
     exit 1
 
   fi
@@ -136,51 +123,13 @@ fi
 
 echo "[+] All checks passed!"
 
-ARCHIVE="`basename -- "$UNICORN_URL"`"
-
-CKSUM=`$CKSUMCMD "$ARCHIVE" 2>/dev/null | cut -d' ' -f1`
-
-if [ ! "$CKSUM" = "$UNICORN_SHA384" ]; then
-
-  echo "[*] Downloading Unicorn v1.0.1 from the web..."
-  rm -f "$ARCHIVE"
-  OK=
-  while [ -z "$OK" ]; do
-    wget -c -O "$ARCHIVE" -- "$UNICORN_URL" && OK=1
-  done
-
-  CKSUM=`$CKSUMCMD "$ARCHIVE" 2>/dev/null | cut -d' ' -f1`
-
-fi
-
-if [ "$CKSUM" = "$UNICORN_SHA384" ]; then
-
-  echo "[+] Cryptographic signature on $ARCHIVE checks out."
-
-else
-
-  echo "[-] Error: signature mismatch on $ARCHIVE (perhaps download error?)."
-  exit 1
-
-fi
-
-echo "[*] Uncompressing archive (this will take a while)..."
-
-rm -rf "unicorn" || exit 1
-mkdir "unicorn" || exit 1
-$TARCMD xzf "$ARCHIVE" -C ./unicorn --strip-components=1 || exit 1
-
-echo "[+] Unpacking successful."
-
-#rm -rf "$ARCHIVE" || exit 1
-
-echo "[*] Applying patches..."
-
-cp patches/*.h unicorn || exit 1
-patch -p1 --directory unicorn < patches/patches.diff || exit 1
-patch -p1 --directory unicorn < patches/compcov.diff || exit 1
+echo "[*] Making sure unicornafl is checked out"
+git submodule init || exit 1
+git submodule update || exit 1
+echo "[+] Got unicornafl."
 
-echo "[+] Patching done."
+echo "[*] making sure config.h matches"
+cp "../config.h" "./unicorn/" || exit 1
 
 echo "[*] Configuring Unicorn build..."
 
@@ -188,8 +137,9 @@ cd "unicorn" || exit 1
 
 echo "[+] Configuration complete."
 
-echo "[*] Attempting to build Unicorn (fingers crossed!)..."
+echo "[*] Attempting to build unicornafl (fingers crossed!)..."
 
+$MAKECMD clean  # make doesn't seem to work for unicorn
 UNICORN_QEMU_FLAGS="--python=$PYTHONBIN" $MAKECMD -j$CORES || exit 1
 
 echo "[+] Build process successful!"
@@ -197,20 +147,21 @@ echo "[+] Build process successful!"
 echo "[*] Installing Unicorn python bindings..."
 cd bindings/python || exit 1
 if [ -z "$VIRTUAL_ENV" ]; then
-  echo "[*] Info: Installing python unicorn using --user"
-  $PYTHONBIN setup.py install --user  --prefix=|| exit 1
+  echo "[*] Info: Installing python unicornafl using --user"
+  $PYTHONBIN setup.py install --user --force --prefix=|| exit 1
 else
-  echo "[*] Info: Installing python unicorn to virtualenv: $VIRTUAL_ENV"
-  $PYTHONBIN setup.py install || exit 1
+  echo "[*] Info: Installing python unicornafl to virtualenv: $VIRTUAL_ENV"
+  $PYTHONBIN setup.py install --force || exit 1
 fi
-export LIBUNICORN_PATH='$(pwd)' # in theory, this allows to switch between afl-unicorn and unicorn so files.
+# export LIBUNICORN_PATH='$(pwd)' # in theory, this allows to switch between afl-unicorn and unicorn so files.
+echo '[*] If needed, you can (re)install the bindigns from `./unicorn/bindings/python` using `python setup.py install`'
 
 cd ../../ || exit 1
 
-echo "[+] Unicorn bindings installed successfully."
+echo "[*] Unicornafl bindings installed successfully."
 
 # Compile the sample, run it, verify that it works!
-echo "[*] Testing unicorn-mode functionality by running a sample test harness under afl-unicorn"
+echo "[*] Testing unicornafl python functionality by running a sample test harness"
 
 cd ../samples/simple || exit 1
 
@@ -222,6 +173,8 @@ if [ -s .test-instr0 ]
 then
   
   echo "[+] Instrumentation tests passed. "
+  echo '[+] Make sure to adapt older scripts to `import unicornafl` and use `uc.afl_forkserver_start`'
+  echo '    or `uc.afl_fuzz` to kick off fuzzing.'
   echo "[+] All set, you can now use Unicorn mode (-U) in afl-fuzz!"
   RETVAL=0
 
diff --git a/unicorn_mode/samples/compcov_x64/compcov_target.elf b/unicorn_mode/samples/compcov_x64/compcov_target.elf
index 0f1ad916..abe972dd 100755
--- a/unicorn_mode/samples/compcov_x64/compcov_target.elf
+++ b/unicorn_mode/samples/compcov_x64/compcov_target.elf
Binary files differdiff --git a/unicorn_mode/samples/compcov_x64/compcov_test_harness.py b/unicorn_mode/samples/compcov_x64/compcov_test_harness.py
index 5698cbc8..9a5da520 100644
--- a/unicorn_mode/samples/compcov_x64/compcov_test_harness.py
+++ b/unicorn_mode/samples/compcov_x64/compcov_test_harness.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 """ 
    Simple test harness for AFL's Unicorn Mode.
 
@@ -17,8 +18,8 @@ import argparse
 import os
 import signal
 
-from unicorn import *
-from unicorn.x86_const import *
+from unicornafl import *
+from unicornafl.x86_const import *
 
 # Path to the file containing the binary to emulate
 BINARY_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'compcov_target.bin')
@@ -120,51 +121,39 @@ def main():
     uc.mem_map(STACK_ADDRESS, STACK_SIZE)
     uc.reg_write(UC_X86_REG_RSP, STACK_ADDRESS + STACK_SIZE)
 
-    #-----------------------------------------------------
-    # Emulate 1 instruction to kick off AFL's fork server
-    #   THIS MUST BE DONE BEFORE LOADING USER DATA! 
-    #   If this isn't done every single run, the AFL fork server 
-    #   will not be started appropriately and you'll get erratic results!
-    #   It doesn't matter what this returns with, it just has to execute at
-    #   least one instruction in order to get the fork server started.
-
-    # Execute 1 instruction just to startup the forkserver
-    print("Starting the AFL forkserver by executing 1 instruction")
-    try:
-        uc.emu_start(uc.reg_read(UC_X86_REG_RIP), 0, 0, count=1)
-    except UcError as e:
-        print("ERROR: Failed to execute a single instruction (error: {})!".format(e))
-        return
+    # Mapping a location to write our buffer to
+    uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
+
 
     #-----------------------------------------------
     # Load the mutated input and map it into memory
 
-    # Load the mutated input from disk
-    print("Loading data input from {}".format(args.input_file))
-    input_file = open(args.input_file, 'rb')
-    input = input_file.read()
-    input_file.close()
+    def place_input_callback(uc, input, _, data):
+        """
+        Callback that loads the mutated input into memory.
+        """
+        # Load the mutated input from disk
+        input_file = open(args.input_file, 'rb')
+        input = input_file.read()
+        input_file.close()
 
-    # Apply constraints to the mutated input
-    if len(input) > DATA_SIZE_MAX:
-        print("Test input is too long (> {} bytes)".format(DATA_SIZE_MAX))
-        return
+        # Apply constraints to the mutated input
+        if len(input) > DATA_SIZE_MAX:
+            return
 
-    # Write the mutated command into the data buffer
-    uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
-    uc.mem_write(DATA_ADDRESS, input)
+        # Write the mutated command into the data buffer
+        uc.mem_write(DATA_ADDRESS, input)
 
     #------------------------------------------------------------
     # Emulate the code, allowing it to process the mutated input
 
-    print("Executing until a crash or execution reaches 0x{0:016x}".format(end_address))
-    try:
-        result = uc.emu_start(uc.reg_read(UC_X86_REG_RIP), end_address, timeout=0, count=0)
-    except UcError as e:
-        print("Execution failed with error: {}".format(e))
-        force_crash(e)
-
-    print("Done.")
+    print("Starting the AFL fuzz")
+    uc.afl_fuzz(
+        input_file=args.input_file,
+        place_input_callback=place_input_callback,
+        exits=[end_address],
+        persistent_iters=1
+    )
 
 if __name__ == "__main__":
     main()
diff --git a/unicorn_mode/samples/simple/simple_test_harness.py b/unicorn_mode/samples/simple/simple_test_harness.py
index 6d0fb399..d85ec9f5 100644
--- a/unicorn_mode/samples/simple/simple_test_harness.py
+++ b/unicorn_mode/samples/simple/simple_test_harness.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 """ 
    Simple test harness for AFL's Unicorn Mode.
 
@@ -17,8 +18,8 @@ import argparse
 import os
 import signal
 
-from unicorn import *
-from unicorn.mips_const import *
+from unicornafl import *
+from unicornafl.mips_const import *
 
 # Path to the file containing the binary to emulate
 BINARY_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'simple_target.bin')
@@ -120,51 +121,29 @@ def main():
     uc.mem_map(STACK_ADDRESS, STACK_SIZE)
     uc.reg_write(UC_MIPS_REG_SP, STACK_ADDRESS + STACK_SIZE)
 
-    #-----------------------------------------------------
-    # Emulate 1 instruction to kick off AFL's fork server
-    #   THIS MUST BE DONE BEFORE LOADING USER DATA! 
-    #   If this isn't done every single run, the AFL fork server 
-    #   will not be started appropriately and you'll get erratic results!
-    #   It doesn't matter what this returns with, it just has to execute at
-    #   least one instruction in order to get the fork server started.
-
-    # Execute 1 instruction just to startup the forkserver
-    print("Starting the AFL forkserver by executing 1 instruction")
-    try:
-        uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), 0, 0, count=1)
-    except UcError as e:
-        print("ERROR: Failed to execute a single instruction (error: {})!".format(e))
-        return
-
-    #-----------------------------------------------
-    # Load the mutated input and map it into memory
-
-    # Load the mutated input from disk
-    print("Loading data input from {}".format(args.input_file))
-    input_file = open(args.input_file, 'rb')
-    input = input_file.read()
-    input_file.close()
-
-    # Apply constraints to the mutated input
-    if len(input) > DATA_SIZE_MAX:
-        print("Test input is too long (> {} bytes)".format(DATA_SIZE_MAX))
-        return
-
-    # Write the mutated command into the data buffer
+    # reserve some space for data
     uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
-    uc.mem_write(DATA_ADDRESS, input)
 
-    #------------------------------------------------------------
-    # Emulate the code, allowing it to process the mutated input
-
-    print("Executing until a crash or execution reaches 0x{0:016x}".format(end_address))
-    try:
-        result = uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=0, count=0)
-    except UcError as e:
-        print("Execution failed with error: {}".format(e))
-        force_crash(e)
-
-    print("Done.")
+    #-----------------------------------------------------
+    # Set up a callback to place input data (do little work here, it's called for every single iteration)
+    # We did not pass in any data and don't use persistent mode, so we can ignore these params.
+    # Be sure to check out the docstrings for the uc.afl_* functions.
+    def place_input_callback(uc, input, persistent_round, data):
+        # Load the mutated input from disk
+        input_file = open(args.input_file, 'rb')
+        input = input_file.read()
+        input_file.close()
+
+        # Apply constraints to the mutated input
+        if len(input) > DATA_SIZE_MAX:
+            #print("Test input is too long (> {} bytes)")
+            return False
+
+        # Write the mutated command into the data buffer
+        uc.mem_write(DATA_ADDRESS, input)
+
+    # Start the fuzzer.
+    uc.afl_fuzz(args.input_file, place_input_callback, [end_address])
 
 if __name__ == "__main__":
     main()
diff --git a/unicorn_mode/samples/simple/simple_test_harness_alt.py b/unicorn_mode/samples/simple/simple_test_harness_alt.py
new file mode 100644
index 00000000..9c3dbc93
--- /dev/null
+++ b/unicorn_mode/samples/simple/simple_test_harness_alt.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+""" 
+   Alternative simple test harness for Unicornafl.
+   It is slower but compatible with anything that uses unicorn.
+
+   Have a look at `unicornafl.monkeypatch()` for an easy way to fuzz unicorn projects.
+
+   This loads the simple_target.bin binary (precompiled as MIPS code) into
+   Unicorn's memory map for emulation, places the specified input into
+   simple_target's buffer (hardcoded to be at 0x300000), and executes 'main()'.
+   If any crashes occur during emulation, this script throws a matching signal
+   to tell AFL that a crash occurred.
+
+   Run under AFL as follows:
+
+   $ cd <afl_path>/unicorn_mode/samples/simple/
+   $ ../../../afl-fuzz -U -m none -i ./sample_inputs -o ./output -- python simple_test_harness_alt.py @@ 
+"""
+
+import argparse
+import os
+import signal
+
+from unicornafl import *
+from unicornafl.mips_const import *
+
+# Path to the file containing the binary to emulate
+BINARY_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'simple_target.bin')
+
+# Memory map for the code to be tested
+CODE_ADDRESS  = 0x00100000  # Arbitrary address where code to test will be loaded
+CODE_SIZE_MAX = 0x00010000  # Max size for the code (64kb)
+STACK_ADDRESS = 0x00200000  # Address of the stack (arbitrarily chosen)
+STACK_SIZE	  = 0x00010000  # Size of the stack (arbitrarily chosen)
+DATA_ADDRESS  = 0x00300000  # Address where mutated data will be placed
+DATA_SIZE_MAX = 0x00010000  # Maximum allowable size of mutated data
+
+try:
+    # If Capstone is installed then we'll dump disassembly, otherwise just dump the binary.
+    from capstone import *
+    cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN)
+    def unicorn_debug_instruction(uc, address, size, user_data):
+        mem = uc.mem_read(address, size)
+        for (cs_address, cs_size, cs_mnemonic, cs_opstr) in cs.disasm_lite(bytes(mem), size):
+            print("    Instr: {:#016x}:\t{}\t{}".format(address, cs_mnemonic, cs_opstr))
+except ImportError:
+    def unicorn_debug_instruction(uc, address, size, user_data):
+        print("    Instr: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))    
+
+def unicorn_debug_block(uc, address, size, user_data):
+    print("Basic Block: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))
+    
+def unicorn_debug_mem_access(uc, access, address, size, value, user_data):
+    if access == UC_MEM_WRITE:
+        print("        >>> Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value))
+    else:
+        print("        >>> Read: addr=0x{0:016x} size={1}".format(address, size))    
+
+def unicorn_debug_mem_invalid_access(uc, access, address, size, value, user_data):
+    if access == UC_MEM_WRITE_UNMAPPED:
+        print("        >>> INVALID Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value))
+    else:
+        print("        >>> INVALID Read: addr=0x{0:016x} size={1}".format(address, size))   
+
+def force_crash(uc_error):
+    # This function should be called to indicate to AFL that a crash occurred during emulation.
+    # Pass in the exception received from Uc.emu_start()
+    mem_errors = [
+        UC_ERR_READ_UNMAPPED, UC_ERR_READ_PROT, UC_ERR_READ_UNALIGNED,
+        UC_ERR_WRITE_UNMAPPED, UC_ERR_WRITE_PROT, UC_ERR_WRITE_UNALIGNED,
+        UC_ERR_FETCH_UNMAPPED, UC_ERR_FETCH_PROT, UC_ERR_FETCH_UNALIGNED,
+    ]
+    if uc_error.errno in mem_errors:
+        # Memory error - throw SIGSEGV
+        os.kill(os.getpid(), signal.SIGSEGV)
+    elif uc_error.errno == UC_ERR_INSN_INVALID:
+        # Invalid instruction - throw SIGILL
+        os.kill(os.getpid(), signal.SIGILL)
+    else:
+        # Not sure what happened - throw SIGABRT
+        os.kill(os.getpid(), signal.SIGABRT)
+
+def main():
+
+    parser = argparse.ArgumentParser(description="Test harness for simple_target.bin")
+    parser.add_argument('input_file', type=str, help="Path to the file containing the mutated input to load")
+    parser.add_argument('-d', '--debug', default=False, action="store_true", help="Enables debug tracing")
+    args = parser.parse_args()
+
+    # Instantiate a MIPS32 big endian Unicorn Engine instance
+    uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
+
+    if args.debug:
+        uc.hook_add(UC_HOOK_BLOCK, unicorn_debug_block)
+        uc.hook_add(UC_HOOK_CODE, unicorn_debug_instruction)
+        uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, unicorn_debug_mem_access)
+        uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_READ_INVALID, unicorn_debug_mem_invalid_access)
+
+    #---------------------------------------------------
+    # Load the binary to emulate and map it into memory
+
+    print("Loading data input from {}".format(args.input_file))
+    binary_file = open(BINARY_FILE, 'rb')
+    binary_code = binary_file.read()
+    binary_file.close()
+
+    # Apply constraints to the mutated input
+    if len(binary_code) > CODE_SIZE_MAX:
+        print("Binary code is too large (> {} bytes)".format(CODE_SIZE_MAX))
+        return
+
+    # Write the mutated command into the data buffer
+    uc.mem_map(CODE_ADDRESS, CODE_SIZE_MAX)
+    uc.mem_write(CODE_ADDRESS, binary_code)
+
+    # Set the program counter to the start of the code
+    start_address = CODE_ADDRESS          # Address of entry point of main()
+    end_address   = CODE_ADDRESS + 0xf4   # Address of last instruction in main()
+    uc.reg_write(UC_MIPS_REG_PC, start_address)
+
+    #-----------------
+    # Setup the stack
+
+    uc.mem_map(STACK_ADDRESS, STACK_SIZE)
+    uc.reg_write(UC_MIPS_REG_SP, STACK_ADDRESS + STACK_SIZE)
+
+    # reserve some space for data
+    uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
+
+    #-----------------------------------------------------
+    #   Kick off AFL's fork server
+    #   THIS MUST BE DONE BEFORE LOADING USER DATA! 
+    #   If this isn't done every single run, the AFL fork server 
+    #   will not be started appropriately and you'll get erratic results!
+
+    print("Starting the AFL forkserver")
+
+    afl_mode = uc.afl_forkserver_start([end_address])
+    if afl_mode != UC_AFL_RET_NO_AFL:
+        # Disable prints for speed
+        out = lambda x, y: None
+    else:
+        out = lambda x, y: print(x.format(y))
+
+    #-----------------------------------------------
+    # Load the mutated input and map it into memory
+
+    # Load the mutated input from disk
+    out("Loading data input from {}", args.input_file)
+    input_file = open(args.input_file, 'rb')
+    input = input_file.read()
+    input_file.close()
+
+    # Apply constraints to the mutated input
+    if len(input) > DATA_SIZE_MAX:
+        out("Test input is too long (> {} bytes)", DATA_SIZE_MAX)
+        return
+
+    # Write the mutated command into the data buffer
+    uc.mem_write(DATA_ADDRESS, input)
+
+    #------------------------------------------------------------
+    # Emulate the code, allowing it to process the mutated input
+
+    out("Executing until a crash or execution reaches 0x{0:016x}", end_address)
+    try:
+        uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=0, count=0)
+    except UcError as e:
+        out("Execution failed with error: {}", e)
+        force_crash(e)
+
+    # UC_AFL_RET_ERROR = 0
+    # UC_AFL_RET_CHILD = 1
+    # UC_AFL_RET_NO_AFL = 2
+    # UC_AFL_RET_FINISHED = 3
+    out("Done. AFL Mode is {}", afl_mode)
+
+if __name__ == "__main__":
+    main()
diff --git a/unicorn_mode/unicorn b/unicorn_mode/unicorn
new file mode 160000
+Subproject aa5ebf5e16f4f5781cfe94229b41eee7ff93b35