about summary refs log tree commit diff
path: root/qemu_mode
diff options
context:
space:
mode:
Diffstat (limited to 'qemu_mode')
-rw-r--r--qemu_mode/README.md59
-rw-r--r--qemu_mode/README.persistent.md131
-rwxr-xr-xqemu_mode/build_qemu_support.sh11
-rw-r--r--qemu_mode/patches/afl-qemu-common.h22
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-inl.h70
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-translate-inl.h273
-rw-r--r--qemu_mode/patches/arm-translate-a64.diff64
-rw-r--r--qemu_mode/patches/arm-translate.diff20
-rw-r--r--qemu_mode/patches/configure.diff26
-rw-r--r--qemu_mode/patches/i386-translate.diff2
10 files changed, 567 insertions, 111 deletions
diff --git a/qemu_mode/README.md b/qemu_mode/README.md
index ccfd50e3..4198af14 100644
--- a/qemu_mode/README.md
+++ b/qemu_mode/README.md
@@ -71,31 +71,12 @@ must be an address of a basic block.
 
 ## 4) Bonus feature #2: persistent mode
 
-QEMU mode supports also persistent mode for x86 and x86_64 targets.
-The environment variable to enable it is AFL_QEMU_PERSISTENT_ADDR=`start addr`.
-In this variable you must specify the address of the function that
-has to be the body of the persistent loop.
-The code in this function must be stateless like in the LLVM persistent mode.
-The return address on stack is patched like in WinAFL in order to repeat the
-execution of such function.
-Another modality to execute the persistent loop is to specify also the
-AFL_QEMU_PERSISTENT_RET=`end addr` env variable.
-With this variable assigned, instead of patching the return address, the
-specified instruction is transformed to a jump towards `start addr`.
-Note that the format of the addresses in such variables is hex.
-
-Note that the base address of PIE binaries in QEMU user mode is 0x4000000000.
-
-With the env variable AFL_QEMU_PERSISTENT_GPR you can tell QEMU to save the
-original value of general purpose registers and restore them in each cycle.
-This allows to use as persistent loop functions that make use of arguments on 
-x86_64.
-
-With AFL_QEMU_PERSISTENT_RETADDR_OFFSET you can specify the offset from the
-stack pointer in which QEMU can find the return address when `start addr` is
-hitted.
-
-Use this mode with caution, probably it will not work at the first shot.
+AFL++'s QEMU mode now supports also persistent mode for x86, x86_64, arm
+and aarch64 targets.
+This increases the speed by several factors, however it is a bit of work to set
+up - but worth the effort.
+
+Please see the extra documentation for it: [README.persistent.md](README.persistent.md)
 
 ## 5) Bonus feature #3: CompareCoverage
 
@@ -105,6 +86,7 @@ The option that enables QEMU CompareCoverage is AFL_COMPCOV_LEVEL.
 There is also ./libcompcov/ which implements CompareCoverage for *cmp functions
 (splitting memcmp, strncmp, etc. to make these conditions easier solvable by
 afl-fuzz).
+
 AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate
 values / read-only memory. AFL_COMPCOV_LEVEL=2 instruments all
 comparison instructions and memory comparison functions when libcompcov
@@ -113,11 +95,23 @@ AFL_COMPCOV_LEVEL=3 has the same effects of AFL_COMPCOV_LEVEL=2 but enables also
 the instrumentation of the floating-point comparisons on x86 and x86_64 (experimental).
 
 Integer comparison instructions are currently instrumented only
-on the x86, x86_64 and ARM targets.
+on the x86, x86_64, arm and aarch64 targets.
 
 Highly recommended.
 
-## 6) Bonus feature #4: Wine mode
+## 6) CMPLOG mode
+
+Another new feature is CMPLOG, which is based on the redqueen project.
+Here all immidiates in CMP instructions are learned and put into a dynamic
+dictionary and applied to all locations in the input that reached that
+CMP, trying to solve and pass it.
+This is a very effective feature and it is available for x86, x86_64, arm
+and aarch64.
+
+To enable it you must pass on the command line of afl-fuzz:
+  -c /path/to/your/target
+
+## 7) Bonus feature #4: Wine mode
 
 AFL++ QEMU can use Wine to fuzz WIn32 PE binaries. Use the -W flag of afl-fuzz.
 
@@ -125,7 +119,7 @@ Note that some binaries require user interaction with the GUI and must be patche
 
 For examples look [here](https://github.com/andreafioraldi/WineAFLplusplusDEMO).
 
-## 7) Notes on linking
+## 8) Notes on linking
 
 The feature is supported only on Linux. Supporting BSD may amount to porting
 the changes made to linux-user/elfload.c and applying them to
@@ -146,7 +140,7 @@ practice, this means two things:
 Setting AFL_INST_LIBS=1 can be used to circumvent the .text detection logic
 and instrument every basic block encountered.
 
-## 8) Benchmarking
+## 9) Benchmarking
 
 If you want to compare the performance of the QEMU instrumentation with that of
 afl-gcc compiled code against the same target, you need to build the
@@ -161,7 +155,7 @@ Comparative measurements of execution speed or instrumentation coverage will be
 fairly meaningless if the optimization levels or instrumentation scopes don't
 match.
 
-## 9) Gotchas, feedback, bugs
+## 10) Gotchas, feedback, bugs
 
 If you need to fix up checksums or do other cleanup on mutated test cases, see
 examples/post_library/ for a viable solution.
@@ -182,7 +176,7 @@ with -march=core2, can help.
 Beyond that, this is an early-stage mechanism, so fields reports are welcome.
 You can send them to <afl-users@googlegroups.com>.
 
-## 10) Alternatives: static rewriting
+## 11) Alternatives: static rewriting
 
 Statically rewriting binaries just once, instead of attempting to translate
 them at run time, can be a faster alternative. That said, static rewriting is
@@ -196,4 +190,5 @@ The best implementation is this one:
 The issue however is Dyninst which is not rewriting the binaries so that
 they run stable. A lot of crashes happen, especially in C++ programs that
 use throw/catch. Try it first, and if it works for you be happy as it is
-2-3x as fast as qemu_mode.
+2-3x as fast as qemu_mode, however usually not as fast as QEMU persistent mode.
+
diff --git a/qemu_mode/README.persistent.md b/qemu_mode/README.persistent.md
new file mode 100644
index 00000000..c96a451b
--- /dev/null
+++ b/qemu_mode/README.persistent.md
@@ -0,0 +1,131 @@
+# How to use the persistent mode in AFL++'s QEMU mode
+
+## 1) Introduction
+
+Persistent mode let you fuzz your target persistently between two
+addresses - without forking for every fuzzing attempt.
+This increases the speed by a factor between x2 and x5, hence it is
+very, very valuable.
+
+The persistent mode is currently only available for x86/x86_64, arm
+and aarch64 targets.
+
+## 2) How use the persistent mode
+
+### 2.1) The START address
+
+The start of the persistent loop has to be set with AFL_QEMU_PERSISTENT_ADDR.
+
+This address can be the address of whatever instruction.
+Setting this address to the start of a function makes the usage simple.
+If the address is however within a function, either RET or OFFSET (see below
+in 2.2 and 2.3) have to be set.
+This address (as well as the RET address, see below) has to be defined in
+hexadecimal with the 0x prefix or as a decimal value.
+
+If the target is compiled with position independant code (PIE/PIC), you must
+add 0x4000000000 to that address, because qemu loads to this base address.
+On strange setups the base address set by QEMU for PIE executable may change,
+you can check it printing the process map using AFL_QEMU_DEBUG_MAPS=1.
+
+If this address is not valid, afl-fuzz will error during startup with the
+message that the forkserver was not found.
+
+### 2.2) the RET address
+
+The RET address is the last instruction of the persistent loop.
+The emulator will emit a jump to START when translating the instruction at RET.
+It is optional, and only needed if the the return should not be
+at the end of the function to which the START address points into, but earlier.
+
+If it is not set, QEMU will assume that START points to a function and will
+patch the return address (on stack or in the link register) to return to START
+(like WinAFL).
+
+It is defined by setting AFL_QEMU_PERSISTENT_RET, and too 0x4000000000 has to
+be set if the target is position independant.
+
+### 2.3) the OFFSET
+
+This option is valid only for x86/x86_64 only, arm/aarch64 do not save the
+return address on stack.
+
+If the START address is *not* the beginning of a function, and *no* RET has
+been set (so the end of the loop will be at the end of the function but START
+will not be at the beginning of it), we need an offset from the ESP pointer
+to locate the return address to patch.
+
+The value by which the ESP pointer has to be corrected has to set in the
+variable AFL_QEMU_PERSISTENT_RETADDR_OFFSET
+
+Now to get this value right here some help:
+1. use gdb on the target 
+2. set a breakpoint to "main" (this is required for PIE/PIC binaries so the
+   addresses are set up)
+3. "run" the target with a valid commandline
+4. set a breakpoint to the function in which START is contained
+5. set a breakpoint to your START address
+6. "continue" to the function start breakpoint
+6. print the ESP value with `print $esp` and take note of it
+7. "continue" the target until the second breakpoint
+8. again print the ESP value
+9. calculate the difference between the two values - and this is the offset
+
+### 2.4) resetting the register state
+
+It is very, very likely you need to restore the general purpose registers state
+when starting a new loop. Because of this you 99% of the time should set
+
+AFL_QEMU_PERSISTENT_GPR=1
+
+An example, is when you want to use main() as persistent START:
+
+```c
+int main(int argc, char **argv) {
+
+  if (argc < 2) return 1;
+  
+  // do stuffs
+
+}
+```
+
+If you don't save and restore the registers in x86_64, the paramteter argc
+will be lost at the second execution of the loop.
+
+## 3) Optional parameters
+
+### 3.1) Loop counter value
+
+The more stable your loop in the target, the longer you can run it, the more
+unstable it is the lower the loop count should be. A low value would be 100,
+the maximum value should be 10000. The default is 1000.
+This value can be set with AFL_QEMU_PERSISTENT_CNT
+
+This is the same concept as in the llvm_mode persistent mode with __AFL_LOOP().
+
+### 3.2) A hook for in-memory fuzzing
+
+You can increase the speed of the persistent mode even more by bypassing all
+the reading of the fuzzing input via a file by reading directly into the
+memory address space of the target process.
+
+All this needs is that the START address has a register that can reach the
+memory buffer or that the memory buffer is at a know location. You probably need
+the value of the size of the buffer (maybe it is in a register when START is
+hitted).
+
+The persistent hook will execute a function on every persistent iteration
+(at the start START) defined in a shared object specified with
+AFL_QEMU_PERSISTENT_HOOK=/path/to/hook.so.
+
+The signature is:
+
+```c
+void afl_persistent_hook(uint64_t* regs, uint64_t guest_base);
+```
+
+In this hook, you can inspect and change the saved GPR state at START.
+
+An example that you can use with little modification for your target can
+be found here: [examples/qemu_persistent_hook](../examples/qemu_persistent_hook)
diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh
index 6f2bc448..79993ce2 100755
--- a/qemu_mode/build_qemu_support.sh
+++ b/qemu_mode/build_qemu_support.sh
@@ -153,19 +153,22 @@ patch -p1 <../patches/translate-all.diff || exit 1
 patch -p1 <../patches/tcg.diff || exit 1
 patch -p1 <../patches/i386-translate.diff || exit 1
 patch -p1 <../patches/arm-translate.diff || exit 1
+patch -p1 <../patches/arm-translate-a64.diff || exit 1
 patch -p1 <../patches/i386-ops_sse.diff || exit 1
 patch -p1 <../patches/i386-fpu_helper.diff || exit 1
 patch -p1 <../patches/softfloat.diff || exit 1
+patch -p1 <../patches/configure.diff || exit 1
 
 echo "[+] Patching done."
 
 if [ "$STATIC" = "1" ]; then
 
-  CFLAGS="-O3 -ggdb" ./configure --disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \
+  ./configure --extra-cflags="-O3 -ggdb -DAFL_QEMU_STATIC_BUILD=1" \
+     --disable-bsd-user --disable-guest-agent --disable-strip --disable-werror \
 	  --disable-gcrypt --disable-debug-info --disable-debug-tcg --disable-tcg-interpreter \
 	  --enable-attr --disable-brlapi --disable-linux-aio --disable-bzip2 --disable-bluez --disable-cap-ng \
 	  --disable-curl --disable-fdt --disable-glusterfs --disable-gnutls --disable-nettle --disable-gtk \
-	  --disable-rdma --disable-libiscsi --disable-vnc-jpeg --enable-kvm --disable-lzo --disable-curses \
+	  --disable-rdma --disable-libiscsi --disable-vnc-jpeg --disable-lzo --disable-curses \
 	  --disable-libnfs --disable-numa --disable-opengl --disable-vnc-png --disable-rbd --disable-vnc-sasl \
 	  --disable-sdl --disable-seccomp --disable-smartcard --disable-snappy --disable-spice --disable-libssh2 \
 	  --disable-libusb --disable-usb-redir --disable-vde --disable-vhost-net --disable-virglrenderer \
@@ -178,9 +181,9 @@ else
   # --enable-pie seems to give a couple of exec's a second performance
   # improvement, much to my surprise. Not sure how universal this is..
   
-  CFLAGS="-O3 -ggdb" ./configure --disable-system \
+  ./configure --disable-system \
     --enable-linux-user --disable-gtk --disable-sdl --disable-vnc \
-    --target-list="${CPU_TARGET}-linux-user" --enable-pie --enable-kvm $CROSS_PREFIX || exit 1
+    --target-list="${CPU_TARGET}-linux-user" --enable-pie $CROSS_PREFIX || exit 1
 
 fi
 
diff --git a/qemu_mode/patches/afl-qemu-common.h b/qemu_mode/patches/afl-qemu-common.h
index 4d651385..4303a5e6 100644
--- a/qemu_mode/patches/afl-qemu-common.h
+++ b/qemu_mode/patches/afl-qemu-common.h
@@ -35,11 +35,18 @@
 #define __AFL_QEMU_COMMON
 
 #include "../../config.h"
+#include "../../include/cmplog.h"
 
-#ifndef CPU_NB_REGS
-#define AFL_REGS_NUM 1000
-#else
+#define PERSISTENT_DEFAULT_MAX_CNT 1000
+
+#ifdef CPU_NB_REGS
 #define AFL_REGS_NUM CPU_NB_REGS
+#elif TARGET_ARM
+#define AFL_REGS_NUM 32
+#elif TARGET_AARCH64
+#define AFL_REGS_NUM 32
+#else
+#define AFL_REGS_NUM 100
 #endif
 
 /* NeverZero */
@@ -56,6 +63,8 @@
 #define INC_AFL_AREA(loc) afl_area_ptr[loc]++
 #endif
 
+typedef void (*afl_persistent_hook_fn)(uint64_t *regs, uint64_t guest_base);
+
 /* Declared in afl-qemu-cpu-inl.h */
 
 extern unsigned char *afl_area_ptr;
@@ -69,11 +78,16 @@ extern unsigned char  is_persistent;
 extern target_long    persistent_stack_offset;
 extern unsigned char  persistent_first_pass;
 extern unsigned char  persistent_save_gpr;
-extern target_ulong   persistent_saved_gpr[AFL_REGS_NUM];
+extern uint64_t       persistent_saved_gpr[AFL_REGS_NUM];
 extern int            persisent_retaddr_offset;
 
+extern afl_persistent_hook_fn afl_persistent_hook_ptr;
+
 extern __thread abi_ulong afl_prev_loc;
 
+extern struct cmp_map *__afl_cmp_map;
+extern __thread u32    __afl_cmp_counter;
+
 void afl_debug_dump_saved_regs();
 
 void afl_persistent_loop();
diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h
index ac847371..5e155c74 100644
--- a/qemu_mode/patches/afl-qemu-cpu-inl.h
+++ b/qemu_mode/patches/afl-qemu-cpu-inl.h
@@ -32,10 +32,11 @@
  */
 
 #include <sys/shm.h>
-#include "../../config.h"
 #include "afl-qemu-common.h"
 
-#define PERSISTENT_DEFAULT_MAX_CNT 1000
+#ifndef AFL_QEMU_STATIC_BUILD
+#include <dlfcn.h>
+#endif
 
 /***************************
  * VARIOUS AUXILIARY STUFF *
@@ -81,6 +82,9 @@ u8 afl_compcov_level;
 
 __thread abi_ulong afl_prev_loc;
 
+struct cmp_map *__afl_cmp_map;
+__thread u32    __afl_cmp_counter;
+
 /* Set in the child process in forkserver mode: */
 
 static int forkserver_installed = 0;
@@ -95,6 +99,8 @@ unsigned char persistent_save_gpr;
 target_ulong  persistent_saved_gpr[AFL_REGS_NUM];
 int           persisent_retaddr_offset;
 
+afl_persistent_hook_fn afl_persistent_hook_ptr;
+
 /* Instrumentation ratio: */
 
 unsigned int afl_inst_rms = MAP_SIZE;         /* Exported for afl_gen_trace */
@@ -182,6 +188,22 @@ static void afl_setup(void) {
 
   }
 
+  if (getenv("___AFL_EINS_ZWEI_POLIZEI___")) {  // CmpLog forkserver
+
+    id_str = getenv(CMPLOG_SHM_ENV_VAR);
+
+    if (id_str) {
+
+      u32 shm_id = atoi(id_str);
+
+      __afl_cmp_map = shmat(shm_id, NULL, 0);
+
+      if (__afl_cmp_map == (void *)-1) exit(1);
+
+    }
+
+  }
+
   if (getenv("AFL_INST_LIBS")) {
 
     afl_start_code = 0;
@@ -224,6 +246,43 @@ static void afl_setup(void) {
 
   if (getenv("AFL_QEMU_PERSISTENT_GPR")) persistent_save_gpr = 1;
 
+  if (getenv("AFL_QEMU_PERSISTENT_HOOK")) {
+
+#ifdef AFL_QEMU_STATIC_BUILD
+
+    fprintf(stderr,
+            "[AFL] ERROR: you cannot use AFL_QEMU_PERSISTENT_HOOK when "
+            "afl-qemu-trace is static\n");
+    exit(1);
+
+#else
+
+    persistent_save_gpr = 1;
+
+    void *plib = dlopen(getenv("AFL_QEMU_PERSISTENT_HOOK"), RTLD_NOW);
+    if (!plib) {
+
+      fprintf(stderr, "[AFL] ERROR: invalid AFL_QEMU_PERSISTENT_HOOK=%s\n",
+              getenv("AFL_QEMU_PERSISTENT_HOOK"));
+      exit(1);
+
+    }
+
+    afl_persistent_hook_ptr = dlsym(plib, "afl_persistent_hook");
+    if (!afl_persistent_hook_ptr) {
+
+      fprintf(stderr,
+              "[AFL] ERROR: failed to find the function "
+              "\"afl_persistent_hook\" in %s\n",
+              getenv("AFL_QEMU_PERSISTENT_HOOK"));
+      exit(1);
+
+    }
+
+#endif
+
+  }
+
   if (getenv("AFL_QEMU_PERSISTENT_RETADDR_OFFSET"))
     persisent_retaddr_offset =
         strtoll(getenv("AFL_QEMU_PERSISTENT_RETADDR_OFFSET"), NULL, 0);
@@ -352,8 +411,13 @@ static void afl_forkserver(CPUState *cpu) {
 
     if (WIFSTOPPED(status))
       child_stopped = 1;
-    else if (unlikely(first_run && is_persistent))
+    else if (unlikely(first_run && is_persistent)) {
+
+      fprintf(stderr, "[AFL] ERROR: no persistent iteration executed\n");
       exit(12);  // Persistent is wrong
+
+    }
+
     first_run = 0;
 
     if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7);
diff --git a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
index 6d42bf3d..6f526d92 100644
--- a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
+++ b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
@@ -102,31 +102,101 @@ static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
 
 }
 
+static void afl_cmplog_16(target_ulong cur_loc, target_ulong arg1,
+                          target_ulong arg2) {
+
+  register uintptr_t k = (uintptr_t)cur_loc;
+
+  u32 hits = __afl_cmp_map->headers[k].hits;
+  __afl_cmp_map->headers[k].hits = hits + 1;
+  // if (!__afl_cmp_map->headers[k].cnt)
+  //  __afl_cmp_map->headers[k].cnt = __afl_cmp_counter++;
+
+  __afl_cmp_map->headers[k].shape = 1;
+  //__afl_cmp_map->headers[k].type = CMP_TYPE_INS;
+
+  hits &= CMP_MAP_H - 1;
+  __afl_cmp_map->log[k][hits].v0 = arg1;
+  __afl_cmp_map->log[k][hits].v1 = arg2;
+
+}
+
+static void afl_cmplog_32(target_ulong cur_loc, target_ulong arg1,
+                          target_ulong arg2) {
+
+  register uintptr_t k = (uintptr_t)cur_loc;
+
+  u32 hits = __afl_cmp_map->headers[k].hits;
+  __afl_cmp_map->headers[k].hits = hits + 1;
+
+  __afl_cmp_map->headers[k].shape = 3;
+
+  hits &= CMP_MAP_H - 1;
+  __afl_cmp_map->log[k][hits].v0 = arg1;
+  __afl_cmp_map->log[k][hits].v1 = arg2;
+
+}
+
+static void afl_cmplog_64(target_ulong cur_loc, target_ulong arg1,
+                          target_ulong arg2) {
+
+  register uintptr_t k = (uintptr_t)cur_loc;
+
+  u32 hits = __afl_cmp_map->headers[k].hits;
+  __afl_cmp_map->headers[k].hits = hits + 1;
+
+  __afl_cmp_map->headers[k].shape = 7;
+
+  hits &= CMP_MAP_H - 1;
+  __afl_cmp_map->log[k][hits].v0 = arg1;
+  __afl_cmp_map->log[k][hits].v1 = arg2;
+
+}
+
 static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
                             TCGMemOp ot, int is_imm) {
 
   void *func;
 
-  if (!afl_compcov_level || cur_loc > afl_end_code || cur_loc < afl_start_code)
-    return;
+  if (cur_loc > afl_end_code || cur_loc < afl_start_code) return;
 
-  if (!is_imm && afl_compcov_level < 2) return;
+  if (__afl_cmp_map) {
 
-  switch (ot) {
+    cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
+    cur_loc &= CMP_MAP_W - 1;
 
-    case MO_64: func = &afl_compcov_log_64; break;
-    case MO_32: func = &afl_compcov_log_32; break;
-    case MO_16: func = &afl_compcov_log_16; break;
-    default: return;
+    switch (ot) {
 
-  }
+      case MO_64: func = &afl_cmplog_64; break;
+      case MO_32: func = &afl_cmplog_32; break;
+      case MO_16: func = &afl_cmplog_16; break;
+      default: return;
 
-  cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
-  cur_loc &= MAP_SIZE - 7;
+    }
+
+    tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
+
+  } else if (afl_compcov_level) {
+
+    if (!is_imm && afl_compcov_level < 2) return;
+
+    cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
+    cur_loc &= MAP_SIZE - 7;
+
+    if (cur_loc >= afl_inst_rms) return;
 
-  if (cur_loc >= afl_inst_rms) return;
+    switch (ot) {
 
-  tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
+      case MO_64: func = &afl_compcov_log_64; break;
+      case MO_32: func = &afl_compcov_log_32; break;
+      case MO_16: func = &afl_compcov_log_16; break;
+      default: return;
+
+    }
+
+    tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
+
+  }
 
 }
 
@@ -182,62 +252,77 @@ static void log_x86_sp_content(void) {
 
 }*/
 
-#define I386_RESTORE_STATE_FOR_PERSISTENT                               \
-  do {                                                                  \
-                                                                        \
-    if (persistent_save_gpr) {                                          \
-                                                                        \
-      int      i;                                                       \
-      TCGv_ptr gpr_sv;                                                  \
-                                                                        \
-      TCGv_ptr first_pass_ptr = tcg_const_ptr(&persistent_first_pass);  \
-      TCGv     first_pass = tcg_temp_local_new();                       \
-      TCGv     one = tcg_const_tl(1);                                   \
-      tcg_gen_ld8u_tl(first_pass, first_pass_ptr, 0);                   \
-                                                                        \
-      TCGLabel *lbl_save_gpr = gen_new_label();                         \
-      TCGLabel *lbl_finish_restore_gpr = gen_new_label();               \
-      tcg_gen_brcond_tl(TCG_COND_EQ, first_pass, one, lbl_save_gpr);    \
-                                                                        \
-      for (i = 0; i < CPU_NB_REGS; ++i) {                               \
-                                                                        \
-        gpr_sv = tcg_const_ptr(&persistent_saved_gpr[i]);               \
-        tcg_gen_ld_tl(cpu_regs[i], gpr_sv, 0);                          \
-                                                                        \
-      }                                                                 \
-                                                                        \
-      tcg_gen_br(lbl_finish_restore_gpr);                               \
-                                                                        \
-      gen_set_label(lbl_save_gpr);                                      \
-                                                                        \
-      for (i = 0; i < CPU_NB_REGS; ++i) {                               \
-                                                                        \
-        gpr_sv = tcg_const_ptr(&persistent_saved_gpr[i]);               \
-        tcg_gen_st_tl(cpu_regs[i], gpr_sv, 0);                          \
-                                                                        \
-      }                                                                 \
-                                                                        \
-      gen_set_label(lbl_finish_restore_gpr);                            \
-      tcg_temp_free(first_pass);                                        \
-                                                                        \
-    } else if (afl_persistent_ret_addr == 0) {                          \
-                                                                        \
-      TCGv_ptr stack_off_ptr = tcg_const_ptr(&persistent_stack_offset); \
-      TCGv     stack_off = tcg_temp_new();                              \
-      tcg_gen_ld_tl(stack_off, stack_off_ptr, 0);                       \
-      tcg_gen_sub_tl(cpu_regs[R_ESP], cpu_regs[R_ESP], stack_off);      \
-      tcg_temp_free(stack_off);                                         \
-                                                                        \
-    }                                                                   \
-                                                                        \
-  } while (0)
-
-#define AFL_QEMU_TARGET_i386_SNIPPET                                          \
+static void callback_to_persistent_hook(void) {
+
+  afl_persistent_hook_ptr(persistent_saved_gpr, guest_base);
+
+}
+
+static void gpr_saving(TCGv *cpu_regs, int regs_num) {
+
+  int      i;
+  TCGv_ptr gpr_sv;
+
+  TCGv_ptr first_pass_ptr = tcg_const_ptr(&persistent_first_pass);
+  TCGv     first_pass = tcg_temp_local_new();
+  TCGv     one = tcg_const_tl(1);
+  tcg_gen_ld8u_tl(first_pass, first_pass_ptr, 0);
+
+  TCGLabel *lbl_restore_gpr = gen_new_label();
+  tcg_gen_brcond_tl(TCG_COND_NE, first_pass, one, lbl_restore_gpr);
+
+  // save GPR registers
+  for (i = 0; i < regs_num; ++i) {
+
+    gpr_sv = tcg_const_ptr(&persistent_saved_gpr[i]);
+    tcg_gen_st_tl(cpu_regs[i], gpr_sv, 0);
+
+  }
+
+  gen_set_label(lbl_restore_gpr);
+
+  tcg_gen_afl_call0(&afl_persistent_loop);
+
+  if (afl_persistent_hook_ptr) tcg_gen_afl_call0(callback_to_persistent_hook);
+
+  // restore GPR registers
+  for (i = 0; i < regs_num; ++i) {
+
+    gpr_sv = tcg_const_ptr(&persistent_saved_gpr[i]);
+    tcg_gen_ld_tl(cpu_regs[i], gpr_sv, 0);
+
+  }
+
+  tcg_temp_free_ptr(first_pass_ptr);
+  tcg_temp_free(first_pass);
+  tcg_temp_free(one);
+
+}
+
+static void restore_state_for_persistent(TCGv *cpu_regs, int regs_num, int sp) {
+
+  if (persistent_save_gpr) {
+
+    gpr_saving(cpu_regs, regs_num);
+
+  } else if (afl_persistent_ret_addr == 0) {
+
+    TCGv_ptr stack_off_ptr = tcg_const_ptr(&persistent_stack_offset);
+    TCGv     stack_off = tcg_temp_new();
+    tcg_gen_ld_tl(stack_off, stack_off_ptr, 0);
+    tcg_gen_sub_tl(cpu_regs[sp], cpu_regs[sp], stack_off);
+    tcg_temp_free(stack_off);
+
+  }
+
+}
+
+#define AFL_QEMU_TARGET_I386_SNIPPET                                          \
   if (is_persistent) {                                                        \
                                                                               \
     if (s->pc == afl_persistent_addr) {                                       \
                                                                               \
-      I386_RESTORE_STATE_FOR_PERSISTENT;                                      \
+      restore_state_for_persistent(cpu_regs, AFL_REGS_NUM, R_ESP);            \
       /*tcg_gen_afl_call0(log_x86_saved_gpr);                                 \
       tcg_gen_afl_call0(log_x86_sp_content);*/                                \
                                                                               \
@@ -245,9 +330,11 @@ static void log_x86_sp_content(void) {
                                                                               \
         TCGv_ptr paddr = tcg_const_ptr(afl_persistent_addr);                  \
         tcg_gen_st_tl(paddr, cpu_regs[R_ESP], persisent_retaddr_offset);      \
+        tcg_temp_free_ptr(paddr);                                             \
                                                                               \
       }                                                                       \
-      tcg_gen_afl_call0(&afl_persistent_loop);                                \
+                                                                              \
+      if (!persistent_save_gpr) tcg_gen_afl_call0(&afl_persistent_loop);      \
       /*tcg_gen_afl_call0(log_x86_sp_content);*/                              \
                                                                               \
     } else if (afl_persistent_ret_addr && s->pc == afl_persistent_ret_addr) { \
@@ -259,3 +346,57 @@ static void log_x86_sp_content(void) {
                                                                               \
   }
 
+// SP = 13, LINK = 14
+
+#define AFL_QEMU_TARGET_ARM_SNIPPET                                            \
+  if (is_persistent) {                                                         \
+                                                                               \
+    if (dc->pc == afl_persistent_addr) {                                       \
+                                                                               \
+      if (persistent_save_gpr) gpr_saving(cpu_R, AFL_REGS_NUM);                \
+                                                                               \
+      if (afl_persistent_ret_addr == 0) {                                      \
+                                                                               \
+        TCGv_ptr paddr = tcg_const_ptr(afl_persistent_addr);                   \
+        tcg_gen_mov_i32(cpu_R[14], paddr);                                     \
+        tcg_temp_free_ptr(paddr);                                              \
+                                                                               \
+      }                                                                        \
+                                                                               \
+      if (!persistent_save_gpr) tcg_gen_afl_call0(&afl_persistent_loop);       \
+                                                                               \
+    } else if (afl_persistent_ret_addr && dc->pc == afl_persistent_ret_addr) { \
+                                                                               \
+      gen_bx_im(dc, afl_persistent_addr);                                      \
+                                                                               \
+    }                                                                          \
+                                                                               \
+  }
+
+// SP = 31, LINK = 30
+
+#define AFL_QEMU_TARGET_ARM64_SNIPPET                                         \
+  if (is_persistent) {                                                        \
+                                                                              \
+    if (s->pc == afl_persistent_addr) {                                       \
+                                                                              \
+      if (persistent_save_gpr) gpr_saving(cpu_X, AFL_REGS_NUM);               \
+                                                                              \
+      if (afl_persistent_ret_addr == 0) {                                     \
+                                                                              \
+        TCGv_ptr paddr = tcg_const_ptr(afl_persistent_addr);                  \
+        tcg_gen_mov_i32(cpu_X[30], paddr);                                    \
+        tcg_temp_free_ptr(paddr);                                             \
+                                                                              \
+      }                                                                       \
+                                                                              \
+      if (!persistent_save_gpr) tcg_gen_afl_call0(&afl_persistent_loop);      \
+                                                                              \
+    } else if (afl_persistent_ret_addr && s->pc == afl_persistent_ret_addr) { \
+                                                                              \
+      gen_goto_tb(s, 0, afl_persistent_addr);                                 \
+                                                                              \
+    }                                                                         \
+                                                                              \
+  }
+
diff --git a/qemu_mode/patches/arm-translate-a64.diff b/qemu_mode/patches/arm-translate-a64.diff
new file mode 100644
index 00000000..83856217
--- /dev/null
+++ b/qemu_mode/patches/arm-translate-a64.diff
@@ -0,0 +1,64 @@
+diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
+index fd36425..992bf17 100644
+--- a/target/arm/translate-a64.c
++++ b/target/arm/translate-a64.c
+@@ -39,6 +39,8 @@
+ #include "translate-a64.h"
+ #include "qemu/atomic128.h"
+ 
++#include "../patches/afl-qemu-cpu-translate-inl.h"
++
+ static TCGv_i64 cpu_X[32];
+ static TCGv_i64 cpu_pc;
+ 
+@@ -3365,6 +3367,12 @@ static void disas_add_sub_imm(DisasContext *s, uint32_t insn)
+         return;
+     }
+ 
++    if (rd == 31 && sub_op) { // cmp xX, imm
++      TCGv_i64 tcg_imm = tcg_const_i64(imm);
++      afl_gen_compcov(s->pc, tcg_rn, tcg_imm, is_64bit ? MO_64 : MO_32, 1);
++      tcg_temp_free_i64(tcg_imm);
++    }
++
+     tcg_result = tcg_temp_new_i64();
+     if (!setflags) {
+         if (sub_op) {
+@@ -3972,6 +3980,9 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
+ 
+     tcg_rm = read_cpu_reg(s, rm, sf);
+     ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3);
++    
++    if (rd == 31 && sub_op) // cmp xX, xY
++      afl_gen_compcov(s->pc, tcg_rn, tcg_rm, sf ? MO_64 : MO_32, 0);
+ 
+     tcg_result = tcg_temp_new_i64();
+ 
+@@ -4037,6 +4048,9 @@ static void disas_add_sub_reg(DisasContext *s, uint32_t insn)
+ 
+     shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, imm6);
+ 
++    if (rd == 31 && sub_op) // cmp xX, xY
++      afl_gen_compcov(s->pc, tcg_rn, tcg_rm, sf ? MO_64 : MO_32, 0);
++
+     tcg_result = tcg_temp_new_i64();
+ 
+     if (!setflags) {
+@@ -4246,6 +4260,8 @@ static void disas_cc(DisasContext *s, uint32_t insn)
+         tcg_y = cpu_reg(s, y);
+     }
+     tcg_rn = cpu_reg(s, rn);
++    
++    afl_gen_compcov(s->pc, tcg_rn, tcg_y, sf ? MO_64 : MO_32, is_imm);
+ 
+     /* Set the flags for the new comparison.  */
+     tcg_tmp = tcg_temp_new_i64();
+@@ -13317,6 +13333,8 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn)
+ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
+ {
+     uint32_t insn;
++    
++    AFL_QEMU_TARGET_ARM64_SNIPPET
+ 
+     insn = arm_ldl_code(env, s->pc, s->sctlr_b);
+     s->insn = insn;
diff --git a/qemu_mode/patches/arm-translate.diff b/qemu_mode/patches/arm-translate.diff
index 58b4a873..daa5d43b 100644
--- a/qemu_mode/patches/arm-translate.diff
+++ b/qemu_mode/patches/arm-translate.diff
@@ -1,5 +1,5 @@
 diff --git a/target/arm/translate.c b/target/arm/translate.c
-index 7c4675ff..0f0928b6 100644
+index 7c4675f..e3d999a 100644
 --- a/target/arm/translate.c
 +++ b/target/arm/translate.c
 @@ -59,6 +59,8 @@
@@ -132,3 +132,21 @@ index 7c4675ff..0f0928b6 100644
              rd = 16;
              break;
          case 0xb: /* cmn */
+@@ -13233,6 +13247,8 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+         return;
+     }
+ 
++    AFL_QEMU_TARGET_ARM_SNIPPET
++
+     insn = arm_ldl_code(env, dc->pc, dc->sctlr_b);
+     dc->insn = insn;
+     dc->pc += 4;
+@@ -13301,6 +13317,8 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+         return;
+     }
+ 
++    AFL_QEMU_TARGET_ARM_SNIPPET
++
+     insn = arm_lduw_code(env, dc->pc, dc->sctlr_b);
+     is_16bit = thumb_insn_is_16bit(dc, insn);
+     dc->pc += 2;
diff --git a/qemu_mode/patches/configure.diff b/qemu_mode/patches/configure.diff
new file mode 100644
index 00000000..acb96294
--- /dev/null
+++ b/qemu_mode/patches/configure.diff
@@ -0,0 +1,26 @@
+diff --git a/configure b/configure
+index 1c9f609..3edc9a7 100755
+--- a/configure
++++ b/configure
+@@ -4603,6 +4603,21 @@ if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \
+     libs_softmmu="-lutil $libs_softmmu"
+ fi
+ 
++##########################################
++cat > $TMPC << EOF
++#include <dlfcn.h>
++#include <stdlib.h>
++int main(int argc, char **argv) { return dlopen("libc.so", RTLD_NOW) != NULL; }
++EOF
++if compile_prog "" "" ; then
++  :
++elif compile_prog "" "-ldl" ; then
++  LIBS="-ldl $LIBS"
++  libs_qga="-ldl $libs_qga"
++else
++  error_exit "libdl check failed"
++fi
++
+ ##########################################
+ # spice probe
+ if test "$spice" != "no" ; then
diff --git a/qemu_mode/patches/i386-translate.diff b/qemu_mode/patches/i386-translate.diff
index 00337e2c..8ccd6f4e 100644
--- a/qemu_mode/patches/i386-translate.diff
+++ b/qemu_mode/patches/i386-translate.diff
@@ -35,7 +35,7 @@ index 0dd5fbe4..a23da128 100644
      rex_w = -1;
      rex_r = 0;
  
-+    AFL_QEMU_TARGET_i386_SNIPPET
++    AFL_QEMU_TARGET_I386_SNIPPET
 +
   next_byte:
      b = x86_ldub_code(env, s);