afl-untracer - next step

This commit is contained in:
van Hauser 2020-04-30 16:27:31 +02:00
parent e68d2345d5
commit a37eca9df5
4 changed files with 199 additions and 83 deletions

View File

@ -1,7 +1,10 @@
all: afl-untracer
all: afl-untracer libtestinstr.so
afl-untracer: afl-untracer.c
$(CC) -I../../include -g -o afl-untracer afl-untracer.c -ldl
$(CC) -I../../include -g -o afl-untracer afl-untracer.c -ldl -pthread
libtestinstr.so: libtestinstr.c
$(CC) -fPIC -o libtestinstr.so -shared libtestinstr.c
clean:
rm -f afl-untracer *~ core

View File

@ -50,6 +50,7 @@
#include <errno.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/shm.h>
@ -67,14 +68,26 @@
#define MEMORY_MAP_DECREMENT 0x200000000000
#define MAX_LIB_COUNT 128
#ifdef __ANDROID__
u32 __afl_map_size = MAP_SIZE;
#else
__thread u32 __afl_map_size = MAP_SIZE;
#endif
// STEP 1:
u8 __afl_dummy[MAP_SIZE];
u8 *__afl_area_ptr = __afl_dummy;
/* use stdin (1) or a file on the commandline (0) */
static u32 use_stdin = 1;
/* This is were the testcase data is written into */
static u8 buf[10000]; // this is the maximum size for a test case! set it!
/* if you want to use fork() then uncomment the following line. Otherwise
threads are used. The differences:
fork() pthread()
speed slow fast
coverage detailed minimal
memory leaks no problem an issue */
//#define USE_FORK
/* If you want to have debug output set this to 1 */
static u32 debug = 1;
// END STEP 1
typedef struct library_list {
@ -83,11 +96,26 @@ typedef struct library_list {
} library_list_t;
library_list_t liblist[MAX_LIB_COUNT];
u32 liblist_cnt;
u32 use_stdin = 1;
#ifdef __ANDROID__
u32 __afl_map_size = MAP_SIZE;
u32 do_exit;
#else
__thread u32 __afl_map_size = MAP_SIZE;
__thread u32 do_exit;
#endif
static pid_t pid = 65537;
static pthread_t __afl_thread;
static u8 __afl_dummy[MAP_SIZE];
static u8 * __afl_area_ptr = __afl_dummy;
static u8 * inputfile; // this will point to argv[1]
static u32 len;
static library_list_t liblist[MAX_LIB_COUNT];
static u32 liblist_cnt;
static void sigtrap_handler(int signum, siginfo_t *si, void *context);
static void fuzz();
/* read the library information */
void read_library_information() {
@ -99,7 +127,7 @@ void read_library_information() {
if ((f = fopen("/proc/self/maps", "r")) == NULL)
FATAL("cannot open /proc/self/maps");
fprintf(stderr, "Library list:\n");
if (debug) fprintf(stderr, "Library list:\n");
while (fgets(buf, sizeof(buf), f)) {
if (strstr(buf, " r-xp ")) {
@ -130,9 +158,10 @@ void read_library_information() {
liblist[liblist_cnt].name = strdup(n);
liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16);
liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16);
fprintf(stderr, "%s:%x (%x-%x)\n", liblist[liblist_cnt].name,
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end);
if (debug)
fprintf(stderr, "%s:%x (%x-%x)\n", liblist[liblist_cnt].name,
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end);
liblist_cnt++;
}
@ -141,7 +170,7 @@ void read_library_information() {
}
fprintf(stderr, "\n");
if (debug) fprintf(stderr, "\n");
#endif
@ -300,7 +329,6 @@ static void __afl_map_shm(void) {
}
/* Fork server logic. */
static void __afl_start_forkserver(void) {
u8 tmp[4] = {0, 0, 0, 0};
@ -312,36 +340,46 @@ static void __afl_start_forkserver(void) {
memcpy(tmp, &status, 4);
/* Phone home and tell the parent that we're OK. */
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1;
// fprintf(stderr, "write0 %d\n", do_exit);
}
static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
s32 status, res = 0xffffff;
s32 status;
/* Wait for parent by reading from the pipe. Abort if read fails. */
if (read(FORKSRV_FD, &status, 4) != 4) return 0;
if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1;
// fprintf(stderr, "read %d\n", do_exit);
/* we have a testcase - read it if we read from stdin */
if (use_stdin) status = read(0, buf, max_len);
if (use_stdin) {
if ((status = read(0, buf, max_len)) <= 0) exit(-1);
} else
status = 1;
// fprintf(stderr, "stdin: %d %d\n", use_stdin, status);
/* report that we are starting the target */
if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0;
if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
// fprintf(stderr, "write1 %d\n", do_exit);
if (status < 1)
return 0;
else
return status;
if (!do_exit)
__afl_area_ptr[0] = 1; // otherwise afl-fuzz will exit with NOINST
return status;
}
static void __afl_end_testcase(void) {
int status = 0xffffff;
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
s32 res = 0xffffff;
if (write(FORKSRV_FD + 1, &res, 4) != 4) do_exit = 1;
// if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
// fprintf(stderr, "write2 %d\n", do_exit);
if (do_exit) exit(0);
}
@ -365,7 +403,7 @@ void setup_trap_instrumentation() {
if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename);
// Index into the coverage bitmap for the current trap instruction.
int bitmap_index = -1;
int bitmap_index = 0;
while ((nread = getline(&line, &len, patches)) != -1) {
@ -403,8 +441,9 @@ void setup_trap_instrumentation() {
void *shadow_addr = SHADOW(lib_addr + i);
void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0);
fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow,
shadow + lib_size - 1, lib_addr);
if (debug)
fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow,
shadow + lib_size - 1, lib_addr);
if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory");
}
@ -420,38 +459,39 @@ void setup_trap_instrumentation() {
FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large",
offset, lib_size);
bitmap_index++;
if (bitmap_index >= __afl_map_size)
FATAL("Too many basic blocks to instrument");
uint32_t *shadow = SHADOW(lib_addr + offset);
if (*shadow != 0) FATAL("Potentially duplicate patch entry: 0x%lx", offset);
if (*shadow != 0) FATAL("Duplicate patch entry: 0x%lx", offset);
// Make lookup entry in shadow memory.
// Make lookup entry in shadow memory.
#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__))
// this is for Intel x64
uint8_t orig_byte = lib_addr[offset];
*shadow = (bitmap_index << 8) | orig_byte;
lib_addr[offset] = 0xcc; // replace instruction with debug trap
fprintf(stderr,
"Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n",
lib_addr, offset, lib_addr + offset, orig_byte, shadow,
bitmap_index, *shadow);
if (debug)
fprintf(stderr,
"Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n",
lib_addr, offset, lib_addr + offset, orig_byte, shadow,
bitmap_index, *shadow);
#else
// this will be ARM and AARCH64
// for ARM we will need to identify if the code is in thumb or ARM
// this will be ARM and AARCH64
// for ARM we will need to identify if the code is in thumb or ARM
#error "non x86_64 not supported yet"
//__arm__
// linux thumb: 0xde01
// linux thumb2: 0xf7f0a000
// linux arm: 0xe7f001f0
//__aarch64__
// linux aarch64: 0xd4200000
//__arm__
// linux thumb: 0xde01
// linux thumb2: 0xf7f0a000
// linux arm: 0xe7f001f0
//__aarch64__
// linux aarch64: 0xd4200000
#endif
bitmap_index++;
}
free(line);
@ -464,7 +504,7 @@ void setup_trap_instrumentation() {
sigemptyset(&s.sa_mask);
sigaction(SIGTRAP, &s, 0);
fprintf(stderr, "Patched %u locations.\n", bitmap_index);
if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index);
__afl_map_size = bitmap_index;
if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3);
@ -485,8 +525,9 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
#error "Unsupported platform"
#endif
fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr,
si->si_addr);
if (debug)
fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr,
si->si_addr);
// If the trap didn't come from our instrumentation, then we probably will
// just segfault here
@ -496,13 +537,15 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
else
faultaddr = (u8 *)addr;
// u8 *loc = SHADOW(faultaddr);
fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
if (debug)
fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
uint32_t shadow = *SHADOW(faultaddr);
uint8_t orig_byte = shadow & 0xff;
uint32_t index = shadow >> 8;
fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", shadow,
orig_byte, index);
if (debug)
fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", shadow,
orig_byte, index);
// Index zero is invalid so that it is still possible to catch actual trap
// instructions in instrumented libraries.
@ -516,62 +559,77 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
}
/* here you need to specify the parameter for the target function */
static void *(*o_TIFFOpen)(char *filename, char *mode);
static void *(*o_TIFFClose)(u8 *file);
static void *(*o_function)(u8 *buf, int len);
int main(int argc, char *argv[]) {
// STEP 1: use stdin or filename via commandline and set the maximum
// size for a test case
pid = getpid();
if (getenv("AFL_DEBUG")) debug = 1;
/* by default we use stdin, but also a filename can be passed, in this
case the input is argv[1] and we have to disable stdin */
if (argc > 1) use_stdin = 0;
if (argc > 1) {
/* This is were the testcase data is written into */
u8 buf[10000]; // this is the maximum size for a test case! set it!
use_stdin = 0;
inputfile = argv[1];
// END STEP 1
}
// STEP 2: load the library you want to fuzz and lookup the functions,
// inclusive of the cleanup functions
// NOTE: above the main() you have to define the functions!
/* setup the target */
void *dl = dlopen("/prg/tests/libtiff.so", RTLD_LAZY);
void *dl = dlopen("./libtestinstr.so", RTLD_LAZY);
if (!dl) FATAL("could not find target library");
o_TIFFOpen = dlsym(dl, "TIFFOpen");
if (!o_TIFFOpen) FATAL("could not resolve target function from library");
o_TIFFClose = dlsym(dl, "TIFFClose");
if (!o_TIFFClose) FATAL("could not resolve target function from library");
o_function = dlsym(dl, "testinstr");
if (!o_function) FATAL("could not resolve target function from library");
// END STEP 2
/* setup instrumentation, shared memory and forkserver */
u32 len;
read_library_information();
setup_trap_instrumentation();
__afl_map_shm();
__afl_start_forkserver();
while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) {
while (1) {
// -> this still needs threading otherwise fuzzing stops when we crash
#ifdef USE_FORK
if ((pid = fork()) == -1) PFATAL("fork failed");
if (pid) {
// STEP 3: call the function to fuzz, also the functions you might
// need to call to prepare the function and - important! -
// to clean everything up
u32 status;
if (waitpid(pid, &status, 0) < 0) exit(1);
// in this example we use the input file, not stdin!
u8 *file (*o_function)(argv[1], "wl");
} else {
// we have to release memory
if (file) (void)(*o_TIFFClose)(file);
#endif
// END STEP 3
pid = getpid();
while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) {
/* report the test case is done and wait for the next */
__afl_end_testcase();
#ifdef USE_FORK
fuzz();
#else
if (pthread_create(&__afl_thread, NULL, (void *)fuzz, NULL) != 0)
PFATAL("cannot create thread");
pthread_join(__afl_thread, NULL);
#endif
/* report the test case is done and wait for the next */
__afl_end_testcase();
#ifdef USE_FORK
exit(0);
#endif
}
#ifdef USE_FORK
}
#endif
}
@ -579,3 +637,23 @@ int main(int argc, char *argv[]) {
}
static void fuzz() {
// STEP 3: call the function to fuzz, also the functions you might
// need to call to prepare the function and - important! -
// to clean everything up
// in this example we use the input file, not stdin!
(*o_function)(buf, len);
// normally you also need to cleanup
//(*o_LibFree)(foo);
// END STEP 3
#ifndef USE_FORK
pthread_exit(NULL);
#endif
}

View File

@ -0,0 +1,35 @@
/*
american fuzzy lop++ - a trivial program to test the build
--------------------------------------------------------
Originally written by Michal Zalewski
Copyright 2014 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
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void testinstr(char *buf, int len) {
if (len < 1)
return;
buf[len] = 0;
// we support three input cases
if (buf[0] == '0')
printf("Looks like a zero to me!\n");
else if (buf[0] == '1')
printf("Pretty sure that is a one!\n");
else
printf("Neither one or zero? How quaint!\n");
}

View File

@ -71,7 +71,7 @@ char *afl_environment_variables[] = {
"AFL_NGRAM_SIZE", "AFL_LLVM_NOT_ZERO", "AFL_LLVM_WHITELIST",
"AFL_NO_AFFINITY", "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID",
"AFL_NO_ARITH", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV",
"AFL_NO_UI", "AFL_NO_PYTHON",
"AFL_NO_UI", "AFL_NO_PYTHON", "AFL_UNTRACER_FILE",
"AFL_NO_X86", // not really an env but we dont want to warn on it
"AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_PATH", "AFL_PERFORMANCE_FILE",
//"AFL_PERSISTENT", // not implemented anymore, so warn additionally