mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-08 08:11:34 +00:00
afl-untracer completed
This commit is contained in:
parent
a37eca9df5
commit
efa9df24c2
@ -31,6 +31,8 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||
- added examples/afl_network_proxy which allows to fuzz a target over the
|
||||
network (not fuzzing tcp/ip services but running afl-fuzz on one system
|
||||
and the target being on an embedded device)
|
||||
- added examples/afl_untracer which does a binary-only fuzzing with the
|
||||
modifications done in memory
|
||||
- added examples/afl_proxy which can be easily used to fuzz and instrument
|
||||
non-standard things
|
||||
- all:
|
||||
|
@ -20,6 +20,7 @@ e.g.:
|
||||
```
|
||||
$ afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@
|
||||
```
|
||||
|
||||
### on the fuzzing master
|
||||
|
||||
Just run afl-fuzz with your normal options, however the target should be
|
||||
@ -42,3 +43,13 @@ either. Note that also the outgoing interface can be specified with a '%' for
|
||||
## how to compile and install
|
||||
|
||||
`make && sudo make install`
|
||||
|
||||
## Future
|
||||
|
||||
It would be much faster and more effective if `afl-network-server` does not
|
||||
send the map data back (64kb or more) but the checksum that `afl-fuzz` would
|
||||
generate. This change however would make it incompatible with existing
|
||||
afl spinoffs.
|
||||
|
||||
But in the future this will be implemented and supported as a compile option.
|
||||
|
||||
|
@ -175,7 +175,7 @@ static void __afl_start_forkserver(void) {
|
||||
|
||||
static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
|
||||
s32 status, res = 0xffffff;
|
||||
s32 status, res = 0x0fffffff; // res is a dummy pid
|
||||
|
||||
/* Wait for parent by reading from the pipe. Abort if read fails. */
|
||||
if (read(FORKSRV_FD, &status, 4) != 4) return 0;
|
||||
@ -193,9 +193,7 @@ static u32 __afl_next_testcase(u8 *buf, u32 max_len) {
|
||||
|
||||
}
|
||||
|
||||
static void __afl_end_testcase(void) {
|
||||
|
||||
int status = 0xffffff;
|
||||
static void __afl_end_testcase(int status) {
|
||||
|
||||
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1);
|
||||
|
||||
@ -273,7 +271,7 @@ int main(int argc, char *argv[]) {
|
||||
__afl_map_shm();
|
||||
__afl_start_forkserver();
|
||||
|
||||
int i = 1, j;
|
||||
int i = 1, j, status, ret;
|
||||
// fprintf(stderr, "Waiting for first testcase\n");
|
||||
while ((len = __afl_next_testcase(buf, max_len)) > 0) {
|
||||
|
||||
@ -281,17 +279,25 @@ int main(int argc, char *argv[]) {
|
||||
if (send(s, &len, 4, 0) != 4) PFATAL("sending size data %d failed", len);
|
||||
if (send(s, buf, len, 0) != len) PFATAL("sending test data failed");
|
||||
|
||||
int received = 0, ret;
|
||||
int received = 0;
|
||||
while (received < 4 &&
|
||||
(ret = recv(s, &status + received, 4 - received, 0)) > 0)
|
||||
received += ret;
|
||||
if (received != 4)
|
||||
FATAL("did not receive waitpid data (%d, %d)", received, ret);
|
||||
// fprintf(stderr, "Received status\n");
|
||||
|
||||
int received = 0;
|
||||
while (received < __afl_map_size &&
|
||||
(ret = recv(s, __afl_area_ptr + received, __afl_map_size - received,
|
||||
0)) > 0)
|
||||
received += ret;
|
||||
if (received != __afl_map_size)
|
||||
FATAL("did not receive valid data (%d, %d)", received, ret);
|
||||
FATAL("did not receive coverage data (%d, %d)", received, ret);
|
||||
// fprintf(stderr, "Received coverage\n");
|
||||
|
||||
/* report the test case is done and wait for the next */
|
||||
__afl_end_testcase();
|
||||
__afl_end_testcase(status);
|
||||
// fprintf(stderr, "Waiting for next testcase %d\n", ++i);
|
||||
|
||||
}
|
||||
@ -299,4 +305,3 @@ int main(int argc, char *argv[]) {
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
@ -579,6 +579,8 @@ int main(int argc, char **argv_orig, char **envp) {
|
||||
// fprintf(stderr, "received %u\n", in_len);
|
||||
run_target(fsrv, use_argv, in_data, in_len, 1);
|
||||
|
||||
if (send(s, fsrv->child_status, 4, 0) != 4)
|
||||
FATAL("could not send waitpid data");
|
||||
if (send(s, fsrv->trace_bits, fsrv->map_size, 0) != fsrv->map_size)
|
||||
FATAL("could not send coverage data");
|
||||
// fprintf(stderr, "sent result\n");
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
|
23
examples/afl_untracer/patches.txt
Normal file
23
examples/afl_untracer/patches.txt
Normal 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
|
@ -43,6 +43,7 @@ typedef struct afl_forkserver {
|
||||
|
||||
s32 fsrv_pid, /* PID of the fork server */
|
||||
child_pid, /* PID of the fuzzed program */
|
||||
child_status, /* waitpid result for the child */
|
||||
out_dir_fd; /* FD of the lock file */
|
||||
|
||||
s32 out_fd, /* Persistent fd for fsrv->out_file */
|
||||
|
@ -790,8 +790,6 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
s32 res;
|
||||
u32 exec_ms;
|
||||
|
||||
int status = 0;
|
||||
|
||||
/* After this memset, fsrv->trace_bits[] are effectively volatile, so we
|
||||
must prevent any earlier operations from venturing into that
|
||||
territory. */
|
||||
@ -821,7 +819,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
if (fsrv->child_pid <= 0) { FATAL("Fork server is misbehaving (OOM?)"); }
|
||||
|
||||
exec_ms = read_timed(fsrv->fsrv_st_fd, &status, 4, timeout, stop_soon_p);
|
||||
exec_ms = read_timed(fsrv->fsrv_st_fd, &fsrv->child_status, 4, timeout, stop_soon_p);
|
||||
|
||||
if (exec_ms > timeout) {
|
||||
|
||||
@ -830,7 +828,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
kill(fsrv->child_pid, SIGKILL);
|
||||
fsrv->last_run_timed_out = 1;
|
||||
if (read(fsrv->fsrv_st_fd, &status, 4) < 4) { exec_ms = 0; }
|
||||
if (read(fsrv->fsrv_st_fd, &fsrv->child_status, 4) < 4) { exec_ms = 0; }
|
||||
|
||||
}
|
||||
|
||||
@ -862,7 +860,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(status)) { fsrv->child_pid = 0; }
|
||||
if (!WIFSTOPPED(fsrv->child_status)) { fsrv->child_pid = 0; }
|
||||
|
||||
fsrv->total_execs++;
|
||||
|
||||
@ -874,9 +872,9 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
|
||||
/* Report outcome to caller. */
|
||||
|
||||
if (WIFSIGNALED(status) && !*stop_soon_p) {
|
||||
if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) {
|
||||
|
||||
fsrv->last_kill_signal = WTERMSIG(status);
|
||||
fsrv->last_kill_signal = WTERMSIG(fsrv->child_status);
|
||||
|
||||
if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL) {
|
||||
|
||||
@ -891,7 +889,7 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout,
|
||||
/* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and
|
||||
must use a special exit code. */
|
||||
|
||||
if (fsrv->uses_asan && WEXITSTATUS(status) == MSAN_ERROR) {
|
||||
if (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) {
|
||||
|
||||
fsrv->last_kill_signal = 0;
|
||||
return FSRV_RUN_CRASH;
|
||||
|
Loading…
x
Reference in New Issue
Block a user