afl-untracer completed

This commit is contained in:
van Hauser
2020-04-30 17:59:59 +02:00
parent a37eca9df5
commit efa9df24c2
10 changed files with 106 additions and 75 deletions

View File

@ -7,4 +7,4 @@ libtestinstr.so: libtestinstr.c
$(CC) -fPIC -o libtestinstr.so -shared libtestinstr.c
clean:
rm -f afl-untracer *~ core
rm -f afl-untracer libtestinstr.so *~ core

View File

@ -3,9 +3,14 @@
afl-untracer is an example skeleton file which can easily be used to fuzz
a closed source library.
It is faster and requires less memory than qemu_mode however it is way
It requires less memory than qemu_mode however it is way
more course grained and does not provide interesting features like compcov
or cmplog.
Read and modify afl-untracer.c then `make` and use it as the afl-fuzz target
(or even remote via afl-network-proxy).
This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL)
and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz).
This implementation is slower because the traps are not patched out with each
run, but on the other hand gives much better coverage information.

View File

@ -76,16 +76,9 @@ static u32 use_stdin = 1;
/* This is were the testcase data is written into */
static u8 buf[10000]; // this is the maximum size for a test case! set it!
/* if you want to use fork() then uncomment the following line. Otherwise
threads are used. The differences:
fork() pthread()
speed slow fast
coverage detailed minimal
memory leaks no problem an issue */
//#define USE_FORK
/* If you want to have debug output set this to 1 */
static u32 debug = 1;
/* If you want to have debug output set this to 1, can also be set with
AFL_DEBUG */
static u32 debug = 0;
// END STEP 1
@ -130,7 +123,7 @@ void read_library_information() {
if (debug) fprintf(stderr, "Library list:\n");
while (fgets(buf, sizeof(buf), f)) {
if (strstr(buf, " r-xp ")) {
if (strstr(buf, " r-x")) {
if (liblist_cnt >= MAX_LIB_COUNT) {
@ -159,9 +152,9 @@ void read_library_information() {
liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16);
liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16);
if (debug)
fprintf(stderr, "%s:%x (%x-%x)\n", liblist[liblist_cnt].name,
fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name,
liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start,
liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end);
liblist[liblist_cnt].addr_start, liblist[liblist_cnt].addr_end - 1);
liblist_cnt++;
}
@ -367,17 +360,13 @@ static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
// fprintf(stderr, "write1 %d\n", do_exit);
if (!do_exit)
__afl_area_ptr[0] = 1; // otherwise afl-fuzz will exit with NOINST
return status;
}
static void __afl_end_testcase(void) {
static void __afl_end_testcase(int status) {
s32 res = 0xffffff;
if (write(FORKSRV_FD + 1, &res, 4) != 4) do_exit = 1;
// if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1;
if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1;
// fprintf(stderr, "write2 %d\n", do_exit);
if (do_exit) exit(0);
@ -455,6 +444,19 @@ void setup_trap_instrumentation() {
// It's an offset, parse it and do the patching.
unsigned long offset = strtoul(line, NULL, 16);
// I dont know what it is. /proc/<pid>/maps shows the right start address
// and the offsets generated by the python scripts are fine as well.
// And loading the library into gdb also shows the offsets generated
// by the script are correct. However when loaded via dlopen the first
// 0x1000 are skipped ...
#if defined(__linux__)
if (offset >= 0x1000)
offset -= 0x1000;
else
fprintf(stderr, "Warning: offset is < 0x1000: %x\n", offset);
#endif
if (offset > lib_size)
FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large",
offset, lib_size);
@ -482,11 +484,10 @@ void setup_trap_instrumentation() {
// this will be ARM and AARCH64
// for ARM we will need to identify if the code is in thumb or ARM
#error "non x86_64 not supported yet"
//__arm__
//__arm__:
// linux thumb: 0xde01
// linux thumb2: 0xf7f0a000
// linux arm: 0xe7f001f0
//__aarch64__
//__aarch64__:
// linux aarch64: 0xd4200000
#endif
@ -510,6 +511,8 @@ void setup_trap_instrumentation() {
}
/* the signal handler for the traps / debugging interrupts
No debug output here because this would cost speed */
static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
uint64_t addr;
@ -525,31 +528,25 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
#error "Unsupported platform"
#endif
if (debug)
fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr,
si->si_addr);
//fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, si->si_addr);
// If the trap didn't come from our instrumentation, then we probably will
// just segfault here
uint8_t *faultaddr;
if (si->si_addr)
if (unlikely(si->si_addr))
faultaddr = (u8 *)si->si_addr - 1;
else
faultaddr = (u8 *)addr;
// u8 *loc = SHADOW(faultaddr);
if (debug)
fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
//if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr));
uint32_t shadow = *SHADOW(faultaddr);
uint8_t orig_byte = shadow & 0xff;
uint32_t index = shadow >> 8;
if (debug)
fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", shadow,
orig_byte, index);
//if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", shadow, orig_byte, index);
// Index zero is invalid so that it is still possible to catch actual trap
// instructions in instrumented libraries.
if (index == 0) abort();
if (unlikely(index == 0)) abort();
// Restore original instruction
*faultaddr = orig_byte;
@ -561,6 +558,7 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) {
/* here you need to specify the parameter for the target function */
static void *(*o_function)(u8 *buf, int len);
/* the MAIN function */
int main(int argc, char *argv[]) {
pid = getpid();
@ -579,11 +577,11 @@ int main(int argc, char *argv[]) {
// inclusive of the cleanup functions
// NOTE: above the main() you have to define the functions!
/* setup the target */
void *dl = dlopen("./libtestinstr.so", RTLD_LAZY);
if (!dl) FATAL("could not find target library");
o_function = dlsym(dl, "testinstr");
if (!o_function) FATAL("could not resolve target function from library");
if (debug) fprintf(stderr, "Function address: %p\n", o_function);
// END STEP 2
@ -595,42 +593,33 @@ int main(int argc, char *argv[]) {
while (1) {
#ifdef USE_FORK
if ((pid = fork()) == -1) PFATAL("fork failed");
if (pid) {
u32 status;
if (waitpid(pid, &status, 0) < 0) exit(1);
/* report the test case is done and wait for the next */
__afl_end_testcase(status);
} else {
#endif
pid = getpid();
while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) {
#ifdef USE_FORK
// in this function the fuzz magic happens, this is STEP 3
fuzz();
#else
if (pthread_create(&__afl_thread, NULL, (void *)fuzz, NULL) != 0)
PFATAL("cannot create thread");
pthread_join(__afl_thread, NULL);
#endif
/* report the test case is done and wait for the next */
__afl_end_testcase();
#ifdef USE_FORK
exit(0);
#endif
// we can use _exit which is faster because our target library
// was loaded via dlopen and there cannot have deconstructors
// registered.
_exit(0);
}
#ifdef USE_FORK
}
#endif
}
return 0;
@ -651,9 +640,4 @@ static void fuzz() {
// END STEP 3
#ifndef USE_FORK
pthread_exit(NULL);
#endif
}

View File

@ -0,0 +1,23 @@
libtestinstr.so:0x2000L
0x1050L
0x1063L
0x106fL
0x1078L
0x1080L
0x10a4L
0x10b0L
0x10b8L
0x10c0L
0x10c9L
0x10d7L
0x10e3L
0x10f8L
0x1100L
0x1105L
0x111aL
0x1135L
0x1143L
0x114eL
0x115cL
0x116aL
0x116bL