aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorvanhauser-thc <vh@thc.org>2020-12-09 11:30:04 +0100
committervanhauser-thc <vh@thc.org>2020-12-09 11:30:04 +0100
commita8e568f248628c39e0bc34173470988034723627 (patch)
treeb4677724560bfcd2bda784c9f4e714ff26fbf490 /utils
parent39a4fac941177387578ec856aacea2187588fc13 (diff)
downloadAFLplusplus-a8e568f248628c39e0bc34173470988034723627.tar.gz
move libdislocator, libtokencap and qbdi_mode to utils/
Diffstat (limited to 'utils')
-rw-r--r--utils/libdislocator/Makefile43
-rw-r--r--utils/libdislocator/README.md68
-rw-r--r--utils/libdislocator/libdislocator.so.c544
-rw-r--r--utils/libtokencap/Makefile94
-rw-r--r--utils/libtokencap/README.md64
-rw-r--r--utils/libtokencap/libtokencap.so.c794
-rwxr-xr-xutils/qbdi_mode/README.md199
-rw-r--r--utils/qbdi_mode/assets/screen1.pngbin0 -> 88333 bytes
-rwxr-xr-xutils/qbdi_mode/build.sh57
-rwxr-xr-xutils/qbdi_mode/demo-so.c41
-rwxr-xr-xutils/qbdi_mode/template.cpp251
11 files changed, 2155 insertions, 0 deletions
diff --git a/utils/libdislocator/Makefile b/utils/libdislocator/Makefile
new file mode 100644
index 00000000..2942c3c3
--- /dev/null
+++ b/utils/libdislocator/Makefile
@@ -0,0 +1,43 @@
+#
+# american fuzzy lop++ - libdislocator
+# ----------------------------------
+#
+# Originally written by Michal Zalewski
+#
+# Copyright 2016 Google Inc. 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
+#
+
+PREFIX ?= /usr/local
+HELPER_PATH = $(PREFIX)/lib/afl
+
+VERSION = $(shell grep '^\#define VERSION ' ../../config.h | cut -d '"' -f2)
+
+CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2
+CFLAGS += -I ../../include/ -Wall -g -Wno-pointer-sign
+
+CFLAGS_ADD=$(USEHUGEPAGE:1=-DUSEHUGEPAGE)
+CFLAGS += $(CFLAGS_ADD)
+
+all: libdislocator.so
+
+libdislocator.so: libdislocator.so.c ../../config.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC libdislocator.so.c -o $@ $(LDFLAGS)
+ cp -fv libdislocator.so ../../
+
+.NOTPARALLEL: clean
+
+clean:
+ rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
+ rm -f ../../libdislocator.so
+
+install: all
+ install -m 755 -d $${DESTDIR}$(HELPER_PATH)
+ install -m 755 ../../libdislocator.so $${DESTDIR}$(HELPER_PATH)
+ install -m 644 -T README.md $${DESTDIR}$(HELPER_PATH)/README.dislocator.md
+
diff --git a/utils/libdislocator/README.md b/utils/libdislocator/README.md
new file mode 100644
index 00000000..1785463e
--- /dev/null
+++ b/utils/libdislocator/README.md
@@ -0,0 +1,68 @@
+# libdislocator, an abusive allocator
+
+ (See ../README.md for the general instruction manual.)
+
+This is a companion library that can be used as a drop-in replacement for the
+libc allocator in the fuzzed binaries. It improves the odds of bumping into
+heap-related security bugs in several ways:
+
+ - It allocates all buffers so that they are immediately adjacent to a
+ subsequent PROT_NONE page, causing most off-by-one reads and writes to
+ immediately segfault,
+
+ - It adds a canary immediately below the allocated buffer, to catch writes
+ to negative offsets (won't catch reads, though),
+
+ - It sets the memory returned by malloc() to garbage values, improving the
+ odds of crashing when the target accesses uninitialized data,
+
+ - It sets freed memory to PROT_NONE and does not actually reuse it, causing
+ most use-after-free bugs to segfault right away,
+
+ - It forces all realloc() calls to return a new address - and sets
+ PROT_NONE on the original block. This catches use-after-realloc bugs,
+
+ - It checks for calloc() overflows and can cause soft or hard failures
+ of alloc requests past a configurable memory limit (AFL_LD_LIMIT_MB,
+ AFL_LD_HARD_FAIL).
+
+ - Optionally, in platforms supporting it, huge pages can be used by passing
+ USEHUGEPAGE=1 to make.
+
+ - Size alignment to `max_align_t` can be enforced with AFL_ALIGNED_ALLOC=1.
+ In this case, a tail canary is inserted in the padding bytes at the end
+ of the allocated zone. This reduce the ability of libdislocator to detect
+ off-by-one bugs but also it make slibdislocator compliant to the C standard.
+
+Basically, it is inspired by some of the non-default options available for the
+OpenBSD allocator - see malloc.conf(5) on that platform for reference. It is
+also somewhat similar to several other debugging libraries, such as gmalloc
+and DUMA - but is simple, plug-and-play, and designed specifically for fuzzing
+jobs.
+
+Note that it does nothing for stack-based memory handling errors. The
+-fstack-protector-all setting for GCC / clang, enabled when using AFL_HARDEN,
+can catch some subset of that.
+
+The allocator is slow and memory-intensive (even the tiniest allocation uses up
+4 kB of physical memory and 8 kB of virtual mem), making it completely unsuitable
+for "production" uses; but it can be faster and more hassle-free than ASAN / MSAN
+when fuzzing small, self-contained binaries.
+
+To use this library, run AFL like so:
+
+```
+AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...]
+```
+
+You *have* to specify path, even if it's just ./libdislocator.so or
+$PWD/libdislocator.so.
+
+Similarly to afl-tmin, the library is not "proprietary" and can be used with
+other fuzzers or testing tools without the need for any code tweaks. It does not
+require AFL-instrumented binaries to work.
+
+Note that the AFL_PRELOAD approach (which AFL internally maps to LD_PRELOAD or
+DYLD_INSERT_LIBRARIES, depending on the OS) works only if the target binary is
+dynamically linked. Otherwise, attempting to use the library will have no
+effect.
diff --git a/utils/libdislocator/libdislocator.so.c b/utils/libdislocator/libdislocator.so.c
new file mode 100644
index 00000000..2324e390
--- /dev/null
+++ b/utils/libdislocator/libdislocator.so.c
@@ -0,0 +1,544 @@
+/*
+
+ american fuzzy lop++ - dislocator, an abusive allocator
+ -----------------------------------------------------
+
+ Originally written by Michal Zalewski
+
+ Copyright 2016 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
+
+ This is a companion library that can be used as a drop-in replacement
+ for the libc allocator in the fuzzed binaries. See README.dislocator.md for
+ more info.
+
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#ifdef __APPLE__
+ #include <mach/vm_statistics.h>
+#endif
+
+#ifdef __FreeBSD__
+ #include <sys/param.h>
+#endif
+
+#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__HAIKU__)
+ #include <unistd.h>
+ #ifdef __linux__
+ #include <sys/syscall.h>
+ #include <malloc.h>
+ #endif
+ #ifdef __NR_getrandom
+ #define arc4random_buf(p, l) \
+ do { \
+ \
+ ssize_t rd = syscall(__NR_getrandom, p, l, 0); \
+ if (rd != l) DEBUGF("getrandom failed"); \
+ \
+ } while (0)
+
+ #else
+ #include <time.h>
+ #define arc4random_buf(p, l) \
+ do { \
+ \
+ srand(time(NULL)); \
+ u32 i; \
+ u8 *ptr = (u8 *)p; \
+ for (i = 0; i < l; i++) \
+ ptr[i] = rand() % INT_MAX; \
+ \
+ } while (0)
+
+ #endif
+#endif
+
+#include "config.h"
+#include "types.h"
+
+#if __STDC_VERSION__ < 201112L || \
+ (defined(__FreeBSD__) && __FreeBSD_version < 1200000)
+// use this hack if not C11
+typedef struct {
+
+ long long __ll;
+ long double __ld;
+
+} max_align_t;
+
+#endif
+
+#define ALLOC_ALIGN_SIZE (_Alignof(max_align_t))
+
+#ifndef PAGE_SIZE
+ #define PAGE_SIZE 4096
+#endif /* !PAGE_SIZE */
+
+#ifndef MAP_ANONYMOUS
+ #define MAP_ANONYMOUS MAP_ANON
+#endif /* !MAP_ANONYMOUS */
+
+#define SUPER_PAGE_SIZE 1 << 21
+
+/* Error / message handling: */
+
+#define DEBUGF(_x...) \
+ do { \
+ \
+ if (alloc_verbose) { \
+ \
+ if (++call_depth == 1) { \
+ \
+ fprintf(stderr, "[AFL] " _x); \
+ fprintf(stderr, "\n"); \
+ \
+ } \
+ call_depth--; \
+ \
+ } \
+ \
+ } while (0)
+
+#define FATAL(_x...) \
+ do { \
+ \
+ if (++call_depth == 1) { \
+ \
+ fprintf(stderr, "*** [AFL] " _x); \
+ fprintf(stderr, " ***\n"); \
+ abort(); \
+ \
+ } \
+ call_depth--; \
+ \
+ } while (0)
+
+/* Macro to count the number of pages needed to store a buffer: */
+
+#define PG_COUNT(_l) (((_l) + (PAGE_SIZE - 1)) / PAGE_SIZE)
+
+/* Canary & clobber bytes: */
+
+#define ALLOC_CANARY 0xAACCAACC
+#define ALLOC_CLOBBER 0xCC
+
+#define TAIL_ALLOC_CANARY 0xAC
+
+#define PTR_C(_p) (((u32 *)(_p))[-1])
+#define PTR_L(_p) (((u32 *)(_p))[-2])
+
+/* Configurable stuff (use AFL_LD_* to set): */
+
+static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */
+static u8 alloc_verbose, /* Additional debug messages */
+ hard_fail, /* abort() when max_mem exceeded? */
+ no_calloc_over, /* abort() on calloc() overflows? */
+ align_allocations; /* Force alignment to sizeof(void*) */
+
+#if defined __OpenBSD__ || defined __APPLE__
+ #define __thread
+ #warning no thread support available
+#endif
+static __thread size_t total_mem; /* Currently allocated mem */
+
+static __thread u32 call_depth; /* To avoid recursion via fprintf() */
+static u32 alloc_canary;
+
+/* This is the main alloc function. It allocates one page more than necessary,
+ sets that tailing page to PROT_NONE, and then increments the return address
+ so that it is right-aligned to that boundary. Since it always uses mmap(),
+ the returned memory will be zeroed. */
+
+static void *__dislocator_alloc(size_t len) {
+
+ u8 * ret, *base;
+ size_t tlen;
+ int flags, fd, sp;
+
+ if (total_mem + len > max_mem || total_mem + len < total_mem) {
+
+ if (hard_fail) FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024);
+
+ DEBUGF("total allocs exceed %u MB, returning NULL", max_mem / 1024 / 1024);
+
+ return NULL;
+
+ }
+
+ size_t rlen;
+ if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1)))
+ rlen = (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE;
+ else
+ rlen = len;
+
+ /* We will also store buffer length and a canary below the actual buffer, so
+ let's add 8 bytes for that. */
+
+ base = NULL;
+ tlen = (1 + PG_COUNT(rlen + 8)) * PAGE_SIZE;
+ flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ fd = -1;
+#if defined(USEHUGEPAGE)
+ sp = (rlen >= SUPER_PAGE_SIZE && !(rlen % SUPER_PAGE_SIZE));
+
+ #if defined(__APPLE__)
+ if (sp) fd = VM_FLAGS_SUPERPAGE_SIZE_2MB;
+ #elif defined(__linux__)
+ if (sp) flags |= MAP_HUGETLB;
+ #elif defined(__FreeBSD__)
+ if (sp) flags |= MAP_ALIGNED_SUPER;
+ #elif defined(__sun)
+ if (sp) {
+
+ base = (void *)(caddr_t)(1 << 21);
+ flags |= MAP_ALIGN;
+
+ }
+
+ #endif
+#else
+ (void)sp;
+#endif
+
+ ret = (u8 *)mmap(base, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
+#if defined(USEHUGEPAGE)
+ /* We try one more time with regular call */
+ if (ret == MAP_FAILED) {
+
+ #if defined(__APPLE__)
+ fd = -1;
+ #elif defined(__linux__)
+ flags &= -MAP_HUGETLB;
+ #elif defined(__FreeBSD__)
+ flags &= -MAP_ALIGNED_SUPER;
+ #elif defined(__sun)
+ flags &= -MAP_ALIGN;
+ #endif
+ ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0);
+
+ }
+
+#endif
+
+ if (ret == MAP_FAILED) {
+
+ if (hard_fail) FATAL("mmap() failed on alloc (OOM?)");
+
+ DEBUGF("mmap() failed on alloc (OOM?)");
+
+ return NULL;
+
+ }
+
+ /* Set PROT_NONE on the last page. */
+
+ if (mprotect(ret + PG_COUNT(rlen + 8) * PAGE_SIZE, PAGE_SIZE, PROT_NONE))
+ FATAL("mprotect() failed when allocating memory");
+
+ /* Offset the return pointer so that it's right-aligned to the page
+ boundary. */
+
+ ret += PAGE_SIZE * PG_COUNT(rlen + 8) - rlen - 8;
+
+ /* Store allocation metadata. */
+
+ ret += 8;
+
+ PTR_L(ret) = len;
+ PTR_C(ret) = alloc_canary;
+
+ total_mem += len;
+
+ if (rlen != len) {
+
+ size_t i;
+ for (i = len; i < rlen; ++i)
+ ret[i] = TAIL_ALLOC_CANARY;
+
+ }
+
+ return ret;
+
+}
+
+/* The "user-facing" wrapper for calloc(). This just checks for overflows and
+ displays debug messages if requested. */
+
+void *calloc(size_t elem_len, size_t elem_cnt) {
+
+ void *ret;
+
+ size_t len = elem_len * elem_cnt;
+
+ /* Perform some sanity checks to detect obvious issues... */
+
+ if (elem_cnt && len / elem_cnt != elem_len) {
+
+ if (no_calloc_over) {
+
+ DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len,
+ elem_cnt);
+ return NULL;
+
+ }
+
+ FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt);
+
+ }
+
+ ret = __dislocator_alloc(len);
+
+ DEBUGF("calloc(%zu, %zu) = %p [%zu total]", elem_len, elem_cnt, ret,
+ total_mem);
+
+ return ret;
+
+}
+
+/* The wrapper for malloc(). Roughly the same, also clobbers the returned
+ memory (unlike calloc(), malloc() is not guaranteed to return zeroed
+ memory). */
+
+void *malloc(size_t len) {
+
+ void *ret;
+
+ ret = __dislocator_alloc(len);
+
+ DEBUGF("malloc(%zu) = %p [%zu total]", len, ret, total_mem);
+
+ if (ret && len) memset(ret, ALLOC_CLOBBER, len);
+
+ return ret;
+
+}
+
+/* The wrapper for free(). This simply marks the entire region as PROT_NONE.
+ If the region is already freed, the code will segfault during the attempt to
+ read the canary. Not very graceful, but works, right? */
+
+void free(void *ptr) {
+
+ u32 len;
+
+ DEBUGF("free(%p)", ptr);
+
+ if (!ptr) return;
+
+ if (PTR_C(ptr) != alloc_canary) FATAL("bad allocator canary on free()");
+
+ len = PTR_L(ptr);
+
+ total_mem -= len;
+
+ if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) {
+
+ u8 * ptr_ = ptr;
+ size_t rlen = (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE;
+ for (; len < rlen; ++len)
+ if (ptr_[len] != TAIL_ALLOC_CANARY)
+ FATAL("bad tail allocator canary on free()");
+
+ }
+
+ /* Protect everything. Note that the extra page at the end is already
+ set as PROT_NONE, so we don't need to touch that. */
+
+ ptr -= PAGE_SIZE * PG_COUNT(len + 8) - len - 8;
+
+ if (mprotect(ptr - 8, PG_COUNT(len + 8) * PAGE_SIZE, PROT_NONE))
+ FATAL("mprotect() failed when freeing memory");
+
+ /* Keep the mapping; this is wasteful, but prevents ptr reuse. */
+
+}
+
+/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer,
+ move data, and then free (aka mprotect()) the original one. */
+
+void *realloc(void *ptr, size_t len) {
+
+ void *ret;
+
+ ret = malloc(len);
+
+ if (ret && ptr) {
+
+ if (PTR_C(ptr) != alloc_canary) FATAL("bad allocator canary on realloc()");
+ // Here the tail canary check is delayed to free()
+
+ memcpy(ret, ptr, MIN(len, PTR_L(ptr)));
+ free(ptr);
+
+ }
+
+ DEBUGF("realloc(%p, %zu) = %p [%zu total]", ptr, len, ret, total_mem);
+
+ return ret;
+
+}
+
+/* posix_memalign we mainly check the proper alignment argument
+ if the requested size fits within the alignment we do
+ a normal request */
+
+int posix_memalign(void **ptr, size_t align, size_t len) {
+
+ // if (*ptr == NULL) return EINVAL; // (andrea) Why? I comment it out for now
+ if ((align % 2) || (align % sizeof(void *))) return EINVAL;
+ if (len == 0) {
+
+ *ptr = NULL;
+ return 0;
+
+ }
+
+ size_t rem = len % align;
+ if (rem) len += align - rem;
+
+ *ptr = __dislocator_alloc(len);
+
+ if (*ptr && len) memset(*ptr, ALLOC_CLOBBER, len);
+
+ DEBUGF("posix_memalign(%p %zu, %zu) [*ptr = %p]", ptr, align, len, *ptr);
+
+ return 0;
+
+}
+
+/* just the non-posix fashion */
+
+void *memalign(size_t align, size_t len) {
+
+ void *ret = NULL;
+
+ if (posix_memalign(&ret, align, len)) {
+
+ DEBUGF("memalign(%zu, %zu) failed", align, len);
+
+ }
+
+ return ret;
+
+}
+
+/* sort of C11 alias of memalign only more severe, alignment-wise */
+
+void *aligned_alloc(size_t align, size_t len) {
+
+ void *ret = NULL;
+
+ if ((len % align)) return NULL;
+
+ if (posix_memalign(&ret, align, len)) {
+
+ DEBUGF("aligned_alloc(%zu, %zu) failed", align, len);
+
+ }
+
+ return ret;
+
+}
+
+/* specific BSD api mainly checking possible overflow for the size */
+
+void *reallocarray(void *ptr, size_t elem_len, size_t elem_cnt) {
+
+ const size_t elem_lim = 1UL << (sizeof(size_t) * 4);
+ const size_t elem_tot = elem_len * elem_cnt;
+ void * ret = NULL;
+
+ if ((elem_len >= elem_lim || elem_cnt >= elem_lim) && elem_len > 0 &&
+ elem_cnt > (SIZE_MAX / elem_len)) {
+
+ DEBUGF("reallocarray size overflow (%zu)", elem_tot);
+
+ } else {
+
+ ret = realloc(ptr, elem_tot);
+
+ }
+
+ return ret;
+
+}
+
+#if !defined(__ANDROID__)
+size_t malloc_usable_size(void *ptr) {
+
+#else
+size_t malloc_usable_size(const void *ptr) {
+
+#endif
+
+ return ptr ? PTR_L(ptr) : 0;
+
+}
+
+__attribute__((constructor)) void __dislocator_init(void) {
+
+ u8 *tmp = (u8 *)getenv("AFL_LD_LIMIT_MB");
+
+ if (tmp) {
+
+ u8 *tok;
+ s32 mmem = (s32)strtol((char *)tmp, (char **)&tok, 10);
+ if (*tok != '\0' || errno == ERANGE) FATAL("Bad value for AFL_LD_LIMIT_MB");
+ max_mem = mmem * 1024 * 1024;
+
+ }
+
+ alloc_canary = ALLOC_CANARY;
+ tmp = (u8 *)getenv("AFL_RANDOM_ALLOC_CANARY");
+
+ if (tmp) arc4random_buf(&alloc_canary, sizeof(alloc_canary));
+
+ alloc_verbose = !!getenv("AFL_LD_VERBOSE");
+ hard_fail = !!getenv("AFL_LD_HARD_FAIL");
+ no_calloc_over = !!getenv("AFL_LD_NO_CALLOC_OVER");
+ align_allocations = !!getenv("AFL_ALIGNED_ALLOC");
+
+}
+
+/* NetBSD fault handler specific api subset */
+
+void (*esetfunc(void (*fn)(int, const char *, ...)))(int, const char *, ...) {
+
+ /* Might not be meaningful to implement; upper calls already report errors */
+ return NULL;
+
+}
+
+void *emalloc(size_t len) {
+
+ return malloc(len);
+
+}
+
+void *ecalloc(size_t elem_len, size_t elem_cnt) {
+
+ return calloc(elem_len, elem_cnt);
+
+}
+
+void *erealloc(void *ptr, size_t len) {
+
+ return realloc(ptr, len);
+
+}
+
diff --git a/utils/libtokencap/Makefile b/utils/libtokencap/Makefile
new file mode 100644
index 00000000..8bbdc259
--- /dev/null
+++ b/utils/libtokencap/Makefile
@@ -0,0 +1,94 @@
+#
+# american fuzzy lop++ - libtokencap
+# --------------------------------
+#
+# Originally written by Michal Zalewski
+#
+# Copyright 2016 Google Inc. 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
+#
+
+PREFIX ?= /usr/local
+HELPER_PATH = $(PREFIX)/lib/afl
+DOC_PATH ?= $(PREFIX)/share/doc/afl
+MAN_PATH ?= $(PREFIX)/share/man/man8
+
+VERSION = $(shell grep '^\#define VERSION ' ../../config.h | cut -d '"' -f2)
+
+CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2
+CFLAGS += -I ../../include/ -Wall -g -Wno-pointer-sign
+
+
+UNAME_S =$(shell uname -s)# GNU make
+UNAME_S:sh=uname -s # BSD make
+_UNIQ=_QINU_
+
+ _OS_DL = $(_UNIQ)$(UNAME_S)
+ __OS_DL = $(_OS_DL:$(_UNIQ)Linux=$(_UNIQ))
+ ___OS_DL = $(__OS_DL:$(_UNIQ)Darwin=$(_UNIQ))
+ ____OS_DL = $(___OS_DL:$(_UNIQ)$(UNAME_S)=)
+_____OS_DL = $(____OS_DL:$(_UNIQ)="-ldl")
+
+ _OS_TARGET = $(___OS_DL:$(_UNIQ)FreeBSD=$(_UNIQ))
+ __OS_TARGET = $(_OS_TARGET:$(_UNIQ)OpenBSD=$(_UNIQ))
+ ___OS_TARGET = $(__OS_TARGET:$(_UNIQ)NetBSD=$(_UNIQ))
+ ____OS_TARGET = $(___OS_TARGET:$(_UNIQ)Haiku=$(_UNIQ))
+ _____OS_TARGET = $(____OS_TARGET:$(_UNIQ)SunOS=$(_UNIQ))
+______OS_TARGET = $(_____OS_TARGET:$(_UNIQ)$(UNAME_S)=)
+
+TARGETS = $(______OS_TARGET:$(_UNIQ)=libtokencap.so)
+
+LDFLAGS += $(_____OS_DL)
+
+#ifeq "$(shell uname)" "Linux"
+# TARGETS = libtokencap.so
+# LDFLAGS += -ldl
+#endif
+#ifeq "$(shell uname)" "Darwin"
+# TARGETS = libtokencap.so
+# LDFLAGS += -ldl
+#endif
+#ifeq "$(shell uname)" "FreeBSD"
+# TARGETS = libtokencap.so
+#endif
+#ifeq "$(shell uname)" "OpenBSD"
+# TARGETS = libtokencap.so
+#endif
+#ifeq "$(shell uname)" "NetBSD"
+# TARGETS = libtokencap.so
+#endif
+#ifeq "$(shell uname)" "DragonFly"
+# TARGETS = libtokencap.so
+# LDFLAGS += -ldl
+#endif
+all: $(TARGETS)
+
+libtokencap.so: libtokencap.so.c ../../config.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS)
+ cp -f libtokencap.so ../../
+
+.NOTPARALLEL: clean
+
+debug:
+ @echo $(UNAME_S)$(_UNIQ) | hexdump -C
+ @echo from $(____OS_DL) : $(_UNIQ)$(UNAME_S) = -\> $(_____OS_DL)
+ @echo from $(_____OS_DL) : $(_UNIQ) = -ldl -\> $(______OS_DL)
+ @echo from $(____OS_DL) : $(_UNIQ)FreeBSD = $(_UNIQ) -\> $(_OS_TARGET)
+ @echo from $(_OS_TARGET) : $(_UNIQ)OpenBSD = $(_UNIQ) -\> $(__OS_TARGET)
+ @echo from $(__OS_TARGET) : $(_UNIQ)NetBSD = $(_UNIQ) -\> $(___OS_TARGET)
+ @echo from $(___OS_TARGET) : $(_UNIQ)$(_UNIQ) = -\> $(____OS_TARGET)
+ @echo from $(____OS_TARGET) : $(_UNIQ) = libtokencap.so -\> $(TARGETS)
+
+clean:
+ rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
+ rm -fv ../../libtokencap.so
+
+install: all
+ install -m 755 -d $${DESTDIR}$(HELPER_PATH)
+ install -m 755 ../../libtokencap.so $${DESTDIR}$(HELPER_PATH)
+ install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.tokencap.md
diff --git a/utils/libtokencap/README.md b/utils/libtokencap/README.md
new file mode 100644
index 00000000..13a440da
--- /dev/null
+++ b/utils/libtokencap/README.md
@@ -0,0 +1,64 @@
+# strcmp() / memcmp() token capture library
+
+ (See ../README.md for the general instruction manual.)
+
+This companion library allows you to instrument `strcmp()`, `memcmp()`,
+and related functions to automatically extract syntax tokens passed to any of
+these libcalls. The resulting list of tokens may be then given as a starting
+dictionary to afl-fuzz (the -x option) to improve coverage on subsequent
+fuzzing runs.
+
+This may help improving coverage in some targets, and do precisely nothing in
+others. In some cases, it may even make things worse: if libtokencap picks up
+syntax tokens that are not used to process the input data, but that are a part
+of - say - parsing a config file... well, you're going to end up wasting a lot
+of CPU time on trying them out in the input stream. In other words, use this
+feature with care. Manually screening the resulting dictionary is almost
+always a necessity.
+
+As for the actual operation: the library stores tokens, without any deduping,
+by appending them to a file specified via AFL_TOKEN_FILE. If the variable is not
+set, the tool uses stderr (which is probably not what you want).
+
+Similarly to afl-tmin, the library is not "proprietary" and can be used with
+other fuzzers or testing tools without the need for any code tweaks. It does not
+require AFL-instrumented binaries to work.
+
+To use the library, you *need* to make sure that your fuzzing target is compiled
+with -fno-builtin and is linked dynamically. If you wish to automate the first
+part without mucking with CFLAGS in Makefiles, you can set AFL_NO_BUILTIN=1
+when using afl-gcc. This setting specifically adds the following flags:
+
+```
+ -fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp
+ -fno-builtin-strcasencmp -fno-builtin-memcmp -fno-builtin-strstr
+ -fno-builtin-strcasestr
+```
+
+The next step is simply loading this library via LD_PRELOAD. The optimal usage
+pattern is to allow afl-fuzz to fuzz normally for a while and build up a corpus,
+and then fire off the target binary, with libtokencap.so loaded, on every file
+found by AFL in that earlier run. This demonstrates the basic principle:
+
+```
+ export AFL_TOKEN_FILE=$PWD/temp_output.txt
+
+ for i in <out_dir>/queue/id*; do
+ LD_PRELOAD=/path/to/libtokencap.so \
+ /path/to/target/program [...params, including $i...]
+ done
+
+ sort -u temp_output.txt >afl_dictionary.txt
+```
+
+If you don't get any results, the target library is probably not using strcmp()
+and memcmp() to parse input; or you haven't compiled it with -fno-builtin; or
+the whole thing isn't dynamically linked, and LD_PRELOAD is having no effect.
+
+Portability hints: There is probably no particularly portable and non-invasive
+way to distinguish between read-only and read-write memory mappings.
+The `__tokencap_load_mappings()` function is the only thing that would
+need to be changed for other OSes.
+
+Current supported OSes are: Linux, Darwin, FreeBSD (thanks to @devnexen)
+
diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c
new file mode 100644
index 00000000..3629e804
--- /dev/null
+++ b/utils/libtokencap/libtokencap.so.c
@@ -0,0 +1,794 @@
+/*
+
+ american fuzzy lop++ - extract tokens passed to strcmp / memcmp
+ -------------------------------------------------------------
+
+ Originally written by Michal Zalewski
+
+ Copyright 2016 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
+
+ This Linux-only companion library allows you to instrument strcmp(),
+ memcmp(), and related functions to automatically extract tokens.
+ See README.tokencap.md for more info.
+
+ */
+
+#ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+#include "../types.h"
+#include "../config.h"
+
+#if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__ && \
+ !defined __OpenBSD__ && !defined __NetBSD__ && !defined __DragonFly__ && \
+ !defined(__HAIKU__) && !defined(__sun)
+ #error "Sorry, this library is unsupported in this platform for now!"
+#endif /* !__linux__ && !__APPLE__ && ! __FreeBSD__ && ! __OpenBSD__ && \
+ !__NetBSD__*/
+
+#if defined __APPLE__
+ #include <mach/vm_map.h>
+ #include <mach/mach_init.h>
+#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+ #if !defined __NetBSD__
+ #include <sys/user.h>
+ #endif
+ #include <sys/mman.h>
+#elif defined __HAIKU__
+ #include <kernel/image.h>
+#elif defined __sun
+ /* For map addresses the old struct is enough */
+ #include <sys/procfs.h>
+ #include <limits.h>
+#endif
+
+#include <dlfcn.h>
+
+#ifdef RTLD_NEXT
+/* The libc functions are a magnitude faster than our replacements.
+ Use them when RTLD_NEXT is available. */
+int (*__libc_strcmp)(const char *str1, const char *str2);
+int (*__libc_strncmp)(const char *str1, const char *str2, size_t len);
+int (*__libc_strcasecmp)(const char *str1, const char *str2);
+int (*__libc_strncasecmp)(const char *str1, const char *str2, size_t len);
+int (*__libc_memcmp)(const void *mem1, const void *mem2, size_t len);
+int (*__libc_bcmp)(const void *mem1, const void *mem2, size_t len);
+char *(*__libc_strstr)(const char *haystack, const char *needle);
+char *(*__libc_strcasestr)(const char *haystack, const char *needle);
+void *(*__libc_memmem)(const void *haystack, size_t haystack_len,
+ const void *needle, size_t needle_len);
+#endif
+
+/* Mapping data and such */
+
+#define MAX_MAPPINGS 1024
+
+static struct mapping { void *st, *en; } __tokencap_ro[MAX_MAPPINGS];
+
+static u32 __tokencap_ro_cnt;
+static u8 __tokencap_ro_loaded;
+static int __tokencap_out_file = -1;
+static pid_t __tokencap_pid = -1;
+
+/* Identify read-only regions in memory. Only parameters that fall into these
+ ranges are worth dumping when passed to strcmp() and so on. Read-write
+ regions are far more likely to contain user input instead. */
+
+static void __tokencap_load_mappings(void) {
+
+#if defined __linux__
+
+ u8 buf[MAX_LINE];
+ FILE *f = fopen("/proc/self/maps", "r");
+
+ __tokencap_ro_loaded = 1;
+
+ if (!f) return;
+
+ while (fgets(buf, MAX_LINE, f)) {
+
+ u8 rf, wf;
+ void *st, *en;
+
+ if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue;
+ if (wf == 'w' || rf != 'r') continue;
+
+ __tokencap_ro[__tokencap_ro_cnt].st = (void *)st;
+ __tokencap_ro[__tokencap_ro_cnt].en = (void *)en;
+
+ if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
+
+ }
+
+ fclose(f);
+
+#elif defined __APPLE__
+
+ struct vm_region_submap_info_64 region;
+ mach_msg_type_number_t cnt = VM_REGION_SUBMAP_INFO_COUNT_64;
+ vm_address_t base = 0;
+ vm_size_t size = 0;
+ natural_t depth = 0;
+
+ __tokencap_ro_loaded = 1;
+
+ while (1) {
+
+ if (vm_region_recurse_64(mach_task_self(), &base, &size, &depth,
+ (vm_region_info_64_t)&region,
+ &cnt) != KERN_SUCCESS)
+ break;
+
+ if (region.is_submap) {
+
+ depth++;
+
+ } else {
+
+ /* We only care of main map addresses and the read only kinds */
+ if ((region.protection & VM_PROT_READ) &&
+ !(region.protection & VM_PROT_WRITE)) {
+
+ __tokencap_ro[__tokencap_ro_cnt].st = (void *)base;
+ __tokencap_ro[__tokencap_ro_cnt].en = (void *)(base + size);
+
+ if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
+
+ }
+
+ base += size;
+ size = 0;
+
+ }
+
+ }
+
+#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
+
+ #if defined __FreeBSD__
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid};
+ #elif defined __OpenBSD__
+ int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid};
+ #elif defined __NetBSD__
+ int mib[] = {CTL_VM, VM_PROC, VM_PROC_MAP, __tokencap_pid,
+ sizeof(struct kinfo_vmentry)};
+ #endif
+ char * buf, *low, *high;
+ size_t miblen = sizeof(mib) / sizeof(mib[0]);
+ size_t len;
+
+ if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) return;
+
+ #if defined __FreeBSD__ || defined __NetBSD__
+ len = len * 4 / 3;
+ #elif defined __OpenBSD__
+ len -= len % sizeof(struct kinfo_vmentry);
+ #endif
+
+ buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+ if (buf == MAP_FAILED) return;
+
+ if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) {
+
+ munmap(buf, len);
+ return;
+
+ }
+
+ low = buf;
+ high = low + len;
+
+ __tokencap_ro_loaded = 1;
+
+ while (low < high) {
+
+ struct kinfo_vmentry *region = (struct kinfo_vmentry *)low;
+
+ #if defined __FreeBSD__ || defined __NetBSD__
+
+ #if defined __FreeBSD__
+ size_t size = region->kve_structsize;
+
+ if (size == 0) break;
+ #elif defined __NetBSD__
+ size_t size = sizeof(*region);
+ #endif
+
+ /* We go through the whole mapping of the process and track read-only
+ * addresses */
+ if ((region->kve_protection & KVME_PROT_READ) &&
+ !(region->kve_protection & KVME_PROT_WRITE)) {
+
+ #elif defined __OpenBSD__
+
+ size_t size = sizeof(*region);
+
+ /* We go through the whole mapping of the process and track read-only
+ * addresses */
+ if ((region->kve_protection & KVE_PROT_READ) &&
+ !(region->kve_protection & KVE_PROT_WRITE)) {
+
+ #endif
+ __tokencap_ro[__tokencap_ro_cnt].st = (void *)region->kve_start;
+ __tokencap_ro[__tokencap_ro_cnt].en = (void *)region->kve_end;
+
+ if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
+
+ }
+
+ low += size;
+
+ }
+
+ munmap(buf, len);
+#elif defined __HAIKU__
+ image_info ii;
+ int32_t group = 0;
+
+ __tokencap_ro_loaded = 1;
+
+ while (get_next_image_info(0, &group, &ii) == B_OK) {
+
+ __tokencap_ro[__tokencap_ro_cnt].st = ii.text;
+ __tokencap_ro[__tokencap_ro_cnt].en = ((char *)ii.text) + ii.text_size;
+
+ if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
+
+ }
+
+#elif defined __sun
+ prmap_t *c, *map;
+ char path[PATH_MAX];
+ ssize_t r;
+ size_t hint;
+ int fd;
+
+ snprintf(path, sizeof(path), "/proc/%ld/map", getpid());
+ fd = open(path, O_RDONLY);
+ hint = (1 << 20);
+ map = malloc(hint);
+
+ __tokencap_ro_loaded = 1;
+
+ for (; (r = pread(fd, map, hint, 0)) == hint;) {
+
+ hint <<= 1;
+ map = realloc(map, hint);
+
+ }
+
+ for (c = map; r > 0; c++, r -= sizeof(prmap_t)) {
+
+ __tokencap_ro[__tokencap_ro_cnt].st = (void *)c->pr_vaddr;
+ __tokencap_ro[__tokencap_ro_cnt].en = (void *)(c->pr_vaddr + c->pr_size);
+
+ if (++__tokencap_ro_cnt == MAX_MAPPINGS) break;
+
+ }
+
+ free(map);
+ close(fd);
+#endif
+
+}
+
+/* Check an address against the list of read-only mappings. */
+
+static u8 __tokencap_is_ro(const void *ptr) {
+
+ u32 i;
+
+ if (!__tokencap_ro_loaded) __tokencap_load_mappings();
+
+ for (i = 0; i < __tokencap_ro_cnt; i++)
+ if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1;
+
+ return 0;
+
+}
+
+/* Dump an interesting token to output file, quoting and escaping it
+ properly. */
+
+static void __tokencap_dump(const u8 *ptr, size_t len, u8 is_text) {
+
+ u8 buf[MAX_AUTO_EXTRA * 4 + 1];
+ u32 i;
+ u32 pos = 0;
+
+ if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA || __tokencap_out_file == -1)
+ return;
+
+ for (i = 0; i < len; i++) {
+
+ if (is_text && !ptr[i]) break;
+
+ switch (ptr[i]) {
+
+ case 0 ... 31:
+ case 127 ... 255:
+ case '\"':
+ case '\\':
+
+ sprintf(buf + pos, "\\x%02x", ptr[i]);
+ pos += 4;
+ break;
+
+ default:
+ buf[pos++] = ptr[i];
+
+ }
+
+ }
+
+ buf[pos] = 0;
+
+ int wrt_ok = (1 == write(__tokencap_out_file, "\"", 1));
+ wrt_ok &= (pos == write(__tokencap_out_file, buf, pos));
+ wrt_ok &= (2 == write(__tokencap_out_file, "\"\n", 2));
+
+}
+
+/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used
+ only if the target is compiled with -fno-builtins and linked dynamically. */
+
+#undef strcmp
+
+int strcmp(const char *str1, const char *str2) {
+
+ if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
+ if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_strcmp) return __libc_strcmp(str1, str2);
+#endif
+
+ while (1) {
+
+ const unsigned char c1 = *str1, c2 = *str2;
+
+ if (c1 != c2) return (c1 > c2) ? 1 : -1;
+ if (!c1) return 0;
+ str1++;
+ str2++;
+
+ }
+
+}
+
+#undef strncmp
+
+int strncmp(const char *str1, const char *str2, size_t len) {
+
+ if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
+ if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_strncmp) return __libc_strncmp(str1, str2, len);
+#endif
+
+ while (len--) {
+
+ unsigned char c1 = *str1, c2 = *str2;
+
+ if (c1 != c2) return (c1 > c2) ? 1 : -1;
+ if (!c1) return 0;
+ str1++;
+ str2++;
+
+ }
+
+ return 0;
+
+}
+
+#undef strcasecmp
+
+int strcasecmp(const char *str1, const char *str2) {
+
+ if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1);
+ if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_strcasecmp) return __libc_strcasecmp(str1, str2);
+#endif
+
+ while (1) {
+
+ const unsigned char c1 = tolower((int)*str1), c2 = tolower((int)*str2);
+
+ if (c1 != c2) return (c1 > c2) ? 1 : -1;
+ if (!c1) return 0;
+ str1++;
+ str2++;
+
+ }
+
+}
+
+#undef strncasecmp
+
+int strncasecmp(const char *str1, const char *str2, size_t len) {
+
+ if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1);
+ if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_strncasecmp) return __libc_strncasecmp(str1, str2, len);
+#endif
+
+ while (len--) {
+
+ const unsigned char c1 = tolower((int)*str1), c2 = tolower((int)*str2);
+
+ if (c1 != c2) return (c1 > c2) ? 1 : -1;
+ if (!c1) return 0;
+ str1++;
+ str2++;
+
+ }
+
+ return 0;
+
+}
+
+#undef memcmp
+
+int memcmp(const void *mem1, const void *mem2, size_t len) {
+
+ if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0);
+ if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0);
+
+#ifdef RTLD_NEXT
+ if (__libc_memcmp) return __libc_memcmp(mem1, mem2, len);
+#endif
+
+ const char *strmem1 = (const char *)mem1;
+ const char *strmem2 = (const char *)mem2;
+
+ while (len--) {
+
+ const unsigned char c1 = *strmem1, c2 = *strmem2;
+ if (c1 != c2) return (c1 > c2) ? 1 : -1;
+ strmem1++;
+ strmem2++;
+
+ }
+
+ return 0;
+
+}
+
+#undef bcmp
+
+int bcmp(const void *mem1, const void *mem2, size_t len) {
+
+ if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0);
+ if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0);
+
+#ifdef RTLD_NEXT
+ if (__libc_bcmp) return __libc_bcmp(mem1, mem2, len);
+#endif
+
+ const char *strmem1 = (const char *)mem1;
+ const char *strmem2 = (const char *)mem2;
+
+ while (len--) {
+
+ int diff = *strmem1 ^ *strmem2;
+ if (diff != 0) return 1;
+ strmem1++;
+ strmem2++;
+
+ }
+
+ return 0;
+
+}
+
+#undef strstr
+
+char *strstr(const char *haystack, const char *needle) {
+
+ if (__tokencap_is_ro(haystack))
+ __tokencap_dump(haystack, strlen(haystack), 1);
+
+ if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_strstr) return __libc_strstr(haystack, needle);
+#endif
+
+ do {
+
+ const char *n = needle;
+ const char *h = haystack;
+
+ while (*n && *h && *n == *h)
+ n++, h++;
+
+ if (!*n) return (char *)haystack;
+
+ } while (*(haystack++));
+
+ return 0;
+
+}
+
+#undef strcasestr
+
+char *strcasestr(const char *haystack, const char *needle) {
+
+ if (__tokencap_is_ro(haystack))
+ __tokencap_dump(haystack, strlen(haystack), 1);
+
+ if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_strcasestr) return __libc_strcasestr(haystack, needle);
+#endif
+
+ do {
+
+ const char *n = needle;
+ const char *h = haystack;
+
+ while (*n && *h && tolower((int)*n) == tolower((int)*h))
+ n++, h++;
+
+ if (!*n) return (char *)haystack;
+
+ } while (*(haystack++));
+
+ return 0;
+
+}
+
+#undef memmem
+
+void *memmem(const void *haystack, size_t haystack_len, const void *needle,
+ size_t needle_len) {
+
+ if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, haystack_len, 1);
+
+ if (__tokencap_is_ro(needle)) __tokencap_dump(needle, needle_len, 1);
+
+#ifdef RTLD_NEXT
+ if (__libc_memmem)
+ return __libc_memmem(haystack, haystack_len, needle, needle_len);
+#endif
+
+ const char *n = (const char *)needle;
+ const char *h = (const char *)haystack;
+ if (haystack_len < needle_len) return 0;
+ if (needle_len == 0) return (void *)haystack;
+ if (needle_len == 1) return memchr(haystack, *n, haystack_len);
+
+ const char *end = h + (haystack_len - needle_len);
+
+ do {
+
+ if (*h == *n) {
+
+ if (memcmp(h, n, needle_len) == 0) return (void *)h;
+
+ }
+
+ } while (h++ <= end);
+
+ return 0;
+
+}
+
+/* Common libraries wrappers (from honggfuzz) */
+
+/*
+ * Apache's httpd wrappers
+ */
+int ap_cstr_casecmp(const char *s1, const char *s2) {
+
+ return strcasecmp(s1, s2);
+
+}
+
+int ap_cstr_casecmpn(const char *s1, const char *s2, size_t n) {
+
+ return strncasecmp(s1, s2, n);
+
+}
+
+const char *ap_strcasestr(const char *s1, const char *s2) {
+
+ return strcasestr(s1, s2);
+
+}
+
+int apr_cstr_casecmp(const char *s1, const char *s2) {
+
+ return strcasecmp(s1, s2);
+
+}
+
+int apr_cstr_casecmpn(const char *s1, const char *s2, size_t n) {
+
+ return strncasecmp(s1, s2, n);
+
+}
+
+/*
+ * *SSL wrappers
+ */
+int CRYPTO_memcmp(const void *m1, const void *m2, size_t len) {
+
+ return memcmp(m1, m2, len);
+
+}
+
+int OPENSSL_memcmp(const void *m1, const void *m2, size_t len) {
+
+ return memcmp(m1, m2, len);
+
+}
+
+int OPENSSL_strcasecmp(const char *s1, const char *s2) {
+
+ return strcasecmp(s1, s2);
+
+}
+
+int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t len) {
+
+ return strncasecmp(s1, s2, len);
+
+}
+
+int32_t memcmpct(const void *s1, const void *s2, size_t len) {
+
+ return memcmp(s1, s2, len);
+
+}
+
+/*
+ * libXML wrappers
+ */
+int xmlStrncmp(const char *s1, const char *s2, int len) {
+
+ if (len <= 0) { return 0; }
+ if (s1 == s2) { return 0; }
+ if (s1 == NULL) { return -1; }
+ if (s2 == NULL) { return 1; }
+ return strncmp(s1, s2, (size_t)len);
+
+}
+
+int xmlStrcmp(const char *s1, const char *s2) {
+
+ if (s1 == s2) { return 0; }
+ if (s1 == NULL) { return -1; }
+ if (s2 == NULL) { return 1; }
+ return strcmp(s1, s2);
+
+}
+
+int xmlStrEqual(const char *s1, const char *s2) {
+
+ if (s1 == s2) { return 1; }
+ if (s1 == NULL) { return 0; }
+ if (s2 == NULL) { return 0; }
+ if (strcmp(s1, s2) == 0) { return 1; }
+ return 0;
+
+}
+
+int xmlStrcasecmp(const char *s1, const char *s2) {
+
+ if (s1 == s2) { return 0; }
+ if (s1 == NULL) { return -1; }
+ if (s2 == NULL) { return 1; }
+ return strcasecmp(s1, s2);
+
+}
+
+int xmlStrncasecmp(const char *s1, const char *s2, int len) {
+
+ if (len <= 0) { return 0; }
+ if (s1 == s2) { return 0; }
+ if (s1 == NULL) { return -1; }
+ if (s2 == NULL) { return 1; }
+ return strncasecmp(s1, s2, (size_t)len);
+
+}
+
+const char *xmlStrstr(const char *haystack, const char *needle) {
+
+ if (haystack == NULL) { return NULL; }
+ if (needle == NULL) { return NULL; }
+ return strstr(haystack, needle);
+
+}
+
+const char *xmlStrcasestr(const char *haystack, const char *needle) {
+
+ if (haystack == NULL) { return NULL; }
+ if (needle == NULL) { return NULL; }
+ return strcasestr(haystack, needle);
+
+}
+
+/*
+ * Samba wrappers
+ */
+int memcmp_const_time(const void *s1, const void *s2, size_t n) {
+
+ return memcmp(s1, s2, n);
+
+}
+
+bool strcsequal(const void *s1, const void *s2) {
+
+ if (s1 == s2) { return true; }
+ if (!s1 || !s2) { return false; }
+ return (strcmp(s1, s2) == 0);
+
+}
+
+/* bcmp/memcmp BSD flavors, similar to CRYPTO_memcmp */
+
+int timingsafe_bcmp(const void *mem1, const void *mem2, size_t len) {
+
+ return bcmp(mem1, mem2, len);
+
+}
+
+int timingsafe_memcmp(const void *mem1, const void *mem2, size_t len) {
+
+ return memcmp(mem1, mem2, len);
+
+}
+
+/* Init code to open the output file (or default to stderr). */
+
+__attribute__((constructor)) void __tokencap_init(void) {
+
+ u8 *fn = getenv("AFL_TOKEN_FILE");
+ if (fn) __tokencap_out_file = open(fn, O_RDWR | O_CREAT | O_APPEND, 0655);
+ if (__tokencap_out_file == -1) __tokencap_out_file = STDERR_FILENO;
+ __tokencap_pid = getpid();
+
+#ifdef RTLD_NEXT
+ __libc_strcmp = dlsym(RTLD_NEXT, "strcmp");
+ __libc_strncmp = dlsym(RTLD_NEXT, "strncmp");
+ __libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp");
+ __libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp");
+ __libc_memcmp = dlsym(RTLD_NEXT, "memcmp");
+ __libc_bcmp = dlsym(RTLD_NEXT, "bcmp");
+ __libc_strstr = dlsym(RTLD_NEXT, "strstr");
+ __libc_strcasestr = dlsym(RTLD_NEXT, "strcasestr");
+ __libc_memmem = dlsym(RTLD_NEXT, "memmem");
+#endif
+
+}
+
+/* closing as best as we can the tokens file */
+__attribute__((destructor)) void __tokencap_shutdown(void) {
+
+ if (__tokencap_out_file != STDERR_FILENO) close(__tokencap_out_file);
+
+}
+
diff --git a/utils/qbdi_mode/README.md b/utils/qbdi_mode/README.md
new file mode 100755
index 00000000..641a6e85
--- /dev/null
+++ b/utils/qbdi_mode/README.md
@@ -0,0 +1,199 @@
+# qbdi-based binary-only instrumentation for afl-fuzz
+
+## 1) Introduction
+
+The code in ./qbdi_mode allows you to build a standalone feature that
+using the QBDI framework to fuzz android native library.
+
+
+## 2) Build
+
+First download the Android NDK
+
+```
+https://developer.android.com/ndk/downloads
+https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip
+```
+
+Then unzip it and build the standalone-toolchain
+For x86_64 standalone-toolchain
+
+```
+unzip android-ndk-r20-linux-x86_64.zip
+cd android-ndk-r20/
+./build/tools/make_standalone_toolchain.py --arch x86_64 --api 21 --install-dir ../android-standalone-toolchain-x86_64
+```
+
+For x86 standalone-toolchain
+
+```
+./build/tools/make_standalone_toolchain.py --arch x86 --api 21 --install-dir ../android-standalone-toolchain-x86
+```
+
+In alternative you can also use the prebuilt toolchain, in that case make sure to set the proper CC and CXX env variables because there are many different compilers for each API version in the prebuilt toolchain.
+
+For example:
+
+```
+export STANDALONE_TOOLCHAIN_PATH=~/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/
+export CC=x86_64-linux-android21-clang
+export CXX=x86_64-linux-android21-clang++
+```
+
+Then download the QBDI SDK from website
+
+```
+https://qbdi.quarkslab.com/
+```
+
+For Android x86_64
+```
+https://github.com/QBDI/QBDI/releases/download/v0.7.0/QBDI-0.7.0-android-X86_64.tar.gz
+```
+
+Then decompress the sdk
+
+```
+mkdir android-qbdi-sdk-x86_64
+cp QBDI-0.7.0-android-X86_64.tar.gz android-qbdi-sdk-x86_64/
+cd android-qbdi-sdk-x86_64/
+tar xvf QBDI-0.7.0-android-X86_64.tar.gz
+```
+
+Now set the `STANDALONE_TOOLCHAIN_PATH` to the path of standalone-toolchain
+
+```
+export STANDALONE_TOOLCHAIN_PATH=/home/hac425/workspace/android-standalone-toolchain-x86_64
+```
+
+set the `QBDI_SDK_PATH` to the path of QBDI SDK
+
+```
+export QBDI_SDK_PATH=/home/hac425/workspace/AFLplusplus/qbdi_mode/android-qbdi-sdk-x86_64/
+```
+
+Then run the build.sh
+
+```
+./build.sh x86_64
+```
+
+this could build the afl-fuzz and also the qbdi template for android x86_64
+
+
+### Example
+
+The demo-so.c is an vulnerable library, it has a function for test
+
+```c
+int target_func(char *buf, int size) {
+
+ printf("buffer:%p, size:%p\n", buf, size);
+ switch (buf[0]) {
+
+ case 1:
+ puts("222");
+ if (buf[1] == '\x44') {
+
+ puts("null ptr deference");
+ *(char *)(0) = 1;
+
+ }
+
+ break;
+ case 0xff:
+ if (buf[2] == '\xff') {
+
+ if (buf[1] == '\x44') {
+
+ puts("crash....");
+ *(char *)(0xdeadbeef) = 1;
+
+ }
+
+ }
+
+ break;
+ default: puts("default action"); break;
+
+ }
+
+ return 1;
+
+}
+```
+
+This could be build to `libdemo.so`.
+
+Then we should load the library in template.cpp and find the `target` function address.
+```c
+ void *handle = dlopen(lib_path, RTLD_LAZY);
+ ..........................................
+ ..........................................
+ ..........................................
+ p_target_func = (target_func)dlsym(handle, "target_func");
+```
+
+then we read the data from file and call the function in `fuzz_func`
+
+```c
+QBDI_NOINLINE int fuzz_func() {
+
+ if (afl_setup()) { afl_forkserver(); }
+
+ /* Read the input from file */
+ unsigned long len = 0;
+ char * data = read_file(input_pathname, &len);
+
+ /* Call the target function with the input data */
+ p_target_func(data, len);
+ return 1;
+
+}
+```
+
+Just compile it
+```
+./build.sh x86_64
+```
+
+Then push the `afl-fuzz`, `loader`, `libdemo.so`, the `libQBDI.so` from the QBDI SDK and the `libc++_shared.so` from android-standalone-toolchain to android device
+
+```
+adb push afl-fuzz /data/local/tmp
+adb push libdemo.so /data/local/tmp
+adb push loader /data/local/tmp
+adb push android-qbdi-sdk-x86_64/usr/local/lib/libQBDI.so /data/local/tmp
+adb push ../../android-standalone-toolchain-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so
+/data/local/tmp
+```
+
+In android adb shell, run the loader to test if it runs
+```
+cd /data/local/tmp
+export LD_LIBRARY_PATH=/data/local/tmp
+mkdir in
+echo 0000 > in/1
+./loader libdemo.so in/1
+p_target_func:0x716d96a98600
+ offset:0x600
+ offset:0x580
+buffer:0x716d96609050, size:0x5
+ offset:0x628
+ offset:0x646
+ offset:0x64b
+ offset:0x65c
+ offset:0x6df
+ offset:0x590
+default action
+ offset:0x6eb
+```
+
+Now run `afl-fuzz` to fuzz the demo library
+
+```
+./afl-fuzz -i in -o out -- ./loader /data/local/tmp/libdemo.so @@
+```
+
+![screen1](assets/screen1.png)
+
diff --git a/utils/qbdi_mode/assets/screen1.png b/utils/qbdi_mode/assets/screen1.png
new file mode 100644
index 00000000..3cf1cb76
--- /dev/null
+++ b/utils/qbdi_mode/assets/screen1.png
Binary files differ
diff --git a/utils/qbdi_mode/build.sh b/utils/qbdi_mode/build.sh
new file mode 100755
index 00000000..2527bd26
--- /dev/null
+++ b/utils/qbdi_mode/build.sh
@@ -0,0 +1,57 @@
+
+if [ -z ${STANDALONE_TOOLCHAIN_PATH} ]; then
+ echo "please set the android-standalone-toolchain path in STANDALONE_TOOLCHAIN_PATH environmental variable"
+ echo "for example: "
+ echo " export STANDALONE_TOOLCHAIN_PATH=/home/android-standalone-toolchain-21/"
+ exit
+fi
+
+if [ -z ${QBDI_SDK_PATH} ]; then
+ echo "please set the qbdi sdk path in QBDI_SDK_PATH environmental variable"
+ echo "for example: "
+ echo " export QBDI_SDK_PATH=/home/QBDI-Android/"
+ exit
+fi
+
+
+
+if [ "$1" = "x86" ]; then
+ echo "build x86 qbdi"
+ compiler_prefix="${STANDALONE_TOOLCHAIN_PATH}/bin/"
+ if [ -z ${CC} ]; then
+ export CC=i686-linux-android-gcc
+ fi
+ if [ -z ${CXX} ]; then
+ export CXX=i686-linux-android-g++
+ fi
+elif [ "$1" = "x86_64" ]; then
+ echo "build x86_64 qbdi"
+ compiler_prefix="${STANDALONE_TOOLCHAIN_PATH}/bin/"
+ if [ -z ${CC} ]; then
+ export CC=x86_64-linux-android-gcc
+ fi
+ if [ -z ${CXX} ]; then
+ export CXX=x86_64-linux-android-g++
+ fi
+else
+ echo "usage: ./build.sh arch[x86, x86_64]"
+ exit
+fi
+
+
+CFLAGS="-I${QBDI_SDK_PATH}/usr/local/include/ -L${QBDI_SDK_PATH}/usr/local/lib/"
+
+echo "[+] Building the QBDI template"
+# build the qbdi template
+${compiler_prefix}${CXX} -o loader template.cpp -lQBDI -ldl -w -g ${CFLAGS}
+
+echo "[+] Building the demo library"
+# build the demo share library
+${compiler_prefix}${CC} -shared -o libdemo.so demo-so.c -w -g
+
+echo "[+] Building afl-fuzz for Android"
+# build afl-fuzz
+cd ../..
+${compiler_prefix}${CC} -DANDROID_DISABLE_FANCY=1 -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ -DAFL_PATH=\"/usr/local/lib/afl\" -DBIN_PATH=\"/usr/local/bin\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -Wno-unused-function src/afl-fuzz-*.c src/afl-fuzz.c src/afl-common.c src/afl-sharedmem.c src/afl-forkserver.c -o utils/qbdi_mode/afl-fuzz -ldl -w
+
+echo "[+] All done. Enjoy!"
diff --git a/utils/qbdi_mode/demo-so.c b/utils/qbdi_mode/demo-so.c
new file mode 100755
index 00000000..dd367036
--- /dev/null
+++ b/utils/qbdi_mode/demo-so.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+
+// gcc -shared -o libdemo.so demo-so.c -w
+int target_func(char *buf, int size) {
+
+ printf("buffer:%p, size:%p\n", buf, size);
+ switch (buf[0]) {
+
+ case 1:
+ puts("222");
+ if (buf[1] == '\x44') {
+
+ puts("null ptr deference");
+ *(char *)(0) = 1;
+
+ }
+
+ break;
+ case 0xff:
+ if (buf[2] == '\xff') {
+
+ if (buf[1] == '\x44') {
+
+ puts("crash....");
+ *(char *)(0xdeadbeef) = 1;
+
+ }
+
+ }
+
+ break;
+ default:
+ puts("default action");
+ break;
+
+ }
+
+ return 1;
+
+}
+
diff --git a/utils/qbdi_mode/template.cpp b/utils/qbdi_mode/template.cpp
new file mode 100755
index 00000000..b2066cc8
--- /dev/null
+++ b/utils/qbdi_mode/template.cpp
@@ -0,0 +1,251 @@
+#include <iostream>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+
+#ifdef __ANDROID__
+ #include "../include/android-ashmem.h"
+#endif
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include "../config.h"
+
+#include <QBDI.h>
+
+/* NeverZero */
+
+#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO)
+ #define INC_AFL_AREA(loc) \
+ asm volatile( \
+ "incb (%0, %1, 1)\n" \
+ "adcb $0, (%0, %1, 1)\n" \
+ : /* no out */ \
+ : "r"(afl_area_ptr), "r"(loc) \
+ : "memory", "eax")
+#else
+ #define INC_AFL_AREA(loc) afl_area_ptr[loc]++
+#endif
+
+using namespace QBDI;
+
+typedef int (*target_func)(char *buf, int size);
+
+static const size_t STACK_SIZE = 0x100000; // 1MB
+static const QBDI::rword FAKE_RET_ADDR = 0x40000;
+target_func p_target_func = NULL;
+rword module_base = 0;
+rword module_end = 0;
+static unsigned char
+ dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */
+unsigned char *afl_area_ptr = NULL; /* Exported for afl_gen_trace */
+
+unsigned long afl_prev_loc = 0;
+
+char input_pathname[PATH_MAX];
+
+/* Set up SHM region and initialize other stuff. */
+
+int afl_setup(void) {
+
+ char *id_str = getenv(SHM_ENV_VAR);
+ int shm_id;
+ if (id_str) {
+
+ shm_id = atoi(id_str);
+ afl_area_ptr = (unsigned char *)shmat(shm_id, NULL, 0);
+ if (afl_area_ptr == (void *)-1) return 0;
+ memset(afl_area_ptr, 0, MAP_SIZE);
+
+ }
+
+ return 1;
+
+}
+
+/* Fork server logic, invoked once we hit _start. */
+static void afl_forkserver() {
+
+ static unsigned char tmp[4];
+ pid_t child_pid;
+
+ if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
+
+ while (1) {
+
+ int status;
+ u32 was_killed;
+ // wait for afl-fuzz
+ if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(2);
+
+ child_pid = fork();
+ if (child_pid < 0) exit(4);
+
+ if (!child_pid) {
+
+ // child return to execute code
+ close(FORKSRV_FD);
+ close(FORKSRV_FD + 1);
+ return;
+
+ }
+
+ // write child pid to afl-fuzz
+ if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5);
+
+ // wait child stop
+ if (waitpid(child_pid, &status, 0) < 0) exit(6);
+
+ // send child stop status to afl-fuzz
+ if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7);
+
+ }
+
+}
+
+void afl_maybe_log(unsigned long cur_loc) {
+
+ if (afl_area_ptr == NULL) { return; }
+ unsigned long afl_idx = cur_loc ^ afl_prev_loc;
+ afl_idx &= MAP_SIZE - 1;
+ INC_AFL_AREA(afl_idx);
+ afl_prev_loc = cur_loc >> 1;
+
+}
+
+char *read_file(char *path, unsigned long *length) {
+
+ unsigned long len;
+ char * buf;
+
+ FILE *fp = fopen(path, "rb");
+ fseek(fp, 0, SEEK_END);
+ len = ftell(fp);
+ buf = (char *)malloc(len);
+ rewind(fp);
+ fread(buf, 1, len, fp);
+ fclose(fp);
+ *length = len;
+ return buf;
+
+}
+
+QBDI_NOINLINE int fuzz_func() {
+
+ if (afl_setup()) { afl_forkserver(); }
+
+ unsigned long len = 0;
+ char * data = read_file(input_pathname, &len);
+
+ // printf("In fuzz_func\n");
+ p_target_func(data, len);
+ return 1;
+
+}
+
+static QBDI::VMAction bbcallback(QBDI::VMInstanceRef vm,
+ const QBDI::VMState *state,
+ QBDI::GPRState * gprState,
+ QBDI::FPRState *fprState, void *data) {
+
+ // errno = SAVED_ERRNO;
+
+#ifdef __x86_64__
+ unsigned long pc = gprState->rip;
+#elif defined(i386)
+ unsigned long pc = gprState->eip;
+#elif defined(__arm__)
+ unsigned long pc = gprState->pc;
+#endif
+
+ // just log the module path
+ if (pc >= module_base && pc <= module_end) {
+
+ unsigned long offset = pc - module_base;
+ printf("\toffset:%p\n", offset);
+ afl_maybe_log(offset);
+
+ }
+
+ return QBDI::VMAction::CONTINUE;
+
+}
+
+int main(int argc, char **argv) {
+
+ if (argc < 3) {
+
+ puts("usage: ./loader library_path input_file_path");
+ exit(0);
+
+ }
+
+ const char *lib_path;
+ lib_path = argv[1];
+ strcpy(input_pathname, argv[2]);
+ void *handle = dlopen(lib_path, RTLD_LAZY);
+
+ if (handle == nullptr) {
+
+ perror("Cannot load library");
+ exit(EXIT_FAILURE);
+
+ }
+
+ const char *lib_name = lib_path;
+ if (strrchr(lib_name, '/') != nullptr) lib_name = strrchr(lib_name, '/') + 1;
+
+ // printf("library name:%s\n", lib_name);
+ // load library module address for log path
+ for (MemoryMap &map : getCurrentProcessMaps()) {
+
+ // printf("module:%s\n", map.name.c_str());
+ if ((map.permission & PF_EXEC) &&
+ strstr(map.name.c_str(), lib_name) != NULL) {
+
+ module_base = map.range.start;
+ module_end = map.range.end;
+
+ }
+
+ }
+
+ if (module_base == 0) {
+
+ std::cerr << "Fail to find base address" << std::endl;
+ return -1;
+
+ }
+
+ // printf("module base:%p, module end:%p\n", module_base, module_end);
+ p_target_func = (target_func)dlsym(handle, "target_func");
+ // p_target_func = (target_func)(module_base + 0x61a);
+ printf("p_target_func:%p\n", p_target_func);
+
+ VM vm;
+ uint8_t *fakestack = nullptr;
+
+ GPRState *state = vm.getGPRState();
+ allocateVirtualStack(state, STACK_SIZE, &fakestack);
+ vm.addInstrumentedModuleFromAddr(module_base);
+ vm.addInstrumentedModuleFromAddr((rword)&main);
+
+ vm.addVMEventCB(BASIC_BLOCK_ENTRY, bbcallback, nullptr);
+
+ // QBDI::simulateCall(state, FAKE_RET_ADDR);
+ // vm.run((rword)&fuzz_func, (rword)FAKE_RET_ADDR);
+
+ rword ret;
+ vm.call(&ret, (rword)&fuzz_func, {});
+
+ return 0;
+
+}
+