mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-12 10:08:07 +00:00
Merge remote-tracking branch 'github/master' into custom_mutator
# Conflicts: # Makefile # afl-fuzz.c
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
|
*.o
|
||||||
|
*.so
|
||||||
.gitignore
|
.gitignore
|
||||||
TODO
|
|
||||||
afl-analyze
|
afl-analyze
|
||||||
afl-as
|
afl-as
|
||||||
afl-clang
|
afl-clang
|
||||||
@ -9,6 +10,8 @@ afl-clang-fast++
|
|||||||
afl-fuzz
|
afl-fuzz
|
||||||
afl-g++
|
afl-g++
|
||||||
afl-gcc
|
afl-gcc
|
||||||
|
afl-gcc-fast
|
||||||
|
afl-g++-fast
|
||||||
afl-gotcpu
|
afl-gotcpu
|
||||||
afl-qemu-trace
|
afl-qemu-trace
|
||||||
afl-showmap
|
afl-showmap
|
||||||
@ -16,3 +19,5 @@ afl-tmin
|
|||||||
as
|
as
|
||||||
qemu_mode/qemu-3.1.0
|
qemu_mode/qemu-3.1.0
|
||||||
qemu_mode/qemu-3.1.0.tar.xz
|
qemu_mode/qemu-3.1.0.tar.xz
|
||||||
|
unicorn_mode/unicorn
|
||||||
|
unicorn_mode/unicorn-*
|
||||||
|
11
.travis.yml
Normal file
11
.travis.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
language: c
|
||||||
|
|
||||||
|
env:
|
||||||
|
- AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make
|
||||||
|
- ./afl-gcc ./test-instr.c -o test-instr
|
||||||
|
- mkdir seeds; mkdir out
|
||||||
|
- echo "" > seeds/nil_seed
|
||||||
|
- timeout --preserve-status 5s ./afl-fuzz -i seeds -o out/ -- ./test-instr
|
99
Makefile
99
Makefile
@ -13,6 +13,9 @@
|
|||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# For Heiko:
|
||||||
|
#TEST_MMAP=1
|
||||||
|
|
||||||
PROGNAME = afl
|
PROGNAME = afl
|
||||||
VERSION = $(shell grep '^\#define VERSION ' config.h | cut -d '"' -f2)
|
VERSION = $(shell grep '^\#define VERSION ' config.h | cut -d '"' -f2)
|
||||||
|
|
||||||
@ -22,8 +25,6 @@ HELPER_PATH = $(PREFIX)/lib/afl
|
|||||||
DOC_PATH = $(PREFIX)/share/doc/afl
|
DOC_PATH = $(PREFIX)/share/doc/afl
|
||||||
MISC_PATH = $(PREFIX)/share/afl
|
MISC_PATH = $(PREFIX)/share/afl
|
||||||
|
|
||||||
CLANG_COMPILER_RT = "/home/user/code/compiler-rt/cmake-build-debug/lib/linux"
|
|
||||||
|
|
||||||
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
# PROGS intentionally omit afl-as, which gets installed elsewhere.
|
||||||
|
|
||||||
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
PROGS = afl-gcc afl-fuzz afl-showmap afl-tmin afl-gotcpu afl-analyze
|
||||||
@ -48,7 +49,8 @@ endif
|
|||||||
|
|
||||||
COMM_HDR = alloc-inl.h config.h debug.h types.h
|
COMM_HDR = alloc-inl.h config.h debug.h types.h
|
||||||
|
|
||||||
ifeq "$(shell echo '\#include <Python.h>XXXvoid main() {}' | sed 's/XXX/\n/g' | $(CC) -x c - -o .test -I$(PYTHON_INCLUDE) -lpython2.7 && echo 1 || echo 0 )" "1"
|
|
||||||
|
ifeq "$(shell echo '\#include <Python.h>@int main() {return 0; }' | tr @ '\n' | $(CC) -x c - -o .test -I$(PYTHON_INCLUDE) -lpython2.7 2>/dev/null && echo 1 || echo 0 )" "1"
|
||||||
PYTHON_OK=1
|
PYTHON_OK=1
|
||||||
PYFLAGS=-DUSE_PYTHON -I$(PYTHON_INCLUDE) -lpython2.7
|
PYFLAGS=-DUSE_PYTHON -I$(PYTHON_INCLUDE) -lpython2.7
|
||||||
else
|
else
|
||||||
@ -56,15 +58,31 @@ else
|
|||||||
PYFLAGS=
|
PYFLAGS=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: test_x86 test_python27 $(PROGS) afl-as test_build all_done
|
|
||||||
|
ifeq "$(shell echo '\#include <sys/ipc.h>@\#include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 )" "1"
|
||||||
|
SHMAT_OK=1
|
||||||
|
else
|
||||||
|
SHMAT_OK=0
|
||||||
|
CFLAGS+=-DUSEMMAP=1
|
||||||
|
LDFLAGS+=-Wno-deprecated-declarations -lrt
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq "$(TEST_MMAP)" "1"
|
||||||
|
SHMAT_OK=0
|
||||||
|
CFLAGS+=-DUSEMMAP=1
|
||||||
|
LDFLAGS+=-Wno-deprecated-declarations -lrt
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
all: test_x86 test_shm test_python27 ready $(PROGS) afl-as test_build all_done
|
||||||
|
|
||||||
|
|
||||||
ifndef AFL_NO_X86
|
ifndef AFL_NO_X86
|
||||||
|
|
||||||
test_x86:
|
test_x86:
|
||||||
@echo "[*] Checking for the ability to compile x86 code..."
|
@echo "[*] Checking for the ability to compile x86 code..."
|
||||||
@echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) -w -x c - -o .test || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 )
|
@echo 'main() { __asm__("xorb %al, %al"); }' | $(CC) -w -x c - -o .test1 || ( echo; echo "Oops, looks like your compiler can't generate x86 code."; echo; echo "Don't panic! You can use the LLVM or QEMU mode, but see docs/INSTALL first."; echo "(To ignore this error, set AFL_NO_X86=1 and try again.)"; echo; exit 1 )
|
||||||
@rm -f .test
|
@rm -f .test1
|
||||||
@echo "[+] Everything seems to be working, ready to compile."
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
@ -73,6 +91,21 @@ test_x86:
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
ifeq "$(SHMAT_OK)" "1"
|
||||||
|
|
||||||
|
test_shm:
|
||||||
|
@echo "[+] shmat seems to be working."
|
||||||
|
@rm -f .test2
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
test_shm:
|
||||||
|
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifeq "$(PYTHON_OK)" "1"
|
ifeq "$(PYTHON_OK)" "1"
|
||||||
|
|
||||||
test_python27:
|
test_python27:
|
||||||
@ -86,6 +119,10 @@ test_python27:
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
ready:
|
||||||
|
@echo "[+] Everything seems to be working, ready to compile."
|
||||||
|
|
||||||
afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86
|
afl-gcc: afl-gcc.c $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
||||||
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
|
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $$i; done
|
||||||
@ -94,30 +131,28 @@ afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86
|
|||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
||||||
ln -sf afl-as as
|
ln -sf afl-as as
|
||||||
|
|
||||||
afl-fuzz: afl-fuzz.c $(COMM_HDR) | test_x86
|
afl-common.o : afl-common.c
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) $(PYFLAGS)
|
$(CC) $(CFLAGS) -c afl-common.c
|
||||||
|
|
||||||
smart-rabbit: afl-fuzz.c $(COMM_HDR) | test_x86
|
sharedmem.o : sharedmem.c
|
||||||
$(CC) $(CFLAGS) afl-fuzz.c -o $@ $(LDFLAGS) -lstdc++ -lpthread -lm -lrt \
|
$(CC) $(CFLAGS) -c sharedmem.c
|
||||||
-Wl,--whole-archive -Wl,$(CLANG_COMPILER_RT)/libclang_rt.ubsan_standalone-x86_64.a -Wl,--no-whole-archive \
|
|
||||||
-Wl,--dynamic-list=$(CLANG_COMPILER_RT)/libclang_rt.ubsan_standalone-x86_64.a.syms \
|
|
||||||
-Wl,--whole-archive -Wl,$(CLANG_COMPILER_RT)/libclang_rt.ubsan_standalone_cxx-x86_64.a -Wl,--no-whole-archive \
|
|
||||||
-Wl,--dynamic-list=$(CLANG_COMPILER_RT)/libclang_rt.ubsan_standalone_cxx-x86_64.a.syms \
|
|
||||||
-Wl,--whole-archive -Wl,$(CLANG_COMPILER_RT)/libclang_rt.fuzzer_no_main-x86_64.a -Wl,--no-whole-archive \
|
|
||||||
-Wl,--dynamic-list=libclang_rt.fuzzer_no_main-x86_64.a.syms
|
|
||||||
|
|
||||||
afl-showmap: afl-showmap.c $(COMM_HDR) | test_x86
|
afl-fuzz: afl-fuzz.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS) $(PYFLAGS)
|
||||||
|
|
||||||
afl-tmin: afl-tmin.c $(COMM_HDR) | test_x86
|
afl-showmap: afl-showmap.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
afl-analyze: afl-analyze.c $(COMM_HDR) | test_x86
|
afl-tmin: afl-tmin.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
afl-analyze: afl-analyze.c afl-common.o sharedmem.o $(COMM_HDR) | test_x86
|
||||||
|
$(CC) $(CFLAGS) $@.c afl-common.o sharedmem.o -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86
|
afl-gotcpu: afl-gotcpu.c $(COMM_HDR) | test_x86
|
||||||
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
|
||||||
ifndef AFL_NO_X86
|
ifndef AFL_NO_X86
|
||||||
|
|
||||||
test_build: afl-gcc afl-as afl-showmap
|
test_build: afl-gcc afl-as afl-showmap
|
||||||
@ -136,17 +171,18 @@ test_build: afl-gcc afl-as afl-showmap
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
all_done: test_build
|
all_done: test_build
|
||||||
@if [ ! "`which clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc."; fi
|
@if [ ! "`which clang 2>/dev/null`" = "" ]; then echo "[+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc."; fi
|
||||||
@echo "[+] All done! Be sure to review README - it's pretty short and useful."
|
@echo "[+] All done! Be sure to review the README.md - it's pretty short and useful."
|
||||||
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
|
@if [ "`uname`" = "Darwin" ]; then printf "\nWARNING: Fuzzing on MacOS X is slow because of the unusually high overhead of\nfork() on this OS. Consider using Linux or *BSD. You can also use VirtualBox\n(virtualbox.org) to put AFL inside a Linux or *BSD VM.\n\n"; fi
|
||||||
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.txt for advice.\033[0m\n" 2>/dev/null
|
@! tty <&1 >/dev/null || printf "\033[0;30mNOTE: If you can read this, your terminal probably uses white background.\nThis will make the UI hard to read. See docs/status_screen.txt for advice.\033[0m\n" 2>/dev/null
|
||||||
|
|
||||||
.NOTPARALLEL: clean
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PROGS) smart-rabbit afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test test-instr .test-instr0 .test-instr1 qemu_mode/qemu-2.10.0.tar.bz2 afl-qemu-trace
|
rm -f $(PROGS) afl-as as afl-g++ afl-clang afl-clang++ *.o *~ a.out core core.[1-9][0-9]* *.stackdump test .test .test1 .test2 test-instr .test-instr0 .test-instr1 qemu_mode/qemu-3.1.0.tar.xz afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-gcc-rt.o afl-g++-fast
|
||||||
rm -rf out_dir qemu_mode/qemu-2.10.0
|
rm -rf out_dir qemu_mode/qemu-3.1.0
|
||||||
$(MAKE) -C llvm_mode clean
|
$(MAKE) -C llvm_mode clean
|
||||||
$(MAKE) -C libdislocator clean
|
$(MAKE) -C libdislocator clean
|
||||||
$(MAKE) -C libtokencap clean
|
$(MAKE) -C libtokencap clean
|
||||||
@ -157,8 +193,9 @@ install: all
|
|||||||
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
|
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
|
||||||
rm -f $${DESTDIR}$(BIN_PATH)/afl-as
|
rm -f $${DESTDIR}$(BIN_PATH)/afl-as
|
||||||
if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
|
if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
|
||||||
|
#if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||||
ifndef AFL_TRACE_PC
|
ifndef AFL_TRACE_PC
|
||||||
if [ -f afl-clang-fast -a -f afl-llvm-pass.so -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 afl-llvm-pass.so afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
if [ -f afl-clang-fast -a -f libLLVMInsTrim.so -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 libLLVMInsTrim.so afl-llvm-pass.so afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||||
else
|
else
|
||||||
if [ -f afl-clang-fast -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
if [ -f afl-clang-fast -a -f afl-llvm-rt.o ]; then set -e; install -m 755 afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi
|
||||||
endif
|
endif
|
||||||
@ -168,10 +205,12 @@ endif
|
|||||||
if [ -f split-compares-pass.so ]; then set -e; install -m 755 split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
if [ -f split-compares-pass.so ]; then set -e; install -m 755 split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
if [ -f split-switches-pass.so ]; then set -e; install -m 755 split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
if [ -f split-switches-pass.so ]; then set -e; install -m 755 split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi
|
||||||
|
|
||||||
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/$$i; done
|
set -e; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-g++
|
||||||
|
set -e; if [ -f afl-clang-fast ] ; then ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi
|
||||||
|
|
||||||
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
|
install -m 755 afl-as $${DESTDIR}$(HELPER_PATH)
|
||||||
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as
|
||||||
install -m 644 docs/README docs/ChangeLog docs/*.txt $${DESTDIR}$(DOC_PATH)
|
install -m 644 docs/README.md docs/ChangeLog docs/*.txt $${DESTDIR}$(DOC_PATH)
|
||||||
cp -r testcases/ $${DESTDIR}$(MISC_PATH)
|
cp -r testcases/ $${DESTDIR}$(MISC_PATH)
|
||||||
cp -r dictionaries/ $${DESTDIR}$(MISC_PATH)
|
cp -r dictionaries/ $${DESTDIR}$(MISC_PATH)
|
||||||
|
|
||||||
@ -182,7 +221,7 @@ publish: clean
|
|||||||
# tar -cvz -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz $(PROGNAME)-$(VERSION)
|
# tar -cvz -f ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz $(PROGNAME)-$(VERSION)
|
||||||
# chmod 644 ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz
|
# chmod 644 ~/www/afl/releases/$(PROGNAME)-$(VERSION).tgz
|
||||||
# ( cd ~/www/afl/releases/; ln -s -f $(PROGNAME)-$(VERSION).tgz $(PROGNAME)-latest.tgz )
|
# ( cd ~/www/afl/releases/; ln -s -f $(PROGNAME)-$(VERSION).tgz $(PROGNAME)-latest.tgz )
|
||||||
# cat docs/README >~/www/afl/README.txt
|
# cat docs/README.md >~/www/afl/README.txt
|
||||||
# cat docs/status_screen.txt >~/www/afl/status_screen.txt
|
# cat docs/status_screen.txt >~/www/afl/status_screen.txt
|
||||||
# cat docs/historical_notes.txt >~/www/afl/historical_notes.txt
|
# cat docs/historical_notes.txt >~/www/afl/historical_notes.txt
|
||||||
# cat docs/technical_details.txt >~/www/afl/technical_details.txt
|
# cat docs/technical_details.txt >~/www/afl/technical_details.txt
|
||||||
|
@ -1,45 +1,48 @@
|
|||||||
============================
|
# american fuzzy lop plus plus (afl++)
|
||||||
american fuzzy lop plus plus
|
|
||||||
============================
|
|
||||||
|
|
||||||
Written by Michal Zalewski <lcamtuf@google.com>
|
Originally developed by Michal "lcamtuf" Zalewski.
|
||||||
|
|
||||||
Repository: https://github.com/vanhauser-thc/AFLplusplus
|
Repository: [https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
||||||
|
|
||||||
afl++ is maintained by Marc Heuse <mh@mh-sec.de> and Heiko Eissfeldt
|
afl++ is maintained by Marc Heuse <mh@mh-sec.de>, Heiko Eissfeldt
|
||||||
<heiko.eissfeldt@hexco.de> as there have been no updates to afl since
|
<heiko.eissfeldt@hexco.de> and Andrea Fioraldi <andreafioraldi@gmail.com>.
|
||||||
November 2017.
|
|
||||||
|
|
||||||
This version has several bug fixes, new features and speed enhancements
|
## The enhancements compared to the original stock afl
|
||||||
based on community patches from https://github.com/vanhauser-thc/afl-patches
|
|
||||||
To see the list of which patches have been applied, see the PATCHES file.
|
|
||||||
|
|
||||||
Additionally AFLfast's power schedules by Marcel Boehme from
|
Many improvements were made over the official afl release - which did not
|
||||||
github.com/mboehme/aflfast have been incorporated.
|
get any improvements since November 2017.
|
||||||
|
|
||||||
Plus it was upgraded to qemu 3.1 from 2.1 with the work of
|
Among others afl++ has, e.g. more performant llvm_mode, supporting
|
||||||
https://github.com/andreafioraldi/afl and got the community patches applied
|
llvm up to version 8, Qemu 3.1, more speed and crashfixes for Qemu,
|
||||||
to it.
|
laf-intel feature for Qemu (with libcompcov) and more.
|
||||||
|
|
||||||
C. Hoellers afl-fuzz Python mutator module and llvm_mode whitelist support
|
Additionally the following patches have been integrated:
|
||||||
was added too (https://github.com/choller/afl)
|
|
||||||
|
* AFLfast's power schedules by Marcel Boehme: [https://github.com/mboehme/aflfast](https://github.com/mboehme/aflfast)
|
||||||
|
|
||||||
|
* C. Hollers afl-fuzz Python mutator module and llvm_mode whitelist support: [https://github.com/choller/afl](https://github.com/choller/afl)
|
||||||
|
|
||||||
|
* the new excellent MOpt mutator: [https://github.com/puppet-meteor/MOpt-AFL](https://github.com/puppet-meteor/MOpt-AFL)
|
||||||
|
|
||||||
|
* instrim, a very effective CFG llvm_mode instrumentation implementation for large targets: [https://github.com/csienslab/instrim](https://github.com/csienslab/instrim)
|
||||||
|
|
||||||
|
* unicorn_mode which allows fuzzing of binaries from completely different platforms (integration provided by domenukk)
|
||||||
|
|
||||||
|
A more thorough list is available in the PATCHES file.
|
||||||
|
|
||||||
So all in all this is the best-of AFL that is currently out there :-)
|
So all in all this is the best-of AFL that is currently out there :-)
|
||||||
|
|
||||||
|
|
||||||
Copyright 2013, 2014, 2015, 2016 Google Inc. All rights reserved.
|
|
||||||
Released under terms and conditions of Apache License, Version 2.0.
|
|
||||||
|
|
||||||
For new versions and additional information, check out:
|
For new versions and additional information, check out:
|
||||||
https://github.com/vanhauser-thc/AFLplusplus
|
[https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
||||||
|
|
||||||
To compare notes with other users or get notified about major new features,
|
To compare notes with other users or get notified about major new features,
|
||||||
send a mail to <afl-users+subscribe@googlegroups.com>.
|
send a mail to <afl-users+subscribe@googlegroups.com>.
|
||||||
|
|
||||||
** See QuickStartGuide.txt if you don't have time to read this file. **
|
See [docs/QuickStartGuide.txt](docs/QuickStartGuide.txt) if you don't have time to
|
||||||
|
read this file.
|
||||||
|
|
||||||
|
|
||||||
1) Challenges of guided fuzzing
|
## 1) Challenges of guided fuzzing
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Fuzzing is one of the most powerful and proven strategies for identifying
|
Fuzzing is one of the most powerful and proven strategies for identifying
|
||||||
@ -67,8 +70,7 @@ to suffer from reliability and performance problems in practical uses - and
|
|||||||
currently do not offer a viable alternative to "dumb" fuzzing techniques.
|
currently do not offer a viable alternative to "dumb" fuzzing techniques.
|
||||||
|
|
||||||
|
|
||||||
2) The afl-fuzz approach
|
## 2) The afl-fuzz approach
|
||||||
------------------------
|
|
||||||
|
|
||||||
American Fuzzy Lop is a brute-force fuzzer coupled with an exceedingly simple
|
American Fuzzy Lop is a brute-force fuzzer coupled with an exceedingly simple
|
||||||
but rock-solid instrumentation-guided genetic algorithm. It uses a modified
|
but rock-solid instrumentation-guided genetic algorithm. It uses a modified
|
||||||
@ -107,8 +109,7 @@ The fuzzer is thoroughly tested to deliver out-of-the-box performance far
|
|||||||
superior to blind fuzzing or coverage-only tools.
|
superior to blind fuzzing or coverage-only tools.
|
||||||
|
|
||||||
|
|
||||||
3) Instrumenting programs for use with AFL
|
## 3) Instrumenting programs for use with AFL
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
PLEASE NOTE: llvm_mode compilation with afl-clang-fast/afl-clang-fast++
|
PLEASE NOTE: llvm_mode compilation with afl-clang-fast/afl-clang-fast++
|
||||||
instead of afl-gcc/afl-g++ is much faster and has a few cool features.
|
instead of afl-gcc/afl-g++ is much faster and has a few cool features.
|
||||||
@ -126,39 +127,45 @@ or even faster than possible with traditional tools.
|
|||||||
The correct way to recompile the target program may vary depending on the
|
The correct way to recompile the target program may vary depending on the
|
||||||
specifics of the build process, but a nearly-universal approach would be:
|
specifics of the build process, but a nearly-universal approach would be:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ CC=/path/to/afl/afl-gcc ./configure
|
$ CC=/path/to/afl/afl-gcc ./configure
|
||||||
$ make clean all
|
$ make clean all
|
||||||
|
```
|
||||||
|
|
||||||
For C++ programs, you'd would also want to set CXX=/path/to/afl/afl-g++.
|
For C++ programs, you'd would also want to set `CXX=/path/to/afl/afl-g++`.
|
||||||
|
|
||||||
The clang wrappers (afl-clang and afl-clang++) can be used in the same way;
|
The clang wrappers (afl-clang and afl-clang++) can be used in the same way;
|
||||||
clang users may also opt to leverage a higher-performance instrumentation mode,
|
clang users may also opt to leverage a higher-performance instrumentation mode,
|
||||||
as described in llvm_mode/README.llvm.
|
as described in [llvm_mode/README.llvm](llvm_mode/README.llvm).
|
||||||
Clang/LLVM has a much better performance and works from LLVM version 4.0 to 8.
|
Clang/LLVM has a much better performance and works with LLVM version 4.0 to 8.
|
||||||
|
|
||||||
Using the LAF Intel performance enhancements are also recommended, see
|
Using the LAF Intel performance enhancements are also recommended, see
|
||||||
llvm_mode/README.laf-intel
|
[llvm_mode/README.laf-intel](llvm_mode/README.laf-intel)
|
||||||
|
|
||||||
Using partial instrumentation is also recommended, see
|
Using partial instrumentation is also recommended, see
|
||||||
llvm_mode/README.whitelist
|
[llvm_mode/README.whitelist](llvm_mode/README.whitelist)
|
||||||
|
|
||||||
When testing libraries, you need to find or write a simple program that reads
|
When testing libraries, you need to find or write a simple program that reads
|
||||||
data from stdin or from a file and passes it to the tested library. In such a
|
data from stdin or from a file and passes it to the tested library. In such a
|
||||||
case, it is essential to link this executable against a static version of the
|
case, it is essential to link this executable against a static version of the
|
||||||
instrumented library, or to make sure that the correct .so file is loaded at
|
instrumented library, or to make sure that the correct .so file is loaded at
|
||||||
runtime (usually by setting LD_LIBRARY_PATH). The simplest option is a static
|
runtime (usually by setting `LD_LIBRARY_PATH`). The simplest option is a static
|
||||||
build, usually possible via:
|
build, usually possible via:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
|
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
|
||||||
|
```
|
||||||
|
|
||||||
Setting AFL_HARDEN=1 when calling 'make' will cause the CC wrapper to
|
Setting `AFL_HARDEN=1` when calling 'make' will cause the CC wrapper to
|
||||||
automatically enable code hardening options that make it easier to detect
|
automatically enable code hardening options that make it easier to detect
|
||||||
simple memory bugs. Libdislocator, a helper library included with AFL (see
|
simple memory bugs. Libdislocator, a helper library included with AFL (see
|
||||||
libdislocator/README.dislocator) can help uncover heap corruption issues, too.
|
[libdislocator/README.dislocator](libdislocator/README.dislocator)) can help uncover heap corruption issues, too.
|
||||||
|
|
||||||
PS. ASAN users are advised to docs/review notes_for_asan.txt file for
|
PS. ASAN users are advised to review [docs/notes_for_asan.txt](docs/notes_for_asan.txt)
|
||||||
important caveats.
|
file for important caveats.
|
||||||
|
|
||||||
|
|
||||||
4) Instrumenting binary-only apps
|
## 4) Instrumenting binary-only apps
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
When source code is *NOT* available, the fuzzer offers experimental support for
|
When source code is *NOT* available, the fuzzer offers experimental support for
|
||||||
@ -168,10 +175,12 @@ with a version of QEMU running in the lesser-known "user space emulation" mode.
|
|||||||
QEMU is a project separate from AFL, but you can conveniently build the
|
QEMU is a project separate from AFL, but you can conveniently build the
|
||||||
feature by doing:
|
feature by doing:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ cd qemu_mode
|
$ cd qemu_mode
|
||||||
$ ./build_qemu_support.sh
|
$ ./build_qemu_support.sh
|
||||||
|
```
|
||||||
|
|
||||||
For additional instructions and caveats, see qemu_mode/README.qemu.
|
For additional instructions and caveats, see [qemu_mode/README.qemu](qemu_mode/README.qemu).
|
||||||
|
|
||||||
The mode is approximately 2-5x slower than compile-time instrumentation, is
|
The mode is approximately 2-5x slower than compile-time instrumentation, is
|
||||||
less conductive to parallelization, and may have some other quirks.
|
less conductive to parallelization, and may have some other quirks.
|
||||||
@ -180,23 +189,25 @@ If [afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) works for
|
|||||||
your binary, then you can use afl-fuzz normally and it will have twice
|
your binary, then you can use afl-fuzz normally and it will have twice
|
||||||
the speed compared to qemu_mode.
|
the speed compared to qemu_mode.
|
||||||
|
|
||||||
|
A more comprehensive description of these and other options can be found in
|
||||||
|
[docs/binaryonly_fuzzing.txt](docs/binaryonly_fuzzing.txt)
|
||||||
|
|
||||||
5) Power schedules
|
|
||||||
|
## 5) Power schedules
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The power schedules were copied from Marcel Böhme's excellent AFLfast
|
The power schedules were copied from Marcel Böhme's excellent AFLfast
|
||||||
implementation and expands on the ability to discover new paths and
|
implementation and expands on the ability to discover new paths and
|
||||||
therefore the coverage.
|
therefore the coverage.
|
||||||
|
|
||||||
| AFL flag | Power Schedule |
|
The available schedules are:
|
||||||
| ------------- | -------------------------- |
|
|
||||||
| `-p explore` (default)|  |
|
- explore (default)
|
||||||
| `-p fast` | =\\min\\left(\\frac{\\alpha(i)}{\\beta}\\cdot\\frac{2^{s(i)}}{f(i)},M\\right)) |
|
- fast
|
||||||
| `-p coe` |  |
|
- coe
|
||||||
| `-p quad` |  |
|
- quad
|
||||||
| `-p lin` |  |
|
- lin
|
||||||
| `-p exploit` (AFL) |  |
|
- exploit
|
||||||
where *α(i)* is the performance score that AFL uses to compute for the seed input *i*, *β(i)>1* is a constant, *s(i)* is the number of times that seed *i* has been chosen from the queue, *f(i)* is the number of generated inputs that exercise the same path as seed *i*, and *μ* is the average number of generated inputs exercising a path.
|
|
||||||
|
|
||||||
In parallel mode (-M/-S, several instances with shared queue), we suggest to
|
In parallel mode (-M/-S, several instances with shared queue), we suggest to
|
||||||
run the master using the exploit schedule (-p exploit) and the slaves with a
|
run the master using the exploit schedule (-p exploit) and the slaves with a
|
||||||
@ -206,13 +217,15 @@ and explore (-p explore) schedules.
|
|||||||
In single mode, using -p fast is usually more beneficial than the default
|
In single mode, using -p fast is usually more beneficial than the default
|
||||||
explore mode.
|
explore mode.
|
||||||
(We don't want to change the default behaviour of afl, so "fast" has not been
|
(We don't want to change the default behaviour of afl, so "fast" has not been
|
||||||
made the default mode)
|
made the default mode).
|
||||||
|
|
||||||
More details can be found in the paper:
|
More details can be found in the paper published at the 23rd ACM Conference on
|
||||||
[23rd ACM Conference on Computer and Communications Security (CCS'16)](https://www.sigsac.org/ccs/CCS2016/accepted-papers/).
|
Computer and Communications Security (CCS'16):
|
||||||
|
|
||||||
|
(https://www.sigsac.org/ccs/CCS2016/accepted-papers/)[https://www.sigsac.org/ccs/CCS2016/accepted-papers/]
|
||||||
|
|
||||||
|
|
||||||
6) Choosing initial test cases
|
## 6) Choosing initial test cases
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
To operate correctly, the fuzzer requires one or more starting file that
|
To operate correctly, the fuzzer requires one or more starting file that
|
||||||
@ -220,7 +233,7 @@ contains a good example of the input data normally expected by the targeted
|
|||||||
application. There are two basic rules:
|
application. There are two basic rules:
|
||||||
|
|
||||||
- Keep the files small. Under 1 kB is ideal, although not strictly necessary.
|
- Keep the files small. Under 1 kB is ideal, although not strictly necessary.
|
||||||
For a discussion of why size matters, see perf_tips.txt.
|
For a discussion of why size matters, see [perf_tips.txt](docs/perf_tips.txt).
|
||||||
|
|
||||||
- Use multiple test cases only if they are functionally different from
|
- Use multiple test cases only if they are functionally different from
|
||||||
each other. There is no point in using fifty different vacation photos
|
each other. There is no point in using fifty different vacation photos
|
||||||
@ -234,7 +247,7 @@ the afl-cmin utility to identify a subset of functionally distinct files that
|
|||||||
exercise different code paths in the target binary.
|
exercise different code paths in the target binary.
|
||||||
|
|
||||||
|
|
||||||
7) Fuzzing binaries
|
## 7) Fuzzing binaries
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
The fuzzing process itself is carried out by the afl-fuzz utility. This program
|
The fuzzing process itself is carried out by the afl-fuzz utility. This program
|
||||||
@ -243,13 +256,17 @@ store its findings, plus a path to the binary to test.
|
|||||||
|
|
||||||
For target binaries that accept input directly from stdin, the usual syntax is:
|
For target binaries that accept input directly from stdin, the usual syntax is:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
|
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
|
||||||
|
```
|
||||||
|
|
||||||
For programs that take input from a file, use '@@' to mark the location in
|
For programs that take input from a file, use '@@' to mark the location in
|
||||||
the target's command line where the input file name should be placed. The
|
the target's command line where the input file name should be placed. The
|
||||||
fuzzer will substitute this for you:
|
fuzzer will substitute this for you:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
|
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
|
||||||
|
```
|
||||||
|
|
||||||
You can also use the -f option to have the mutated data written to a specific
|
You can also use the -f option to have the mutated data written to a specific
|
||||||
file. This is useful if the program expects a particular file extension or so.
|
file. This is useful if the program expects a particular file extension or so.
|
||||||
@ -261,7 +278,7 @@ You can use -t and -m to override the default timeout and memory limit for the
|
|||||||
executed process; rare examples of targets that may need these settings touched
|
executed process; rare examples of targets that may need these settings touched
|
||||||
include compilers and video decoders.
|
include compilers and video decoders.
|
||||||
|
|
||||||
Tips for optimizing fuzzing performance are discussed in perf_tips.txt.
|
Tips for optimizing fuzzing performance are discussed in [perf_tips.txt](docs/perf_tips.txt).
|
||||||
|
|
||||||
Note that afl-fuzz starts by performing an array of deterministic fuzzing
|
Note that afl-fuzz starts by performing an array of deterministic fuzzing
|
||||||
steps, which can take several days, but tend to produce neat test cases. If you
|
steps, which can take several days, but tend to produce neat test cases. If you
|
||||||
@ -269,12 +286,12 @@ want quick & dirty results right away - akin to zzuf and other traditional
|
|||||||
fuzzers - add the -d option to the command line.
|
fuzzers - add the -d option to the command line.
|
||||||
|
|
||||||
|
|
||||||
8) Interpreting output
|
## 8) Interpreting output
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
See the status_screen.txt file for information on how to interpret the
|
See the [docs/status_screen.txt](docs/status_screen.txt) file for information on
|
||||||
displayed stats and monitor the health of the process. Be sure to consult this
|
how to interpret the displayed stats and monitor the health of the process. Be
|
||||||
file especially if any UI elements are highlighted in red.
|
sure to consult this file especially if any UI elements are highlighted in red.
|
||||||
|
|
||||||
The fuzzing process will continue until you press Ctrl-C. At minimum, you want
|
The fuzzing process will continue until you press Ctrl-C. At minimum, you want
|
||||||
to allow the fuzzer to complete one queue cycle, which may take anywhere from a
|
to allow the fuzzer to complete one queue cycle, which may take anywhere from a
|
||||||
@ -312,35 +329,39 @@ queue entries. This should help with debugging.
|
|||||||
When you can't reproduce a crash found by afl-fuzz, the most likely cause is
|
When you can't reproduce a crash found by afl-fuzz, the most likely cause is
|
||||||
that you are not setting the same memory limit as used by the tool. Try:
|
that you are not setting the same memory limit as used by the tool. Try:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ LIMIT_MB=50
|
$ LIMIT_MB=50
|
||||||
$ ( ulimit -Sv $[LIMIT_MB << 10]; /path/to/tested_binary ... )
|
$ ( ulimit -Sv $[LIMIT_MB << 10]; /path/to/tested_binary ... )
|
||||||
|
```
|
||||||
|
|
||||||
Change LIMIT_MB to match the -m parameter passed to afl-fuzz. On OpenBSD,
|
Change LIMIT_MB to match the -m parameter passed to afl-fuzz. On OpenBSD,
|
||||||
also change -Sv to -Sd.
|
also change -Sv to -Sd.
|
||||||
|
|
||||||
Any existing output directory can be also used to resume aborted jobs; try:
|
Any existing output directory can be also used to resume aborted jobs; try:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
|
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
|
||||||
|
```
|
||||||
|
|
||||||
If you have gnuplot installed, you can also generate some pretty graphs for any
|
If you have gnuplot installed, you can also generate some pretty graphs for any
|
||||||
active fuzzing task using afl-plot. For an example of how this looks like,
|
active fuzzing task using afl-plot. For an example of how this looks like,
|
||||||
see http://lcamtuf.coredump.cx/afl/plot/.
|
see [http://lcamtuf.coredump.cx/afl/plot/](http://lcamtuf.coredump.cx/afl/plot/).
|
||||||
|
|
||||||
|
|
||||||
9) Parallelized fuzzing
|
## 9) Parallelized fuzzing
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Every instance of afl-fuzz takes up roughly one core. This means that on
|
Every instance of afl-fuzz takes up roughly one core. This means that on
|
||||||
multi-core systems, parallelization is necessary to fully utilize the hardware.
|
multi-core systems, parallelization is necessary to fully utilize the hardware.
|
||||||
For tips on how to fuzz a common target on multiple cores or multiple networked
|
For tips on how to fuzz a common target on multiple cores or multiple networked
|
||||||
machines, please refer to parallel_fuzzing.txt.
|
machines, please refer to [docs/parallel_fuzzing.txt](docs/parallel_fuzzing.txt).
|
||||||
|
|
||||||
The parallel fuzzing mode also offers a simple way for interfacing AFL to other
|
The parallel fuzzing mode also offers a simple way for interfacing AFL to other
|
||||||
fuzzers, to symbolic or concolic execution engines, and so forth; again, see the
|
fuzzers, to symbolic or concolic execution engines, and so forth; again, see the
|
||||||
last section of parallel_fuzzing.txt for tips.
|
last section of [docs/parallel_fuzzing.txt](docs/parallel_fuzzing.txt) for tips.
|
||||||
|
|
||||||
|
|
||||||
10) Fuzzer dictionaries
|
## 10) Fuzzer dictionaries
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
By default, afl-fuzz mutation engine is optimized for compact data formats -
|
By default, afl-fuzz mutation engine is optimized for compact data formats -
|
||||||
@ -351,13 +372,13 @@ redundant verbiage - notably including HTML, SQL, or JavaScript.
|
|||||||
To avoid the hassle of building syntax-aware tools, afl-fuzz provides a way to
|
To avoid the hassle of building syntax-aware tools, afl-fuzz provides a way to
|
||||||
seed the fuzzing process with an optional dictionary of language keywords,
|
seed the fuzzing process with an optional dictionary of language keywords,
|
||||||
magic headers, or other special tokens associated with the targeted data type
|
magic headers, or other special tokens associated with the targeted data type
|
||||||
- and use that to reconstruct the underlying grammar on the go:
|
-- and use that to reconstruct the underlying grammar on the go:
|
||||||
|
|
||||||
http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html
|
[http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html](http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html)
|
||||||
|
|
||||||
To use this feature, you first need to create a dictionary in one of the two
|
To use this feature, you first need to create a dictionary in one of the two
|
||||||
formats discussed in dictionaries/README.dictionaries; and then point the fuzzer
|
formats discussed in [dictionaries/README.dictionaries](ictionaries/README.dictionaries);
|
||||||
to it via the -x option in the command line.
|
and then point the fuzzer to it via the -x option in the command line.
|
||||||
|
|
||||||
(Several common dictionaries are already provided in that subdirectory, too.)
|
(Several common dictionaries are already provided in that subdirectory, too.)
|
||||||
|
|
||||||
@ -365,7 +386,7 @@ There is no way to provide more structured descriptions of the underlying
|
|||||||
syntax, but the fuzzer will likely figure out some of this based on the
|
syntax, but the fuzzer will likely figure out some of this based on the
|
||||||
instrumentation feedback alone. This actually works in practice, say:
|
instrumentation feedback alone. This actually works in practice, say:
|
||||||
|
|
||||||
http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html
|
[http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html](http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html)
|
||||||
|
|
||||||
PS. Even when no explicit dictionary is given, afl-fuzz will try to extract
|
PS. Even when no explicit dictionary is given, afl-fuzz will try to extract
|
||||||
existing syntax tokens in the input corpus by watching the instrumentation
|
existing syntax tokens in the input corpus by watching the instrumentation
|
||||||
@ -374,10 +395,10 @@ parsers and grammars, but isn't nearly as good as the -x mode.
|
|||||||
|
|
||||||
If a dictionary is really hard to come by, another option is to let AFL run
|
If a dictionary is really hard to come by, another option is to let AFL run
|
||||||
for a while, and then use the token capture library that comes as a companion
|
for a while, and then use the token capture library that comes as a companion
|
||||||
utility with AFL. For that, see libtokencap/README.tokencap.
|
utility with AFL. For that, see [libtokencap/README.tokencap](libtokencap/README.tokencap).
|
||||||
|
|
||||||
|
|
||||||
11) Crash triage
|
## 11) Crash triage
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The coverage-based grouping of crashes usually produces a small data set that
|
The coverage-based grouping of crashes usually produces a small data set that
|
||||||
@ -406,7 +427,9 @@ beneath.
|
|||||||
Oh, one more thing: for test case minimization, give afl-tmin a try. The tool
|
Oh, one more thing: for test case minimization, give afl-tmin a try. The tool
|
||||||
can be operated in a very simple way:
|
can be operated in a very simple way:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
|
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
|
||||||
|
```
|
||||||
|
|
||||||
The tool works with crashing and non-crashing test cases alike. In the crash
|
The tool works with crashing and non-crashing test cases alike. In the crash
|
||||||
mode, it will happily accept instrumented and non-instrumented binaries. In the
|
mode, it will happily accept instrumented and non-instrumented binaries. In the
|
||||||
@ -421,10 +444,10 @@ file, attempts to sequentially flip bytes, and observes the behavior of the
|
|||||||
tested program. It then color-codes the input based on which sections appear to
|
tested program. It then color-codes the input based on which sections appear to
|
||||||
be critical, and which are not; while not bulletproof, it can often offer quick
|
be critical, and which are not; while not bulletproof, it can often offer quick
|
||||||
insights into complex file formats. More info about its operation can be found
|
insights into complex file formats. More info about its operation can be found
|
||||||
near the end of technical_details.txt.
|
near the end of [docs/technical_details.txt](docs/technical_details.txt).
|
||||||
|
|
||||||
|
|
||||||
12) Going beyond crashes
|
## 12) Going beyond crashes
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Fuzzing is a wonderful and underutilized technique for discovering non-crashing
|
Fuzzing is a wonderful and underutilized technique for discovering non-crashing
|
||||||
@ -445,11 +468,11 @@ found by modifying the target programs to call abort() when, say:
|
|||||||
|
|
||||||
Implementing these or similar sanity checks usually takes very little time;
|
Implementing these or similar sanity checks usually takes very little time;
|
||||||
if you are the maintainer of a particular package, you can make this code
|
if you are the maintainer of a particular package, you can make this code
|
||||||
conditional with #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION (a flag also
|
conditional with `#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` (a flag also
|
||||||
shared with libfuzzer) or #ifdef __AFL_COMPILER (this one is just for AFL).
|
shared with libfuzzer) or `#ifdef __AFL_COMPILER` (this one is just for AFL).
|
||||||
|
|
||||||
|
|
||||||
13) Common-sense risks
|
## 13) Common-sense risks
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Please keep in mind that, similarly to many other computationally-intensive
|
Please keep in mind that, similarly to many other computationally-intensive
|
||||||
@ -475,10 +498,12 @@ tasks, fuzzing may put strain on your hardware and on the OS. In particular:
|
|||||||
|
|
||||||
A good way to monitor disk I/O on Linux is the 'iostat' command:
|
A good way to monitor disk I/O on Linux is the 'iostat' command:
|
||||||
|
|
||||||
|
```shell
|
||||||
$ iostat -d 3 -x -k [...optional disk ID...]
|
$ iostat -d 3 -x -k [...optional disk ID...]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
14) Known limitations & areas for improvement
|
## 14) Known limitations & areas for improvement
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
Here are some of the most important caveats for AFL:
|
Here are some of the most important caveats for AFL:
|
||||||
@ -499,33 +524,34 @@ Here are some of the most important caveats for AFL:
|
|||||||
experimental/post_library/ (with AFL_POST_LIBRARY)
|
experimental/post_library/ (with AFL_POST_LIBRARY)
|
||||||
|
|
||||||
- There are some unfortunate trade-offs with ASAN and 64-bit binaries. This
|
- There are some unfortunate trade-offs with ASAN and 64-bit binaries. This
|
||||||
isn't due to any specific fault of afl-fuzz; see notes_for_asan.txt for
|
isn't due to any specific fault of afl-fuzz; see [docs/notes_for_asan.txt](docs/notes_for_asan.txt)
|
||||||
tips.
|
for tips.
|
||||||
|
|
||||||
- There is no direct support for fuzzing network services, background
|
- There is no direct support for fuzzing network services, background
|
||||||
daemons, or interactive apps that require UI interaction to work. You may
|
daemons, or interactive apps that require UI interaction to work. You may
|
||||||
need to make simple code changes to make them behave in a more traditional
|
need to make simple code changes to make them behave in a more traditional
|
||||||
way. Preeny may offer a relatively simple option, too - see:
|
way. Preeny may offer a relatively simple option, too - see:
|
||||||
https://github.com/zardus/preeny
|
[https://github.com/zardus/preeny](https://github.com/zardus/preeny)
|
||||||
|
|
||||||
Some useful tips for modifying network-based services can be also found at:
|
Some useful tips for modifying network-based services can be also found at:
|
||||||
https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop
|
[https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop](https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop)
|
||||||
|
|
||||||
- AFL doesn't output human-readable coverage data. If you want to monitor
|
- AFL doesn't output human-readable coverage data. If you want to monitor
|
||||||
coverage, use afl-cov from Michael Rash: https://github.com/mrash/afl-cov
|
coverage, use afl-cov from Michael Rash: [https://github.com/mrash/afl-cov](https://github.com/mrash/afl-cov)
|
||||||
|
|
||||||
- Occasionally, sentient machines rise against their creators. If this
|
- Occasionally, sentient machines rise against their creators. If this
|
||||||
happens to you, please consult http://lcamtuf.coredump.cx/prep/.
|
happens to you, please consult [http://lcamtuf.coredump.cx/prep/](http://lcamtuf.coredump.cx/prep/).
|
||||||
|
|
||||||
Beyond this, see INSTALL for platform-specific tips.
|
Beyond this, see INSTALL for platform-specific tips.
|
||||||
|
|
||||||
|
|
||||||
15) Special thanks
|
## 15) Special thanks
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Many of the improvements to afl-fuzz wouldn't be possible without feedback,
|
Many of the improvements to the original afl wouldn't be possible without
|
||||||
bug reports, or patches from:
|
feedback, bug reports, or patches from:
|
||||||
|
|
||||||
|
```
|
||||||
Jann Horn Hanno Boeck
|
Jann Horn Hanno Boeck
|
||||||
Felix Groebert Jakub Wilk
|
Felix Groebert Jakub Wilk
|
||||||
Richard W. M. Jones Alexander Cherepanov
|
Richard W. M. Jones Alexander Cherepanov
|
||||||
@ -565,18 +591,18 @@ bug reports, or patches from:
|
|||||||
Rene Freingruber Sergey Davidoff
|
Rene Freingruber Sergey Davidoff
|
||||||
Sami Liedes Craig Young
|
Sami Liedes Craig Young
|
||||||
Andrzej Jackowski Daniel Hodson
|
Andrzej Jackowski Daniel Hodson
|
||||||
|
Nathan Voss Dominik Maier
|
||||||
|
```
|
||||||
|
|
||||||
Thank you!
|
Thank you!
|
||||||
|
|
||||||
|
|
||||||
16) Contact
|
## 16) Contact
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Questions? Concerns? Bug reports? The contributors can be reached via
|
Questions? Concerns? Bug reports? The contributors can be reached via
|
||||||
https://github.com/vanhauser-thc/AFLplusplus
|
[https://github.com/vanhauser-thc/AFLplusplus](https://github.com/vanhauser-thc/AFLplusplus)
|
||||||
|
|
||||||
There is also a mailing list for the afl project; to join, send a mail to
|
There is also a mailing list for the afl project; to join, send a mail to
|
||||||
<afl-users+subscribe@googlegroups.com>. Or, if you prefer to browse
|
<afl-users+subscribe@googlegroups.com>. Or, if you prefer to browse
|
||||||
archives first, try:
|
archives first, try: [https://groups.google.com/group/afl-users](https://groups.google.com/group/afl-users)
|
||||||
|
|
||||||
https://groups.google.com/group/afl-users
|
|
34
TODO
Normal file
34
TODO
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
Roadmap 2.53d:
|
||||||
|
==============
|
||||||
|
- indent all the code: clang-format -style=Google
|
||||||
|
|
||||||
|
- update docs/sister_projects.txt
|
||||||
|
|
||||||
|
afl-fuzz:
|
||||||
|
- put mutator, scheduler, forkserver and input channels in individual files
|
||||||
|
- reuse forkserver for showmap, afl-cmin, etc.
|
||||||
|
|
||||||
|
gcc_plugin:
|
||||||
|
- needs to be rewritten
|
||||||
|
- fix crashes when compiling :(
|
||||||
|
- whitelist support
|
||||||
|
- skip over uninteresting blocks
|
||||||
|
- laf-intel
|
||||||
|
- neverZero
|
||||||
|
|
||||||
|
qemu_mode:
|
||||||
|
- deferred mode with AFL_DEFERRED_QEMU=0xaddress
|
||||||
|
|
||||||
|
unit testing / or large testcase campaign
|
||||||
|
|
||||||
|
|
||||||
|
Roadmap 2.54d:
|
||||||
|
==============
|
||||||
|
- expand MAP size to 256k (current L2 cache size on processors)
|
||||||
|
-> 18 bit map
|
||||||
|
- llvm_mode: dynamic map size and collission free basic block IDs
|
||||||
|
|
||||||
|
qemu_mode:
|
||||||
|
- persistent mode patching the return address (WinAFL style)
|
||||||
|
- instrument only comparison with immediate values by default when using compcov
|
||||||
|
|
100
afl-analyze.c
100
afl-analyze.c
@ -26,6 +26,8 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "sharedmem.h"
|
||||||
|
#include "afl-common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -47,7 +49,7 @@
|
|||||||
|
|
||||||
static s32 child_pid; /* PID of the tested program */
|
static s32 child_pid; /* PID of the tested program */
|
||||||
|
|
||||||
static u8* trace_bits; /* SHM with instrumentation bitmap */
|
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||||
|
|
||||||
static u8 *in_file, /* Analyzer input test case */
|
static u8 *in_file, /* Analyzer input test case */
|
||||||
*prog_in, /* Targeted program input file */
|
*prog_in, /* Targeted program input file */
|
||||||
@ -64,8 +66,7 @@ static u32 in_len, /* Input data length */
|
|||||||
|
|
||||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||||
|
|
||||||
static s32 shm_id, /* ID of the SHM region */
|
static s32 dev_null_fd = -1; /* FD to /dev/null */
|
||||||
dev_null_fd = -1; /* FD to /dev/null */
|
|
||||||
|
|
||||||
static u8 edges_only, /* Ignore hit counts? */
|
static u8 edges_only, /* Ignore hit counts? */
|
||||||
use_hex_offsets, /* Show hex offsets? */
|
use_hex_offsets, /* Show hex offsets? */
|
||||||
@ -141,37 +142,11 @@ static inline u8 anything_set(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get rid of shared memory and temp files (atexit handler). */
|
/* Get rid of temp files (atexit handler). */
|
||||||
|
|
||||||
static void remove_shm(void) {
|
static void at_exit_handler(void) {
|
||||||
|
|
||||||
unlink(prog_in); /* Ignore errors */
|
unlink(prog_in); /* Ignore errors */
|
||||||
shmctl(shm_id, IPC_RMID, NULL);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Configure shared memory. */
|
|
||||||
|
|
||||||
static void setup_shm(void) {
|
|
||||||
|
|
||||||
u8* shm_str;
|
|
||||||
|
|
||||||
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
|
|
||||||
|
|
||||||
if (shm_id < 0) PFATAL("shmget() failed");
|
|
||||||
|
|
||||||
atexit(remove_shm);
|
|
||||||
|
|
||||||
shm_str = alloc_printf("%d", shm_id);
|
|
||||||
|
|
||||||
setenv(SHM_ENV_VAR, shm_str, 1);
|
|
||||||
|
|
||||||
ck_free(shm_str);
|
|
||||||
|
|
||||||
trace_bits = shmat(shm_id, NULL, 0);
|
|
||||||
|
|
||||||
if (!trace_bits) PFATAL("shmat() failed");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,48 +725,6 @@ static void setup_signal_handlers(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Detect @@ in args. */
|
|
||||||
|
|
||||||
static void detect_file_args(char** argv) {
|
|
||||||
|
|
||||||
u32 i = 0;
|
|
||||||
u8* cwd = getcwd(NULL, 0);
|
|
||||||
|
|
||||||
if (!cwd) PFATAL("getcwd() failed");
|
|
||||||
|
|
||||||
while (argv[i]) {
|
|
||||||
|
|
||||||
u8* aa_loc = strstr(argv[i], "@@");
|
|
||||||
|
|
||||||
if (aa_loc) {
|
|
||||||
|
|
||||||
u8 *aa_subst, *n_arg;
|
|
||||||
|
|
||||||
/* Be sure that we're always using fully-qualified paths. */
|
|
||||||
|
|
||||||
if (prog_in[0] == '/') aa_subst = prog_in;
|
|
||||||
else aa_subst = alloc_printf("%s/%s", cwd, prog_in);
|
|
||||||
|
|
||||||
/* Construct a replacement argv value. */
|
|
||||||
|
|
||||||
*aa_loc = 0;
|
|
||||||
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
|
|
||||||
argv[i] = n_arg;
|
|
||||||
*aa_loc = '@';
|
|
||||||
|
|
||||||
if (prog_in[0] != '/') ck_free(aa_subst);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
free(cwd); /* not tracked */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Display usage hints. */
|
/* Display usage hints. */
|
||||||
|
|
||||||
static void usage(u8* argv0) {
|
static void usage(u8* argv0) {
|
||||||
@ -807,7 +740,8 @@ static void usage(u8* argv0) {
|
|||||||
" -f file - input file read by the tested program (stdin)\n"
|
" -f file - input file read by the tested program (stdin)\n"
|
||||||
" -t msec - timeout for each run (%u ms)\n"
|
" -t msec - timeout for each run (%u ms)\n"
|
||||||
" -m megs - memory limit for child process (%u MB)\n"
|
" -m megs - memory limit for child process (%u MB)\n"
|
||||||
" -Q - use binary-only instrumentation (QEMU mode)\n\n"
|
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||||
|
" -U - use unicorn-based instrumentation (Unicorn mode)\n\n"
|
||||||
|
|
||||||
"Analysis settings:\n\n"
|
"Analysis settings:\n\n"
|
||||||
|
|
||||||
@ -933,20 +867,19 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Main entry point */
|
/* Main entry point */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
s32 opt;
|
s32 opt;
|
||||||
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
|
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0, unicorn_mode = 0;
|
||||||
char** use_argv;
|
char** use_argv;
|
||||||
|
|
||||||
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
||||||
|
|
||||||
SAYF(cCYA "afl-analyze" VERSION cRST " by <lcamtuf@google.com>\n");
|
SAYF(cCYA "afl-analyze" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||||
|
|
||||||
while ((opt = getopt(argc,argv,"+i:f:m:t:eQ")) > 0)
|
while ((opt = getopt(argc,argv,"+i:f:m:t:eQU")) > 0)
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
|
||||||
@ -1026,6 +959,14 @@ int main(int argc, char** argv) {
|
|||||||
qemu_mode = 1;
|
qemu_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'U':
|
||||||
|
|
||||||
|
if (unicorn_mode) FATAL("Multiple -U options not supported");
|
||||||
|
if (!mem_limit_given) mem_limit = MEM_LIMIT_UNICORN;
|
||||||
|
|
||||||
|
unicorn_mode = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
@ -1036,13 +977,14 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
use_hex_offsets = !!getenv("AFL_ANALYZE_HEX");
|
use_hex_offsets = !!getenv("AFL_ANALYZE_HEX");
|
||||||
|
|
||||||
setup_shm();
|
setup_shm(0);
|
||||||
|
atexit(at_exit_handler);
|
||||||
setup_signal_handlers();
|
setup_signal_handlers();
|
||||||
|
|
||||||
set_up_environment();
|
set_up_environment();
|
||||||
|
|
||||||
find_binary(argv[optind]);
|
find_binary(argv[optind]);
|
||||||
detect_file_args(argv + optind);
|
detect_file_args(argv + optind, prog_in);
|
||||||
|
|
||||||
if (qemu_mode)
|
if (qemu_mode)
|
||||||
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
||||||
|
48
afl-as.h
48
afl-as.h
@ -189,6 +189,7 @@ static const u8* main_payload_32 =
|
|||||||
" orb $1, (%edx, %edi, 1)\n"
|
" orb $1, (%edx, %edi, 1)\n"
|
||||||
#else
|
#else
|
||||||
" incb (%edx, %edi, 1)\n"
|
" incb (%edx, %edi, 1)\n"
|
||||||
|
" adcb $0, (%edx, %edi, 1)\n" // never zero counter implementation. slightly better path discovery and little performance impact
|
||||||
#endif /* ^SKIP_COUNTS */
|
#endif /* ^SKIP_COUNTS */
|
||||||
"\n"
|
"\n"
|
||||||
"__afl_return:\n"
|
"__afl_return:\n"
|
||||||
@ -220,6 +221,29 @@ static const u8* main_payload_32 =
|
|||||||
" testl %eax, %eax\n"
|
" testl %eax, %eax\n"
|
||||||
" je __afl_setup_abort\n"
|
" je __afl_setup_abort\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
#ifdef USEMMAP
|
||||||
|
" pushl $384 /* shm_open mode 0600 */\n"
|
||||||
|
" pushl $2 /* flags O_RDWR */\n"
|
||||||
|
" pushl %eax /* SHM file path */\n"
|
||||||
|
" call shm_open\n"
|
||||||
|
" addl $12, %esp\n"
|
||||||
|
"\n"
|
||||||
|
" cmpl $-1, %eax\n"
|
||||||
|
" je __afl_setup_abort\n"
|
||||||
|
"\n"
|
||||||
|
" pushl $0 /* mmap off */\n"
|
||||||
|
" pushl %eax /* shm fd */\n"
|
||||||
|
" pushl $1 /* mmap flags */\n"
|
||||||
|
" pushl $3 /* mmap prot */\n"
|
||||||
|
" pushl $"STRINGIFY(MAP_SIZE)" /* mmap len */\n"
|
||||||
|
" pushl $0 /* mmap addr */\n"
|
||||||
|
" call mmap\n"
|
||||||
|
" addl $12, %esp\n"
|
||||||
|
"\n"
|
||||||
|
" cmpl $-1, %eax\n"
|
||||||
|
" je __afl_setup_abort\n"
|
||||||
|
"\n"
|
||||||
|
#else
|
||||||
" pushl %eax\n"
|
" pushl %eax\n"
|
||||||
" call atoi\n"
|
" call atoi\n"
|
||||||
" addl $4, %esp\n"
|
" addl $4, %esp\n"
|
||||||
@ -233,6 +257,7 @@ static const u8* main_payload_32 =
|
|||||||
" cmpl $-1, %eax\n"
|
" cmpl $-1, %eax\n"
|
||||||
" je __afl_setup_abort\n"
|
" je __afl_setup_abort\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
#endif
|
||||||
" /* Store the address of the SHM region. */\n"
|
" /* Store the address of the SHM region. */\n"
|
||||||
"\n"
|
"\n"
|
||||||
" movl %eax, __afl_area_ptr\n"
|
" movl %eax, __afl_area_ptr\n"
|
||||||
@ -417,6 +442,7 @@ static const u8* main_payload_64 =
|
|||||||
" orb $1, (%rdx, %rcx, 1)\n"
|
" orb $1, (%rdx, %rcx, 1)\n"
|
||||||
#else
|
#else
|
||||||
" incb (%rdx, %rcx, 1)\n"
|
" incb (%rdx, %rcx, 1)\n"
|
||||||
|
" adcb $0, (%rdx, %rcx, 1)\n" // never zero counter implementation. slightly better path discovery and little performance impact
|
||||||
#endif /* ^SKIP_COUNTS */
|
#endif /* ^SKIP_COUNTS */
|
||||||
"\n"
|
"\n"
|
||||||
"__afl_return:\n"
|
"__afl_return:\n"
|
||||||
@ -501,6 +527,27 @@ static const u8* main_payload_64 =
|
|||||||
" testq %rax, %rax\n"
|
" testq %rax, %rax\n"
|
||||||
" je __afl_setup_abort\n"
|
" je __afl_setup_abort\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
#ifdef USEMMAP
|
||||||
|
" movl $384, %edx /* shm_open mode 0600 */\n"
|
||||||
|
" movl $2, %esi /* flags O_RDWR */\n"
|
||||||
|
" movq %rax, %rdi /* SHM file path */\n"
|
||||||
|
CALL_L64("shm_open")
|
||||||
|
"\n"
|
||||||
|
" cmpq $-1, %rax\n"
|
||||||
|
" je __afl_setup_abort\n"
|
||||||
|
"\n"
|
||||||
|
" movl $0, %r9d\n"
|
||||||
|
" movl %eax, %r8d\n"
|
||||||
|
" movl $1, %ecx\n"
|
||||||
|
" movl $3, %edx\n"
|
||||||
|
" movl $"STRINGIFY(MAP_SIZE)", %esi\n"
|
||||||
|
" movl $0, %edi\n"
|
||||||
|
CALL_L64("mmap")
|
||||||
|
"\n"
|
||||||
|
" cmpq $-1, %rax\n"
|
||||||
|
" je __afl_setup_abort\n"
|
||||||
|
"\n"
|
||||||
|
#else
|
||||||
" movq %rax, %rdi\n"
|
" movq %rax, %rdi\n"
|
||||||
CALL_L64("atoi")
|
CALL_L64("atoi")
|
||||||
"\n"
|
"\n"
|
||||||
@ -512,6 +559,7 @@ static const u8* main_payload_64 =
|
|||||||
" cmpq $-1, %rax\n"
|
" cmpq $-1, %rax\n"
|
||||||
" je __afl_setup_abort\n"
|
" je __afl_setup_abort\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
#endif
|
||||||
" /* Store the address of the SHM region. */\n"
|
" /* Store the address of the SHM region. */\n"
|
||||||
"\n"
|
"\n"
|
||||||
" movq %rax, %rdx\n"
|
" movq %rax, %rdx\n"
|
||||||
|
14
afl-cmin
14
afl-cmin
@ -49,9 +49,9 @@ MEM_LIMIT=100
|
|||||||
TIMEOUT=none
|
TIMEOUT=none
|
||||||
|
|
||||||
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
|
unset IN_DIR OUT_DIR STDIN_FILE EXTRA_PAR MEM_LIMIT_GIVEN \
|
||||||
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE
|
AFL_CMIN_CRASHES_ONLY AFL_CMIN_ALLOW_ANY QEMU_MODE UNICORN_MODE
|
||||||
|
|
||||||
while getopts "+i:o:f:m:t:eQC" opt; do
|
while getopts "+i:o:f:m:t:eQUC" opt; do
|
||||||
|
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
|
|
||||||
@ -83,6 +83,11 @@ while getopts "+i:o:f:m:t:eQC" opt; do
|
|||||||
test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
|
test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
|
||||||
QEMU_MODE=1
|
QEMU_MODE=1
|
||||||
;;
|
;;
|
||||||
|
"U")
|
||||||
|
EXTRA_PAR="$EXTRA_PAR -U"
|
||||||
|
test "$MEM_LIMIT_GIVEN" = "" && MEM_LIMIT=250
|
||||||
|
UNICORN_MODE=1
|
||||||
|
;;
|
||||||
"?")
|
"?")
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
@ -111,7 +116,8 @@ Execution control settings:
|
|||||||
-m megs - memory limit for child process ($MEM_LIMIT MB)
|
-m megs - memory limit for child process ($MEM_LIMIT MB)
|
||||||
-t msec - run time limit for child process (none)
|
-t msec - run time limit for child process (none)
|
||||||
-Q - use binary-only instrumentation (QEMU mode)
|
-Q - use binary-only instrumentation (QEMU mode)
|
||||||
|
-U - use unicorn-based instrumentation (Unicorn mode)
|
||||||
|
|
||||||
Minimization settings:
|
Minimization settings:
|
||||||
|
|
||||||
-C - keep crashing inputs, reject everything else
|
-C - keep crashing inputs, reject everything else
|
||||||
@ -196,7 +202,7 @@ if [ ! -f "$TARGET_BIN" -o ! -x "$TARGET_BIN" ]; then
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" ]; then
|
if [ "$AFL_SKIP_BIN_CHECK" = "" -a "$QEMU_MODE" = "" -a "$UNICORN_MODE" = "" ]; then
|
||||||
|
|
||||||
if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
|
if ! grep -qF "__AFL_SHM_ID" "$TARGET_BIN"; then
|
||||||
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
|
echo "[-] Error: binary '$TARGET_BIN' doesn't appear to be instrumented." 1>&2
|
||||||
|
69
afl-common.c
Normal file
69
afl-common.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
gather some functions common to multiple executables
|
||||||
|
|
||||||
|
detect_file_args
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include "alloc-inl.h"
|
||||||
|
|
||||||
|
/* Detect @@ in args. */
|
||||||
|
#ifndef __glibc__
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
void detect_file_args(char** argv, u8* prog_in) {
|
||||||
|
|
||||||
|
u32 i = 0;
|
||||||
|
#ifdef __GLIBC__
|
||||||
|
u8* cwd = getcwd(NULL, 0); /* non portable glibc extension */
|
||||||
|
#else
|
||||||
|
u8* cwd;
|
||||||
|
char *buf;
|
||||||
|
long size = pathconf(".", _PC_PATH_MAX);
|
||||||
|
if ((buf = (char *)malloc((size_t)size)) != NULL) {
|
||||||
|
cwd = getcwd(buf, (size_t)size); /* portable version */
|
||||||
|
} else {
|
||||||
|
PFATAL("getcwd() failed");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!cwd) PFATAL("getcwd() failed");
|
||||||
|
|
||||||
|
while (argv[i]) {
|
||||||
|
|
||||||
|
u8* aa_loc = strstr(argv[i], "@@");
|
||||||
|
|
||||||
|
if (aa_loc) {
|
||||||
|
|
||||||
|
u8 *aa_subst, *n_arg;
|
||||||
|
|
||||||
|
if (!prog_in) FATAL("@@ syntax is not supported by this tool.");
|
||||||
|
|
||||||
|
/* Be sure that we're always using fully-qualified paths. */
|
||||||
|
|
||||||
|
if (prog_in[0] == '/') aa_subst = prog_in;
|
||||||
|
else aa_subst = alloc_printf("%s/%s", cwd, prog_in);
|
||||||
|
|
||||||
|
/* Construct a replacement argv value. */
|
||||||
|
|
||||||
|
*aa_loc = 0;
|
||||||
|
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
|
||||||
|
argv[i] = n_arg;
|
||||||
|
*aa_loc = '@';
|
||||||
|
|
||||||
|
if (prog_in[0] != '/') ck_free(aa_subst);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cwd); /* not tracked */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
5
afl-common.h
Normal file
5
afl-common.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#ifndef __AFLCOMMON_H
|
||||||
|
#define __AFLCOMMON_H
|
||||||
|
|
||||||
|
void detect_file_args(char **argv, u8 *prog_in);
|
||||||
|
#endif
|
4071
afl-fuzz.c
4071
afl-fuzz.c
File diff suppressed because it is too large
Load Diff
@ -252,6 +252,10 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USEMMAP
|
||||||
|
cc_params[cc_par_cnt++] = "-lrt";
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
if (!getenv("AFL_DONT_OPTIMIZE")) {
|
||||||
|
|
||||||
#if defined(__FreeBSD__) && defined(__x86_64__)
|
#if defined(__FreeBSD__) && defined(__x86_64__)
|
||||||
@ -304,6 +308,7 @@ int main(int argc, char** argv) {
|
|||||||
if (isatty(2) && !getenv("AFL_QUIET")) {
|
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||||
|
|
||||||
SAYF(cCYA "afl-cc" VERSION cRST " by <lcamtuf@google.com>\n");
|
SAYF(cCYA "afl-cc" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||||
|
SAYF(cYEL "[!] " cBRI "NOTE: " cRST "afl-gcc is deprecated, llvm_mode is much faster and has more options\n");
|
||||||
|
|
||||||
} else be_quiet = 1;
|
} else be_quiet = 1;
|
||||||
|
|
||||||
|
104
afl-showmap.c
104
afl-showmap.c
@ -28,6 +28,8 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "sharedmem.h"
|
||||||
|
#include "afl-common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -48,7 +50,7 @@
|
|||||||
|
|
||||||
static s32 child_pid; /* PID of the tested program */
|
static s32 child_pid; /* PID of the tested program */
|
||||||
|
|
||||||
static u8* trace_bits; /* SHM with instrumentation bitmap */
|
u8* trace_bits; /* SHM with instrumentation bitmap */
|
||||||
|
|
||||||
static u8 *out_file, /* Trace output file */
|
static u8 *out_file, /* Trace output file */
|
||||||
*doc_path, /* Path to docs */
|
*doc_path, /* Path to docs */
|
||||||
@ -59,8 +61,6 @@ static u32 exec_tmout; /* Exec timeout (ms) */
|
|||||||
|
|
||||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||||
|
|
||||||
static s32 shm_id; /* ID of the SHM region */
|
|
||||||
|
|
||||||
static u8 quiet_mode, /* Hide non-essential messages? */
|
static u8 quiet_mode, /* Hide non-essential messages? */
|
||||||
edges_only, /* Ignore hit counts? */
|
edges_only, /* Ignore hit counts? */
|
||||||
cmin_mode, /* Generate output in afl-cmin mode? */
|
cmin_mode, /* Generate output in afl-cmin mode? */
|
||||||
@ -126,39 +126,6 @@ static void classify_counts(u8* mem, const u8* map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get rid of shared memory (atexit handler). */
|
|
||||||
|
|
||||||
static void remove_shm(void) {
|
|
||||||
|
|
||||||
shmctl(shm_id, IPC_RMID, NULL);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Configure shared memory. */
|
|
||||||
|
|
||||||
static void setup_shm(void) {
|
|
||||||
|
|
||||||
u8* shm_str;
|
|
||||||
|
|
||||||
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
|
|
||||||
|
|
||||||
if (shm_id < 0) PFATAL("shmget() failed");
|
|
||||||
|
|
||||||
atexit(remove_shm);
|
|
||||||
|
|
||||||
shm_str = alloc_printf("%d", shm_id);
|
|
||||||
|
|
||||||
setenv(SHM_ENV_VAR, shm_str, 1);
|
|
||||||
|
|
||||||
ck_free(shm_str);
|
|
||||||
|
|
||||||
trace_bits = shmat(shm_id, NULL, 0);
|
|
||||||
|
|
||||||
if (!trace_bits) PFATAL("shmat() failed");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write results. */
|
/* Write results. */
|
||||||
|
|
||||||
static u32 write_results(void) {
|
static u32 write_results(void) {
|
||||||
@ -413,50 +380,6 @@ static void setup_signal_handlers(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Detect @@ in args. */
|
|
||||||
|
|
||||||
static void detect_file_args(char** argv) {
|
|
||||||
|
|
||||||
u32 i = 0;
|
|
||||||
u8* cwd = getcwd(NULL, 0);
|
|
||||||
|
|
||||||
if (!cwd) PFATAL("getcwd() failed");
|
|
||||||
|
|
||||||
while (argv[i]) {
|
|
||||||
|
|
||||||
u8* aa_loc = strstr(argv[i], "@@");
|
|
||||||
|
|
||||||
if (aa_loc) {
|
|
||||||
|
|
||||||
u8 *aa_subst, *n_arg;
|
|
||||||
|
|
||||||
if (!at_file) FATAL("@@ syntax is not supported by this tool.");
|
|
||||||
|
|
||||||
/* Be sure that we're always using fully-qualified paths. */
|
|
||||||
|
|
||||||
if (at_file[0] == '/') aa_subst = at_file;
|
|
||||||
else aa_subst = alloc_printf("%s/%s", cwd, at_file);
|
|
||||||
|
|
||||||
/* Construct a replacement argv value. */
|
|
||||||
|
|
||||||
*aa_loc = 0;
|
|
||||||
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
|
|
||||||
argv[i] = n_arg;
|
|
||||||
*aa_loc = '@';
|
|
||||||
|
|
||||||
if (at_file[0] != '/') ck_free(aa_subst);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
free(cwd); /* not tracked */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Show banner. */
|
/* Show banner. */
|
||||||
|
|
||||||
static void show_banner(void) {
|
static void show_banner(void) {
|
||||||
@ -481,7 +404,9 @@ static void usage(u8* argv0) {
|
|||||||
|
|
||||||
" -t msec - timeout for each run (none)\n"
|
" -t msec - timeout for each run (none)\n"
|
||||||
" -m megs - memory limit for child process (%u MB)\n"
|
" -m megs - memory limit for child process (%u MB)\n"
|
||||||
" -Q - use binary-only instrumentation (QEMU mode)\n\n"
|
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||||
|
" -U - use Unicorn-based instrumentation (Unicorn mode)\n"
|
||||||
|
" (Not necessary, here for consistency with other afl-* tools)\n\n"
|
||||||
|
|
||||||
"Other settings:\n\n"
|
"Other settings:\n\n"
|
||||||
|
|
||||||
@ -610,19 +535,18 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Main entry point */
|
/* Main entry point */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
s32 opt;
|
s32 opt;
|
||||||
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
|
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0, unicorn_mode = 0;
|
||||||
u32 tcnt;
|
u32 tcnt;
|
||||||
char** use_argv;
|
char** use_argv;
|
||||||
|
|
||||||
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
||||||
|
|
||||||
while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbc")) > 0)
|
while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQUbc")) > 0)
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
|
||||||
@ -719,6 +643,14 @@ int main(int argc, char** argv) {
|
|||||||
qemu_mode = 1;
|
qemu_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'U':
|
||||||
|
|
||||||
|
if (unicorn_mode) FATAL("Multiple -U options not supported");
|
||||||
|
if (!mem_limit_given) mem_limit = MEM_LIMIT_UNICORN;
|
||||||
|
|
||||||
|
unicorn_mode = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'b':
|
case 'b':
|
||||||
|
|
||||||
/* Secret undocumented mode. Writes output in raw binary format
|
/* Secret undocumented mode. Writes output in raw binary format
|
||||||
@ -741,7 +673,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if (optind == argc || !out_file) usage(argv[0]);
|
if (optind == argc || !out_file) usage(argv[0]);
|
||||||
|
|
||||||
setup_shm();
|
setup_shm(0);
|
||||||
setup_signal_handlers();
|
setup_signal_handlers();
|
||||||
|
|
||||||
set_up_environment();
|
set_up_environment();
|
||||||
@ -753,7 +685,7 @@ int main(int argc, char** argv) {
|
|||||||
ACTF("Executing '%s'...\n", target_path);
|
ACTF("Executing '%s'...\n", target_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
detect_file_args(argv + optind);
|
detect_file_args(argv + optind, at_file);
|
||||||
|
|
||||||
if (qemu_mode)
|
if (qemu_mode)
|
||||||
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo This reconfigures the system to have a better fuzzing performance
|
echo This reconfigures the system to have a better fuzzing performance
|
||||||
|
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
|
||||||
|
echo Error you need to be root to run this
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
sysctl -w kernel.core_pattern=core
|
sysctl -w kernel.core_pattern=core
|
||||||
sysctl -w kernel.randomize_va_space=0
|
sysctl -w kernel.randomize_va_space=0
|
||||||
sysctl -w kernel.sched_child_runs_first=1
|
sysctl -w kernel.sched_child_runs_first=1
|
||||||
@ -7,8 +11,11 @@ sysctl -w kernel.sched_autogroup_enabled=1
|
|||||||
sysctl -w kernel.sched_migration_cost_ns=50000000
|
sysctl -w kernel.sched_migration_cost_ns=50000000
|
||||||
sysctl -w kernel.sched_latency_ns=250000000
|
sysctl -w kernel.sched_latency_ns=250000000
|
||||||
echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
||||||
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor > /dev/null
|
test -e /sys/devices/system/cpu/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/scaling_governor
|
||||||
|
test -e /sys/devices/system/cpu/cpufreq/policy0/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpufreq/policy*/scaling_governor
|
||||||
|
test -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||||
test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
|
test -e /sys/devices/system/cpu/intel_pstate/no_turbo && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
|
||||||
|
test -e /sys/devices/system/cpu/cpufreq/boost && echo 1 > /sys/devices/system/cpu/cpufreq/boost
|
||||||
echo
|
echo
|
||||||
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
|
||||||
echo '/etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"'
|
echo '/etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"'
|
||||||
|
109
afl-tmin.c
109
afl-tmin.c
@ -26,6 +26,8 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "alloc-inl.h"
|
#include "alloc-inl.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "sharedmem.h"
|
||||||
|
#include "afl-common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -50,8 +52,8 @@ static s32 forksrv_pid, /* PID of the fork server */
|
|||||||
static s32 fsrv_ctl_fd, /* Fork server control pipe (write) */
|
static s32 fsrv_ctl_fd, /* Fork server control pipe (write) */
|
||||||
fsrv_st_fd; /* Fork server status pipe (read) */
|
fsrv_st_fd; /* Fork server status pipe (read) */
|
||||||
|
|
||||||
static u8 *trace_bits, /* SHM with instrumentation bitmap */
|
u8 *trace_bits; /* SHM with instrumentation bitmap */
|
||||||
*mask_bitmap; /* Mask for trace bits (-B) */
|
static u8 *mask_bitmap; /* Mask for trace bits (-B) */
|
||||||
|
|
||||||
static u8 *in_file, /* Minimizer input test case */
|
static u8 *in_file, /* Minimizer input test case */
|
||||||
*out_file, /* Minimizer output file */
|
*out_file, /* Minimizer output file */
|
||||||
@ -73,8 +75,7 @@ static u32 in_len, /* Input data length */
|
|||||||
|
|
||||||
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
|
||||||
|
|
||||||
static s32 shm_id, /* ID of the SHM region */
|
static s32 dev_null_fd = -1; /* FD to /dev/null */
|
||||||
dev_null_fd = -1; /* FD to /dev/null */
|
|
||||||
|
|
||||||
static u8 crash_mode, /* Crash-centric mode? */
|
static u8 crash_mode, /* Crash-centric mode? */
|
||||||
exit_crash, /* Treat non-zero exit as crash? */
|
exit_crash, /* Treat non-zero exit as crash? */
|
||||||
@ -159,42 +160,12 @@ static inline u8 anything_set(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get rid of temp files (atexit handler). */
|
||||||
|
|
||||||
/* Get rid of shared memory and temp files (atexit handler). */
|
static void at_exit_handler(void) {
|
||||||
|
|
||||||
static void remove_shm(void) {
|
|
||||||
|
|
||||||
if (prog_in) unlink(prog_in); /* Ignore errors */
|
if (prog_in) unlink(prog_in); /* Ignore errors */
|
||||||
shmctl(shm_id, IPC_RMID, NULL);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Configure shared memory. */
|
|
||||||
|
|
||||||
static void setup_shm(void) {
|
|
||||||
|
|
||||||
u8* shm_str;
|
|
||||||
|
|
||||||
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
|
|
||||||
|
|
||||||
if (shm_id < 0) PFATAL("shmget() failed");
|
|
||||||
|
|
||||||
atexit(remove_shm);
|
|
||||||
|
|
||||||
shm_str = alloc_printf("%d", shm_id);
|
|
||||||
|
|
||||||
setenv(SHM_ENV_VAR, shm_str, 1);
|
|
||||||
|
|
||||||
ck_free(shm_str);
|
|
||||||
|
|
||||||
trace_bits = shmat(shm_id, NULL, 0);
|
|
||||||
|
|
||||||
if (!trace_bits) PFATAL("shmat() failed");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Read initial file. */
|
/* Read initial file. */
|
||||||
|
|
||||||
static void read_initial_file(void) {
|
static void read_initial_file(void) {
|
||||||
@ -700,7 +671,7 @@ next_del_blksize:
|
|||||||
alpha_del1 = 0;
|
alpha_del1 = 0;
|
||||||
syms_removed = 0;
|
syms_removed = 0;
|
||||||
|
|
||||||
memset(alpha_map, 0, 256 * sizeof(u32));
|
memset(alpha_map, 0, sizeof(alpha_map));
|
||||||
|
|
||||||
for (i = 0; i < in_len; i++) {
|
for (i = 0; i < in_len; i++) {
|
||||||
if (!alpha_map[in_data[i]]) alpha_size++;
|
if (!alpha_map[in_data[i]]) alpha_size++;
|
||||||
@ -911,48 +882,6 @@ static void setup_signal_handlers(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Detect @@ in args. */
|
|
||||||
|
|
||||||
static void detect_file_args(char** argv) {
|
|
||||||
|
|
||||||
u32 i = 0;
|
|
||||||
u8* cwd = getcwd(NULL, 0);
|
|
||||||
|
|
||||||
if (!cwd) PFATAL("getcwd() failed");
|
|
||||||
|
|
||||||
while (argv[i]) {
|
|
||||||
|
|
||||||
u8* aa_loc = strstr(argv[i], "@@");
|
|
||||||
|
|
||||||
if (aa_loc) {
|
|
||||||
|
|
||||||
u8 *aa_subst, *n_arg;
|
|
||||||
|
|
||||||
/* Be sure that we're always using fully-qualified paths. */
|
|
||||||
|
|
||||||
if (prog_in[0] == '/') aa_subst = prog_in;
|
|
||||||
else aa_subst = alloc_printf("%s/%s", cwd, prog_in);
|
|
||||||
|
|
||||||
/* Construct a replacement argv value. */
|
|
||||||
|
|
||||||
*aa_loc = 0;
|
|
||||||
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
|
|
||||||
argv[i] = n_arg;
|
|
||||||
*aa_loc = '@';
|
|
||||||
|
|
||||||
if (prog_in[0] != '/') ck_free(aa_subst);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
free(cwd); /* not tracked */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Display usage hints. */
|
/* Display usage hints. */
|
||||||
|
|
||||||
static void usage(u8* argv0) {
|
static void usage(u8* argv0) {
|
||||||
@ -969,7 +898,9 @@ static void usage(u8* argv0) {
|
|||||||
" -f file - input file read by the tested program (stdin)\n"
|
" -f file - input file read by the tested program (stdin)\n"
|
||||||
" -t msec - timeout for each run (%u ms)\n"
|
" -t msec - timeout for each run (%u ms)\n"
|
||||||
" -m megs - memory limit for child process (%u MB)\n"
|
" -m megs - memory limit for child process (%u MB)\n"
|
||||||
" -Q - use binary-only instrumentation (QEMU mode)\n\n"
|
" -Q - use binary-only instrumentation (QEMU mode)\n"
|
||||||
|
" -U - use Unicorn-based instrumentation (Unicorn mode)\n\n"
|
||||||
|
" (Not necessary, here for consistency with other afl-* tools)\n\n"
|
||||||
|
|
||||||
"Minimization settings:\n\n"
|
"Minimization settings:\n\n"
|
||||||
|
|
||||||
@ -1096,7 +1027,6 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read mask bitmap from file. This is for the -B option. */
|
/* Read mask bitmap from file. This is for the -B option. */
|
||||||
|
|
||||||
static void read_bitmap(u8* fname) {
|
static void read_bitmap(u8* fname) {
|
||||||
@ -1118,14 +1048,14 @@ static void read_bitmap(u8* fname) {
|
|||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
s32 opt;
|
s32 opt;
|
||||||
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
|
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0, unicorn_mode = 0;
|
||||||
char** use_argv;
|
char** use_argv;
|
||||||
|
|
||||||
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
|
||||||
|
|
||||||
SAYF(cCYA "afl-tmin" VERSION cRST " by <lcamtuf@google.com>\n");
|
SAYF(cCYA "afl-tmin" VERSION cRST " by <lcamtuf@google.com>\n");
|
||||||
|
|
||||||
while ((opt = getopt(argc,argv,"+i:o:f:m:t:B:xeQ")) > 0)
|
while ((opt = getopt(argc,argv,"+i:o:f:m:t:B:xeQU")) > 0)
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
|
||||||
@ -1217,6 +1147,14 @@ int main(int argc, char** argv) {
|
|||||||
qemu_mode = 1;
|
qemu_mode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'U':
|
||||||
|
|
||||||
|
if (unicorn_mode) FATAL("Multiple -Q options not supported");
|
||||||
|
if (!mem_limit_given) mem_limit = MEM_LIMIT_UNICORN;
|
||||||
|
|
||||||
|
unicorn_mode = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'B': /* load bitmap */
|
case 'B': /* load bitmap */
|
||||||
|
|
||||||
/* This is a secret undocumented option! It is speculated to be useful
|
/* This is a secret undocumented option! It is speculated to be useful
|
||||||
@ -1245,13 +1183,14 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if (optind == argc || !in_file || !out_file) usage(argv[0]);
|
if (optind == argc || !in_file || !out_file) usage(argv[0]);
|
||||||
|
|
||||||
setup_shm();
|
setup_shm(0);
|
||||||
|
atexit(at_exit_handler);
|
||||||
setup_signal_handlers();
|
setup_signal_handlers();
|
||||||
|
|
||||||
set_up_environment();
|
set_up_environment();
|
||||||
|
|
||||||
find_binary(argv[optind]);
|
find_binary(argv[optind]);
|
||||||
detect_file_args(argv + optind);
|
detect_file_args(argv + optind, prog_in);
|
||||||
|
|
||||||
if (qemu_mode)
|
if (qemu_mode)
|
||||||
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
|
||||||
|
12
alloc-inl.h
12
alloc-inl.h
@ -76,6 +76,17 @@
|
|||||||
|
|
||||||
/* Sanity-checking macros for pointers. */
|
/* Sanity-checking macros for pointers. */
|
||||||
|
|
||||||
|
#define CHECK_PTR(_p) do { \
|
||||||
|
if (_p) { \
|
||||||
|
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
||||||
|
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
|
||||||
|
ABORT("Use after free."); \
|
||||||
|
else ABORT("Corrupted head alloc canary."); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
#define CHECK_PTR(_p) do { \
|
#define CHECK_PTR(_p) do { \
|
||||||
if (_p) { \
|
if (_p) { \
|
||||||
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
|
||||||
@ -87,6 +98,7 @@
|
|||||||
ABORT("Corrupted tail alloc canary."); \
|
ABORT("Corrupted tail alloc canary."); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
*/
|
||||||
|
|
||||||
#define CHECK_PTR_EXPR(_p) ({ \
|
#define CHECK_PTR_EXPR(_p) ({ \
|
||||||
typeof (_p) _tmp = (_p); \
|
typeof (_p) _tmp = (_p); \
|
||||||
|
7
config.h
7
config.h
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
/* Version string: */
|
/* Version string: */
|
||||||
|
|
||||||
#define VERSION "++2.52d"
|
#define VERSION "++2.53d" // c = release, d = volatile github dev
|
||||||
|
|
||||||
/******************************************************
|
/******************************************************
|
||||||
* *
|
* *
|
||||||
@ -59,6 +59,10 @@
|
|||||||
|
|
||||||
#define MEM_LIMIT_QEMU 200
|
#define MEM_LIMIT_QEMU 200
|
||||||
|
|
||||||
|
/* Default memory limit when running in Unicorn mode (MB): */
|
||||||
|
|
||||||
|
#define MEM_LIMIT_UNICORN 200
|
||||||
|
|
||||||
/* Number of calibration cycles per every new test case (and for test
|
/* Number of calibration cycles per every new test case (and for test
|
||||||
cases that show variable behavior): */
|
cases that show variable behavior): */
|
||||||
|
|
||||||
@ -83,6 +87,7 @@
|
|||||||
of 32-bit int overflows): */
|
of 32-bit int overflows): */
|
||||||
|
|
||||||
#define HAVOC_MAX_MULT 16
|
#define HAVOC_MAX_MULT 16
|
||||||
|
#define HAVOC_MAX_MULT_MOPT 32
|
||||||
|
|
||||||
/* Absolute minimum number of havoc cycles (after all adjustments): */
|
/* Absolute minimum number of havoc cycles (after all adjustments): */
|
||||||
|
|
||||||
|
@ -13,10 +13,39 @@ Want to stay in the loop on major new features? Join our mailing list by
|
|||||||
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
sending a mail to <afl-users+subscribe@googlegroups.com>.
|
||||||
|
|
||||||
|
|
||||||
-----------------------------
|
----------------------
|
||||||
Version ++2.52d (tbd):
|
Version ++2.53d (dev):
|
||||||
-----------------------------
|
----------------------
|
||||||
|
|
||||||
|
- ... your patch? :)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
Version ++2.53c (release):
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
- README is now README.md
|
||||||
|
- imported the few minor changes from the 2.53b release
|
||||||
|
- unicorn_mode got added - thanks to domenukk for the patch!
|
||||||
|
- fix llvm_mode AFL_TRACE_PC with modern llvm
|
||||||
|
- fix a crash in qemu_mode which also exists in stock afl
|
||||||
|
- added libcompcov, a laf-intel implementation for qemu! :)
|
||||||
|
see qemu_mode/libcompcov/README.libcompcov
|
||||||
|
- afl-fuzz now displays the selected core in the status screen (blue {#})
|
||||||
|
- updated afl-fuzz and afl-system-config for new scaling governor location
|
||||||
|
in modern kernels
|
||||||
|
- using the old ineffective afl-gcc will now show a deprecation warning
|
||||||
|
- all queue, hang and crash files now have their discovery time in their name
|
||||||
|
- if llvm_mode was compiled, afl-clang/afl-clang++ will point to these
|
||||||
|
instead of afl-gcc
|
||||||
|
- added instrim, a much faster llvm_mode instrumentation at the cost of
|
||||||
|
path discovery. See llvm_mode/README.instrim (https://github.com/csienslab/instrim)
|
||||||
|
- added MOpt (github.com/puppet-meteor/MOpt-AFL) mode, see docs/README.MOpt
|
||||||
|
- added code to make it more portable to other platforms than Intel Linux
|
||||||
|
- added never zero counters for afl-gcc and optionally (because of an
|
||||||
|
optimization issue in llvm < 9) for llvm_mode (AFL_LLVM_NEVER_ZERO=1)
|
||||||
|
- added a new doc about binary only fuzzing: docs/binaryonly_fuzzing.txt
|
||||||
- more cpu power for afl-system-config
|
- more cpu power for afl-system-config
|
||||||
- added forkserver patch to afl-tmin, makes it much faster (originally from
|
- added forkserver patch to afl-tmin, makes it much faster (originally from
|
||||||
github.com/nccgroup/TriforceAFL)
|
github.com/nccgroup/TriforceAFL)
|
||||||
@ -27,11 +56,13 @@ Version ++2.52d (tbd):
|
|||||||
see docs/python_mutators.txt (originally by choller@mozilla)
|
see docs/python_mutators.txt (originally by choller@mozilla)
|
||||||
- added AFL_CAL_FAST for slow applications and AFL_DEBUG_CHILD_OUTPUT for
|
- added AFL_CAL_FAST for slow applications and AFL_DEBUG_CHILD_OUTPUT for
|
||||||
debugging
|
debugging
|
||||||
- added a -s seed switch to allow afl run with a fixed initial
|
- added -V time and -E execs option to better comparison runs, runs afl-fuzz
|
||||||
seed that is not updated. this is good for performance and path discovery
|
for a specific time/executions.
|
||||||
|
- added a -s seed switch to allow afl run with a fixed initial
|
||||||
|
seed that is not updated. This is good for performance and path discovery
|
||||||
tests as the random numbers are deterministic then
|
tests as the random numbers are deterministic then
|
||||||
- ... your idea or patch?
|
- llvm_mode LAF_... env variables can now be specified as AFL_LLVM_LAF_...
|
||||||
|
that is longer but in line with other llvm specific env vars
|
||||||
|
|
||||||
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@ -41,7 +72,7 @@ Version ++2.52c (2019-06-05):
|
|||||||
- Applied community patches. See docs/PATCHES for the full list.
|
- Applied community patches. See docs/PATCHES for the full list.
|
||||||
LLVM and Qemu modes are now faster.
|
LLVM and Qemu modes are now faster.
|
||||||
Important changes:
|
Important changes:
|
||||||
afl-fuzz: -e EXTENSION commandline option
|
afl-fuzz: -e EXTENSION commandline option
|
||||||
llvm_mode: LAF-intel performance (needs activation, see llvm/README.laf-intel)
|
llvm_mode: LAF-intel performance (needs activation, see llvm/README.laf-intel)
|
||||||
a few new environment variables for afl-fuzz, llvm and qemu, see docs/env_variables.txt
|
a few new environment variables for afl-fuzz, llvm and qemu, see docs/env_variables.txt
|
||||||
- Added the power schedules of AFLfast by Marcel Boehme, but set the default
|
- Added the power schedules of AFLfast by Marcel Boehme, but set the default
|
||||||
|
@ -17,6 +17,9 @@ afl-qemu-optimize-entrypoint.diff by mh(at)mh-sec(dot)de
|
|||||||
afl-qemu-speed.diff by abiondo on github
|
afl-qemu-speed.diff by abiondo on github
|
||||||
afl-qemu-optimize-map.diff by mh(at)mh-sec(dot)de
|
afl-qemu-optimize-map.diff by mh(at)mh-sec(dot)de
|
||||||
|
|
||||||
|
+ unicorn_mode (modernized and updated by domenukk)
|
||||||
|
+ instrim (https://github.com/csienslab/instrim) was integrated
|
||||||
|
+ MOpt (github.com/puppet-meteor/MOpt-AFL) was imported
|
||||||
+ AFLfast additions (github.com/mboehme/aflfast) were incorporated.
|
+ AFLfast additions (github.com/mboehme/aflfast) were incorporated.
|
||||||
+ Qemu 3.1 upgrade with enhancement patches (github.com/andreafioraldi/afl)
|
+ Qemu 3.1 upgrade with enhancement patches (github.com/andreafioraldi/afl)
|
||||||
+ Python mutator modules support (github.com/choller/afl)
|
+ Python mutator modules support (github.com/choller/afl)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
AFL quick start guide
|
AFL quick start guide
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
You should read docs/README. It's pretty short. If you really can't, here's
|
You should read docs/README.md - it's pretty short. If you really can't, here's
|
||||||
how to hit the ground running:
|
how to hit the ground running:
|
||||||
|
|
||||||
1) Compile AFL with 'make'. If build fails, see docs/INSTALL for tips.
|
1) Compile AFL with 'make'. If build fails, see docs/INSTALL for tips.
|
||||||
@ -17,7 +17,7 @@ how to hit the ground running:
|
|||||||
|
|
||||||
The program must crash properly when a fault is encountered. Watch out for
|
The program must crash properly when a fault is encountered. Watch out for
|
||||||
custom SIGSEGV or SIGABRT handlers and background processes. For tips on
|
custom SIGSEGV or SIGABRT handlers and background processes. For tips on
|
||||||
detecting non-crashing flaws, see section 11 in docs/README.
|
detecting non-crashing flaws, see section 11 in docs/README.md .
|
||||||
|
|
||||||
3) Compile the program / library to be fuzzed using afl-gcc. A common way to
|
3) Compile the program / library to be fuzzed using afl-gcc. A common way to
|
||||||
do this would be:
|
do this would be:
|
||||||
@ -48,7 +48,7 @@ how to hit the ground running:
|
|||||||
That's it. Sit back, relax, and - time permitting - try to skim through the
|
That's it. Sit back, relax, and - time permitting - try to skim through the
|
||||||
following files:
|
following files:
|
||||||
|
|
||||||
- docs/README - A general introduction to AFL,
|
- docs/README.md - A general introduction to AFL,
|
||||||
- docs/perf_tips.txt - Simple tips on how to fuzz more quickly,
|
- docs/perf_tips.txt - Simple tips on how to fuzz more quickly,
|
||||||
- docs/status_screen.txt - An explanation of the tidbits shown in the UI,
|
- docs/status_screen.txt - An explanation of the tidbits shown in the UI,
|
||||||
- docs/parallel_fuzzing.txt - Advice on running AFL on multiple cores.
|
- docs/parallel_fuzzing.txt - Advice on running AFL on multiple cores.
|
||||||
|
51
docs/README.MOpt
Normal file
51
docs/README.MOpt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# MOpt(imized) AFL by <puppet@zju.edu.cn>
|
||||||
|
|
||||||
|
### 1. Description
|
||||||
|
MOpt-AFL is a AFL-based fuzzer that utilizes a customized Particle Swarm
|
||||||
|
Optimization (PSO) algorithm to find the optimal selection probability
|
||||||
|
distribution of operators with respect to fuzzing effectiveness.
|
||||||
|
More details can be found in the technical report.
|
||||||
|
|
||||||
|
### 2. Cite Information
|
||||||
|
Chenyang Lyu, Shouling Ji, Chao Zhang, Yuwei Li, Wei-Han Lee, Yu Song and
|
||||||
|
Raheem Beyah, MOPT: Optimized Mutation Scheduling for Fuzzers,
|
||||||
|
USENIX Security 2019.
|
||||||
|
|
||||||
|
### 3. Seed Sets
|
||||||
|
We open source all the seed sets used in the paper
|
||||||
|
"MOPT: Optimized Mutation Scheduling for Fuzzers".
|
||||||
|
|
||||||
|
### 4. Experiment Results
|
||||||
|
The experiment results can be found in
|
||||||
|
https://drive.google.com/drive/folders/184GOzkZGls1H2NuLuUfSp9gfqp1E2-lL?usp=sharing.
|
||||||
|
We only open source the crash files since the space is limited.
|
||||||
|
|
||||||
|
### 5. Technical Report
|
||||||
|
MOpt_TechReport.pdf is the technical report of the paper
|
||||||
|
"MOPT: Optimized Mutation Scheduling for Fuzzers", which contains more deatails.
|
||||||
|
|
||||||
|
### 6. Parameter Introduction
|
||||||
|
Most important, you must add the parameter `-L` (e.g., `-L 0`) to launch the
|
||||||
|
MOpt scheme.
|
||||||
|
|
||||||
|
Option '-L' controls the time to move on to the pacemaker fuzzing mode.
|
||||||
|
'-L t': when MOpt-AFL finishes the mutation of one input, if it has not
|
||||||
|
discovered any new unique crash or path for more than t minutes, MOpt-AFL will
|
||||||
|
enter the pacemaker fuzzing mode.
|
||||||
|
|
||||||
|
Setting 0 will enter the pacemaker fuzzing mode at first, which is
|
||||||
|
recommended in a short time-scale evaluation.
|
||||||
|
|
||||||
|
Other important parameters can be found in afl-fuzz.c, for instance,
|
||||||
|
|
||||||
|
'swarm_num': the number of the PSO swarms used in the fuzzing process.
|
||||||
|
'period_pilot': how many times MOpt-AFL will execute the target program
|
||||||
|
in the pilot fuzzing module, then it will enter the core fuzzing module.
|
||||||
|
'period_core': how many times MOpt-AFL will execute the target program in the
|
||||||
|
core fuzzing module, then it will enter the PSO updating module.
|
||||||
|
'limit_time_bound': control how many interesting test cases need to be found
|
||||||
|
before MOpt-AFL quits the pacemaker fuzzing mode and reuses the deterministic stage.
|
||||||
|
0 < 'limit_time_bound' < 1, MOpt-AFL-tmp.
|
||||||
|
'limit_time_bound' >= 1, MOpt-AFL-ever.
|
||||||
|
|
||||||
|
Have fun with MOpt in AFL!
|
1
docs/README.md
Symbolic link
1
docs/README.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../README.md
|
140
docs/binaryonly_fuzzing.txt
Normal file
140
docs/binaryonly_fuzzing.txt
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
|
||||||
|
Fuzzing binary-only programs with afl++
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
afl++, libfuzzer and others are great if you have the source code, and
|
||||||
|
it allows for very fast and coverage guided fuzzing.
|
||||||
|
|
||||||
|
However, if there is only the binary program and not source code available,
|
||||||
|
then standard afl++ (dumb mode) is not effective.
|
||||||
|
|
||||||
|
The following is a description of how these can be fuzzed with afl++
|
||||||
|
|
||||||
|
!!!!!
|
||||||
|
TL;DR: try DYNINST with afl-dyninst. If it produces too many crashes then
|
||||||
|
use afl -Q qemu_mode, or better: use both in parallel.
|
||||||
|
!!!!!
|
||||||
|
|
||||||
|
|
||||||
|
QEMU
|
||||||
|
----
|
||||||
|
Qemu is the "native" solution to the program.
|
||||||
|
It is available in the ./qemu_mode/ directory and once compiled it can
|
||||||
|
be accessed by the afl-fuzz -Q command line option.
|
||||||
|
The speed decrease is at about 50%
|
||||||
|
It is the easiest to use alternative and even works for cross-platform binaries.
|
||||||
|
|
||||||
|
As it is included in afl++ this needs no URL.
|
||||||
|
|
||||||
|
|
||||||
|
UNICORN
|
||||||
|
-------
|
||||||
|
Unicorn is a fork of QEMU. The instrumentation is, therefore, very similar.
|
||||||
|
In contrast to QEMU, Unicorn does not offer a full system or even userland emulation.
|
||||||
|
Runtime environment and/or loaders have to be written from scratch, if needed.
|
||||||
|
On top, block chaining has been removed. This means the speed boost introduced in
|
||||||
|
to the patched QEMU Mode of afl++ cannot simply be ported over to Unicorn.
|
||||||
|
For further information, check out ./unicorn_mode.txt.
|
||||||
|
|
||||||
|
|
||||||
|
DYNINST
|
||||||
|
-------
|
||||||
|
Dyninst is a binary instrumentation framework similar to Pintool and Dynamorio
|
||||||
|
(see far below). However whereas Pintool and Dynamorio work at runtime, dyninst
|
||||||
|
instruments the target at load time, and then let it run.
|
||||||
|
This is great for some things, e.g. fuzzing, and not so effective for others,
|
||||||
|
e.g. malware analysis.
|
||||||
|
|
||||||
|
So what we can do with dyninst is taking every basic block, and put afl's
|
||||||
|
instrumention code in there - and then save the binary.
|
||||||
|
Afterwards we can just fuzz the newly saved target binary with afl-fuzz.
|
||||||
|
Sounds great? It is. The issue though - it is a non-trivial problem to
|
||||||
|
insert instructions, which change addresses in the process space, so
|
||||||
|
everything is still working afterwards. Hence more often than not binaries
|
||||||
|
crash when they are run (because of instrumentation).
|
||||||
|
|
||||||
|
The speed decrease is about 15-35%, depending on the optimization options
|
||||||
|
used with afl-dyninst.
|
||||||
|
|
||||||
|
So if dyninst works, it is the best option available. Otherwise it just doesn't
|
||||||
|
work well.
|
||||||
|
|
||||||
|
https://github.com/vanhauser-thc/afl-dyninst
|
||||||
|
|
||||||
|
|
||||||
|
INTEL-PT
|
||||||
|
--------
|
||||||
|
If you have a newer Intel CPU, you can make use of Intels processor trace.
|
||||||
|
The big issue with Intel's PT is the small buffer size and the complex
|
||||||
|
encoding of the debug information collected through PT.
|
||||||
|
This makes the decoding very CPU intensive and hence slow.
|
||||||
|
As a result, the overall speed decrease is about 70-90% (depending on
|
||||||
|
the implementation and other factors).
|
||||||
|
|
||||||
|
There are two afl intel-pt implementations:
|
||||||
|
|
||||||
|
1. https://github.com/junxzm1990/afl-pt
|
||||||
|
=> this needs Ubuntu 14.04.05 without any updates and the 4.4 kernel.
|
||||||
|
|
||||||
|
2. https://github.com/hunter-ht-2018/ptfuzzer
|
||||||
|
=> this needs a 4.14 or 4.15 kernel. the "nopti" kernel boot option must
|
||||||
|
be used. This one is faster than the other.
|
||||||
|
|
||||||
|
|
||||||
|
CORESIGHT
|
||||||
|
---------
|
||||||
|
|
||||||
|
Coresight is ARM's answer to Intel's PT.
|
||||||
|
There is no implementation so far which handle coresight and getting
|
||||||
|
it working on an ARM Linux is very difficult due to custom kernel building
|
||||||
|
on embedded systems is difficult. And finding one that has coresight in
|
||||||
|
the ARM chip is difficult too.
|
||||||
|
My guess is that it is slower than Qemu, but faster than Intel PT.
|
||||||
|
If anyone finds any coresight implementation for afl please ping me:
|
||||||
|
vh@thc.org
|
||||||
|
|
||||||
|
|
||||||
|
PIN & DYNAMORIO
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Pintool and Dynamorio are dynamic instrumentation engines, and they can be
|
||||||
|
used for getting basic block information at runtime.
|
||||||
|
Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows
|
||||||
|
whereas Dynamorio is additionally available for ARM and AARCH64.
|
||||||
|
Dynamorio is also 10x faster than Pintool.
|
||||||
|
|
||||||
|
The big issue with Dynamorio (and therefore Pintool too) is speed.
|
||||||
|
Dynamorio has a speed decrease of 98-99%
|
||||||
|
Pintool has a speed decrease of 99.5%
|
||||||
|
|
||||||
|
Hence Dynamorio is the option to go for if everything fails, and Pintool
|
||||||
|
only if Dynamorio fails too.
|
||||||
|
|
||||||
|
Dynamorio solutions:
|
||||||
|
https://github.com/vanhauser-thc/afl-dynamorio
|
||||||
|
https://github.com/mxmssh/drAFL
|
||||||
|
https://github.com/googleprojectzero/winafl/ <= very good but windows only
|
||||||
|
|
||||||
|
Pintool solutions:
|
||||||
|
https://github.com/vanhauser-thc/afl-pin
|
||||||
|
https://github.com/mothran/aflpin
|
||||||
|
https://github.com/spinpx/afl_pin_mode <= only old Pintool version supported
|
||||||
|
|
||||||
|
|
||||||
|
Non-AFL solutions
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
There are many binary-only fuzzing frameworks. Some are great for CTFs but don't
|
||||||
|
work with large binaries, others are very slow but have good path discovery,
|
||||||
|
some are very hard to set-up ...
|
||||||
|
|
||||||
|
QSYM: https://github.com/sslab-gatech/qsym
|
||||||
|
Manticore: https://github.com/trailofbits/manticore
|
||||||
|
S2E: https://github.com/S2E
|
||||||
|
<please send me any missing that are good>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
That's it!
|
||||||
|
News, corrections, updates?
|
||||||
|
Email vh@thc.org
|
@ -7,8 +7,8 @@ Environmental variables
|
|||||||
users or for some types of custom fuzzing setups. See README for the general
|
users or for some types of custom fuzzing setups. See README for the general
|
||||||
instruction manual.
|
instruction manual.
|
||||||
|
|
||||||
1) Settings for afl-gcc, afl-clang, and afl-as
|
1) Settings for afl-gcc, afl-clang, and afl-as - and gcc_plugin afl-gcc-fast
|
||||||
----------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
Because they can't directly accept command-line options, the compile-time
|
Because they can't directly accept command-line options, the compile-time
|
||||||
tools make fairly broad use of environmental variables:
|
tools make fairly broad use of environmental variables:
|
||||||
@ -82,18 +82,22 @@ discussed in section #1, with the exception of:
|
|||||||
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
|
- TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are
|
||||||
created.
|
created.
|
||||||
|
|
||||||
|
- AFL_INST_RATIO, as we switched for instrim instrumentation which
|
||||||
|
is more effective but makes not much sense together with this option.
|
||||||
|
|
||||||
Then there are a few specific features that are only available in llvm_mode:
|
Then there are a few specific features that are only available in llvm_mode:
|
||||||
|
|
||||||
LAF-INTEL
|
LAF-INTEL
|
||||||
=========
|
=========
|
||||||
This great feature will split compares to series of single byte comparisons
|
This great feature will split compares to series of single byte comparisons
|
||||||
to allow afl-fuzz to find otherwise rather impossible paths.
|
to allow afl-fuzz to find otherwise rather impossible paths. It is not
|
||||||
|
restricted to Intel CPUs ;-)
|
||||||
|
|
||||||
- Setting LAF_SPLIT_SWITCHES will split switch()es
|
- Setting AFL_LLVM_LAF_SPLIT_SWITCHES will split switch()es
|
||||||
|
|
||||||
- Setting LAF_TRANSFORM_COMPARES will split string compare functions
|
- Setting AFL_LLVM_LAF_TRANSFORM_COMPARES will split string compare functions
|
||||||
|
|
||||||
- Setting LAF_SPLIT_COMPARES will split > 8 bit CMP instructions
|
- Setting AFL_LLVM_LAF_SPLIT_COMPARES will split > 8 bit CMP instructions
|
||||||
|
|
||||||
See llvm_mode/README.laf-intel for more information.
|
See llvm_mode/README.laf-intel for more information.
|
||||||
|
|
||||||
@ -102,13 +106,33 @@ Then there are a few specific features that are only available in llvm_mode:
|
|||||||
This feature allows selectively instrumentation of the source
|
This feature allows selectively instrumentation of the source
|
||||||
|
|
||||||
- Setting AFL_LLVM_WHITELIST with a filename will only instrument those
|
- Setting AFL_LLVM_WHITELIST with a filename will only instrument those
|
||||||
files that match these names.
|
files that match the names listed in this file.
|
||||||
|
|
||||||
See llvm_mode/README.whitelist for more information.
|
See llvm_mode/README.whitelist for more information.
|
||||||
|
|
||||||
Note that AFL_INST_RATIO will behave a bit differently than for afl-gcc,
|
INSTRIM
|
||||||
because functions are *not* instrumented unconditionally - so low values
|
=======
|
||||||
will have a more striking effect. For this tool, 0 is not a valid choice.
|
This feature increases the speed by whopping 20% but at the cost of a
|
||||||
|
lower path discovery and therefore coverage.
|
||||||
|
|
||||||
|
- Setting AFL_LLVM_INSTRIM activates this mode
|
||||||
|
|
||||||
|
- Setting AFL_LLVM_INSTRIM_LOOPHEAD=1 expands on INSTRIM to optimize loops.
|
||||||
|
afl-fuzz will only be able to see the path the loop took, but not how
|
||||||
|
many times it was called (unless it is a complex loop).
|
||||||
|
|
||||||
|
See llvm_mode/README.instrim
|
||||||
|
|
||||||
|
NOT_ZERO
|
||||||
|
========
|
||||||
|
|
||||||
|
- Setting AFL_LLVM_NOT_ZERO=1 during compilation will use counters
|
||||||
|
that skip zero on overflow. This is the default for llvm >= 9,
|
||||||
|
however for llvm versions below that this will increase an unnecessary
|
||||||
|
slowdown due a performance issue that is only fixed in llvm 9+.
|
||||||
|
This feature increases path discovery by a little bit.
|
||||||
|
|
||||||
|
See llvm_mode/README.neverzero
|
||||||
|
|
||||||
3) Settings for afl-fuzz
|
3) Settings for afl-fuzz
|
||||||
------------------------
|
------------------------
|
||||||
@ -220,6 +244,10 @@ The QEMU wrapper used to instrument binary-only code supports several settings:
|
|||||||
|
|
||||||
- Setting AFL_INST_LIBS causes the translator to also instrument the code
|
- Setting AFL_INST_LIBS causes the translator to also instrument the code
|
||||||
inside any dynamically linked libraries (notably including glibc).
|
inside any dynamically linked libraries (notably including glibc).
|
||||||
|
|
||||||
|
- Setting AFL_QEMU_COMPCOV enables the CompareCoverage tracing of all
|
||||||
|
cmp and sub in x86 and x86_64. Support for other architectures and
|
||||||
|
comparison functions (mem/strcmp et al.) is planned.
|
||||||
|
|
||||||
- The underlying QEMU binary will recognize any standard "user space
|
- The underlying QEMU binary will recognize any standard "user space
|
||||||
emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no
|
emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no
|
||||||
|
@ -6,6 +6,10 @@ Sister projects
|
|||||||
designed for, or meant to integrate with AFL. See README for the general
|
designed for, or meant to integrate with AFL. See README for the general
|
||||||
instruction manual.
|
instruction manual.
|
||||||
|
|
||||||
|
!!!
|
||||||
|
!!! This list is outdated and needs an update, missing: e.g. Angora, FairFuzz
|
||||||
|
!!!
|
||||||
|
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
Support for other languages / environments:
|
Support for other languages / environments:
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
@ -263,7 +267,7 @@ Static binary-only instrumentation (Aleksandar Nikolich)
|
|||||||
reports better performance compared to QEMU, but occasional translation
|
reports better performance compared to QEMU, but occasional translation
|
||||||
errors with stripped binaries.
|
errors with stripped binaries.
|
||||||
|
|
||||||
https://github.com/vrtadmin/moflow/tree/master/afl-dyninst
|
https://github.com/vanhauser-thc/afl-dyninst
|
||||||
|
|
||||||
AFL PIN (Parker Thompson)
|
AFL PIN (Parker Thompson)
|
||||||
-------------------------
|
-------------------------
|
||||||
|
107
docs/unicorn_mode.txt
Normal file
107
docs/unicorn_mode.txt
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
=========================================================
|
||||||
|
Unicorn-based binary-only instrumentation for afl-fuzz
|
||||||
|
=========================================================
|
||||||
|
|
||||||
|
1) Introduction
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The code in ./unicorn_mode allows you to build a standalone feature that
|
||||||
|
leverages the Unicorn Engine and allows callers to obtain instrumentation
|
||||||
|
output for black-box, closed-source binary code snippets. This mechanism
|
||||||
|
can be then used by afl-fuzz to stress-test targets that couldn't be built
|
||||||
|
with afl-gcc or used in QEMU mode, or with other extensions such as
|
||||||
|
TriforceAFL.
|
||||||
|
|
||||||
|
There is a significant performance penalty compared to native AFL,
|
||||||
|
but at least we're able to use AFL on these binaries, right?
|
||||||
|
|
||||||
|
The idea and much of the implementation comes from Nathan Voss <njvoss299@gmail.com>.
|
||||||
|
|
||||||
|
2) How to use
|
||||||
|
-------------
|
||||||
|
|
||||||
|
*** Building AFL's Unicorn Mode ***
|
||||||
|
|
||||||
|
First, make afl as usual.
|
||||||
|
Once that completes successfully you need to build and add in the Unicorn Mode
|
||||||
|
features:
|
||||||
|
|
||||||
|
$ cd unicorn_mode
|
||||||
|
$ ./build_unicorn_support.sh
|
||||||
|
|
||||||
|
NOTE: This script downloads a recent Unicorn Engine commit that has been tested
|
||||||
|
and is stable-ish from the Unicorn github page. If you are offline, you'll need
|
||||||
|
to hack up this script a little bit and supply your own copy of Unicorn's latest
|
||||||
|
stable release. It's not very hard, just check out the beginning of the
|
||||||
|
build_unicorn_support.sh script and adjust as necessary.
|
||||||
|
|
||||||
|
Building Unicorn will take a little bit (~5-10 minutes). Once it completes
|
||||||
|
it automatically compiles a sample application and verify that it works.
|
||||||
|
|
||||||
|
*** Fuzzing with Unicorn Mode ***
|
||||||
|
|
||||||
|
To really use unicorn-mode effectively you need to prepare the following:
|
||||||
|
|
||||||
|
* Relevant binary code to be fuzzed
|
||||||
|
* Knowledge of the memory map and good starting state
|
||||||
|
* Folder containing sample inputs to start fuzzing with
|
||||||
|
- Same ideas as any other AFL inputs
|
||||||
|
- Quality/speed of results will depend greatly on quality of starting
|
||||||
|
samples
|
||||||
|
- See AFL's guidance on how to create a sample corpus
|
||||||
|
* Unicorn-based test harness which:
|
||||||
|
- Adds memory map regions
|
||||||
|
- Loads binary code into memory
|
||||||
|
- Emulates at least one instruction*
|
||||||
|
- Yeah, this is lame. See 'Gotchas' section below for more info
|
||||||
|
- Loads and verifies data to fuzz from a command-line specified file
|
||||||
|
- AFL will provide mutated inputs by changing the file passed to
|
||||||
|
the test harness
|
||||||
|
- Presumably the data to be fuzzed is at a fixed buffer address
|
||||||
|
- If input constraints (size, invalid bytes, etc.) are known they
|
||||||
|
should be checked after the file is loaded. If a constraint
|
||||||
|
fails, just exit the test harness. AFL will treat the input as
|
||||||
|
'uninteresting' and move on.
|
||||||
|
- Sets up registers and memory state for beginning of test
|
||||||
|
- Emulates the interested code from beginning to end
|
||||||
|
- If a crash is detected, the test harness must 'crash' by
|
||||||
|
throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.)
|
||||||
|
|
||||||
|
Once you have all those things ready to go you just need to run afl-fuzz in
|
||||||
|
'unicorn-mode' by passing in the '-U' flag:
|
||||||
|
|
||||||
|
$ afl-fuzz -U -m none -i /path/to/inputs -o /path/to/results -- ./test_harness @@
|
||||||
|
|
||||||
|
The normal afl-fuzz command line format applies to everything here. Refer to
|
||||||
|
AFL's main documentation for more info about how to use afl-fuzz effectively.
|
||||||
|
|
||||||
|
For a much clearer vision of what all of this looks like, please refer to the
|
||||||
|
sample provided in the 'unicorn_mode/samples' directory. There is also a blog
|
||||||
|
post that goes over the basics at:
|
||||||
|
|
||||||
|
https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf
|
||||||
|
|
||||||
|
The 'helper_scripts' directory also contains several helper scripts that allow you
|
||||||
|
to dump context from a running process, load it, and hook heap allocations. For details
|
||||||
|
on how to use this check out the follow-up blog post to the one linked above.
|
||||||
|
|
||||||
|
A example use of AFL-Unicorn mode is discussed in the Paper Unicorefuzz:
|
||||||
|
https://www.usenix.org/conference/woot19/presentation/maier
|
||||||
|
|
||||||
|
3) Gotchas, feedback, bugs
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
To make sure that AFL's fork server starts up correctly the Unicorn test
|
||||||
|
harness script must emulate at least one instruction before loading the
|
||||||
|
data that will be fuzzed from the input file. It doesn't matter what the
|
||||||
|
instruction is, nor if it is valid. This is an artifact of how the fork-server
|
||||||
|
is started and could likely be fixed with some clever re-arranging of the
|
||||||
|
patches applied to Unicorn.
|
||||||
|
|
||||||
|
Running the build script builds Unicorn and its python bindings and installs
|
||||||
|
them on your system. This installation will supersede any existing Unicorn
|
||||||
|
installation with the patched afl-unicorn version.
|
||||||
|
|
||||||
|
Refer to the unicorn_mode/samples/arm_example/arm_tester.c for an example
|
||||||
|
of how to do this properly! If you don't get this right, AFL will not
|
||||||
|
load any mutated inputs and your fuzzing will be useless!
|
350
llvm_mode/LLVMInsTrim.so.cc
Normal file
350
llvm_mode/LLVMInsTrim.so.cc
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/IR/CFG.h"
|
||||||
|
#include "llvm/IR/Dominators.h"
|
||||||
|
#include "llvm/IR/IRBuilder.h"
|
||||||
|
#include "llvm/IR/Instructions.h"
|
||||||
|
#include "llvm/IR/LegacyPassManager.h"
|
||||||
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/Pass.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
||||||
|
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||||
|
#include "llvm/IR/DebugInfo.h"
|
||||||
|
#include "llvm/IR/BasicBlock.h"
|
||||||
|
#include "llvm/IR/CFG.h"
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <random>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "../config.h"
|
||||||
|
#include "../debug.h"
|
||||||
|
|
||||||
|
#include "MarkNodes.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
static cl::opt<bool> MarkSetOpt("markset", cl::desc("MarkSet"),
|
||||||
|
cl::init(false));
|
||||||
|
static cl::opt<bool> LoopHeadOpt("loophead", cl::desc("LoopHead"),
|
||||||
|
cl::init(false));
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct InsTrim : public ModulePass {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::list<std::string> myWhitelist;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mt19937 generator;
|
||||||
|
int total_instr = 0;
|
||||||
|
|
||||||
|
unsigned genLabel() {
|
||||||
|
return generator() % 65536;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static char ID;
|
||||||
|
InsTrim() : ModulePass(ID), generator(0) {//}
|
||||||
|
|
||||||
|
// AFLCoverage() : ModulePass(ID) {
|
||||||
|
char* instWhiteListFilename = getenv("AFL_LLVM_WHITELIST");
|
||||||
|
if (instWhiteListFilename) {
|
||||||
|
std::string line;
|
||||||
|
std::ifstream fileStream;
|
||||||
|
fileStream.open(instWhiteListFilename);
|
||||||
|
if (!fileStream)
|
||||||
|
report_fatal_error("Unable to open AFL_LLVM_WHITELIST");
|
||||||
|
getline(fileStream, line);
|
||||||
|
while (fileStream) {
|
||||||
|
myWhitelist.push_back(line);
|
||||||
|
getline(fileStream, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
||||||
|
AU.addRequired<DominatorTreeWrapperPass>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LLVM_VERSION_MAJOR < 4
|
||||||
|
const char *
|
||||||
|
#else
|
||||||
|
StringRef
|
||||||
|
#endif
|
||||||
|
getPassName() const override {
|
||||||
|
return "InstTrim Instrumentation";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool runOnModule(Module &M) override {
|
||||||
|
char be_quiet = 0;
|
||||||
|
|
||||||
|
if (isatty(2) && !getenv("AFL_QUIET")) {
|
||||||
|
SAYF(cCYA "LLVMInsTrim" VERSION cRST " by csienslab\n");
|
||||||
|
} else be_quiet = 1;
|
||||||
|
|
||||||
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
char* neverZero_counters_str;
|
||||||
|
if ((neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO")) != NULL)
|
||||||
|
OKF("LLVM neverZero activated (by hexcoder)\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || getenv("LOOPHEAD") != NULL) {
|
||||||
|
LoopHeadOpt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is our default
|
||||||
|
MarkSetOpt = true;
|
||||||
|
|
||||||
|
/* // I dont think this makes sense to port into LLVMInsTrim
|
||||||
|
char* inst_ratio_str = getenv("AFL_INST_RATIO");
|
||||||
|
unsigned int inst_ratio = 100;
|
||||||
|
if (inst_ratio_str) {
|
||||||
|
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || inst_ratio > 100)
|
||||||
|
FATAL("Bad value of AFL_INST_RATIO (must be between 1 and 100)");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
LLVMContext &C = M.getContext();
|
||||||
|
IntegerType *Int8Ty = IntegerType::getInt8Ty(C);
|
||||||
|
IntegerType *Int32Ty = IntegerType::getInt32Ty(C);
|
||||||
|
|
||||||
|
GlobalVariable *CovMapPtr = new GlobalVariable(
|
||||||
|
M, PointerType::getUnqual(Int8Ty), false, GlobalValue::ExternalLinkage,
|
||||||
|
nullptr, "__afl_area_ptr");
|
||||||
|
|
||||||
|
GlobalVariable *OldPrev = new GlobalVariable(
|
||||||
|
M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc",
|
||||||
|
0, GlobalVariable::GeneralDynamicTLSModel, 0, false);
|
||||||
|
|
||||||
|
u64 total_rs = 0;
|
||||||
|
u64 total_hs = 0;
|
||||||
|
|
||||||
|
for (Function &F : M) {
|
||||||
|
if (!F.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myWhitelist.empty()) {
|
||||||
|
bool instrumentBlock = false;
|
||||||
|
DebugLoc Loc;
|
||||||
|
StringRef instFilename;
|
||||||
|
|
||||||
|
for (auto &BB : F) {
|
||||||
|
BasicBlock::iterator IP = BB.getFirstInsertionPt();
|
||||||
|
IRBuilder<> IRB(&(*IP));
|
||||||
|
if (!Loc)
|
||||||
|
Loc = IP->getDebugLoc();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Loc ) {
|
||||||
|
DILocation *cDILoc = dyn_cast<DILocation>(Loc.getAsMDNode());
|
||||||
|
|
||||||
|
unsigned int instLine = cDILoc->getLine();
|
||||||
|
instFilename = cDILoc->getFilename();
|
||||||
|
|
||||||
|
if (instFilename.str().empty()) {
|
||||||
|
/* If the original location is empty, try using the inlined location */
|
||||||
|
DILocation *oDILoc = cDILoc->getInlinedAt();
|
||||||
|
if (oDILoc) {
|
||||||
|
instFilename = oDILoc->getFilename();
|
||||||
|
instLine = oDILoc->getLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue only if we know where we actually are */
|
||||||
|
if (!instFilename.str().empty()) {
|
||||||
|
for (std::list<std::string>::iterator it = myWhitelist.begin(); it != myWhitelist.end(); ++it) {
|
||||||
|
if (instFilename.str().length() >= it->length()) {
|
||||||
|
if (instFilename.str().compare(instFilename.str().length() - it->length(), it->length(), *it) == 0) {
|
||||||
|
instrumentBlock = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either we couldn't figure out our location or the location is
|
||||||
|
* not whitelisted, so we skip instrumentation. */
|
||||||
|
if (!instrumentBlock) {
|
||||||
|
if (!instFilename.str().empty())
|
||||||
|
SAYF(cYEL "[!] " cBRI "Not in whitelist, skipping %s ...\n", instFilename.str().c_str());
|
||||||
|
else
|
||||||
|
SAYF(cYEL "[!] " cBRI "No filename information found, skipping it");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<BasicBlock *> MS;
|
||||||
|
if (!MarkSetOpt) {
|
||||||
|
for (auto &BB : F) {
|
||||||
|
MS.insert(&BB);
|
||||||
|
}
|
||||||
|
total_rs += F.size();
|
||||||
|
} else {
|
||||||
|
auto Result = markNodes(&F);
|
||||||
|
auto RS = Result.first;
|
||||||
|
auto HS = Result.second;
|
||||||
|
|
||||||
|
MS.insert(RS.begin(), RS.end());
|
||||||
|
if (!LoopHeadOpt) {
|
||||||
|
MS.insert(HS.begin(), HS.end());
|
||||||
|
total_rs += MS.size();
|
||||||
|
} else {
|
||||||
|
DenseSet<std::pair<BasicBlock *, BasicBlock *>> EdgeSet;
|
||||||
|
DominatorTreeWrapperPass *DTWP = &getAnalysis<DominatorTreeWrapperPass>(F);
|
||||||
|
auto DT = &DTWP->getDomTree();
|
||||||
|
|
||||||
|
total_rs += RS.size();
|
||||||
|
total_hs += HS.size();
|
||||||
|
|
||||||
|
for (BasicBlock *BB : HS) {
|
||||||
|
bool Inserted = false;
|
||||||
|
for (auto BI = pred_begin(BB), BE = pred_end(BB);
|
||||||
|
BI != BE; ++BI
|
||||||
|
) {
|
||||||
|
auto Edge = BasicBlockEdge(*BI, BB);
|
||||||
|
if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) {
|
||||||
|
EdgeSet.insert({*BI, BB});
|
||||||
|
Inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Inserted) {
|
||||||
|
MS.insert(BB);
|
||||||
|
total_rs += 1;
|
||||||
|
total_hs -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) {
|
||||||
|
auto PredBB = I->first;
|
||||||
|
auto SuccBB = I->second;
|
||||||
|
auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split",
|
||||||
|
DT, nullptr,
|
||||||
|
#if LLVM_VERSION_MAJOR >= 8
|
||||||
|
nullptr,
|
||||||
|
#endif
|
||||||
|
false);
|
||||||
|
MS.insert(NewBB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *EBB = &F.getEntryBlock();
|
||||||
|
if (succ_begin(EBB) == succ_end(EBB)) {
|
||||||
|
MS.insert(EBB);
|
||||||
|
total_rs += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BasicBlock &BB : F) {
|
||||||
|
if (MS.find(&BB) == MS.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||||
|
IRB.CreateStore(ConstantInt::get(Int32Ty, genLabel()), OldPrev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BasicBlock &BB : F) {
|
||||||
|
auto PI = pred_begin(&BB);
|
||||||
|
auto PE = pred_end(&BB);
|
||||||
|
if (MarkSetOpt && MS.find(&BB) == MS.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRBuilder<> IRB(&*BB.getFirstInsertionPt());
|
||||||
|
Value *L = NULL;
|
||||||
|
if (PI == PE) {
|
||||||
|
L = ConstantInt::get(Int32Ty, genLabel());
|
||||||
|
} else {
|
||||||
|
auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin());
|
||||||
|
DenseMap<BasicBlock *, unsigned> PredMap;
|
||||||
|
for (auto PI = pred_begin(&BB), PE = pred_end(&BB);
|
||||||
|
PI != PE; ++PI
|
||||||
|
) {
|
||||||
|
BasicBlock *PBB = *PI;
|
||||||
|
auto It = PredMap.insert({PBB, genLabel()});
|
||||||
|
unsigned Label = It.first->second;
|
||||||
|
PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB);
|
||||||
|
}
|
||||||
|
L = PN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load prev_loc */
|
||||||
|
LoadInst *PrevLoc = IRB.CreateLoad(OldPrev);
|
||||||
|
PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty());
|
||||||
|
|
||||||
|
/* Load SHM pointer */
|
||||||
|
LoadInst *MapPtr = IRB.CreateLoad(CovMapPtr);
|
||||||
|
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, L));
|
||||||
|
|
||||||
|
/* Update bitmap */
|
||||||
|
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
||||||
|
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
|
||||||
|
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
||||||
|
|
||||||
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
if (neverZero_counters_str != NULL) { // with llvm 9 we make this the default as the bug in llvm is then fixed
|
||||||
|
#else
|
||||||
|
#warning "neverZero implementation needs to be reviewed!"
|
||||||
|
#endif
|
||||||
|
/* hexcoder: Realize a counter that skips zero during overflow.
|
||||||
|
* Once this counter reaches its maximum value, it next increments to 1
|
||||||
|
*
|
||||||
|
* Instead of
|
||||||
|
* Counter + 1 -> Counter
|
||||||
|
* we inject now this
|
||||||
|
* Counter + 1 -> {Counter, OverflowFlag}
|
||||||
|
* Counter + OverflowFlag -> Counter
|
||||||
|
*/
|
||||||
|
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||||
|
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||||
|
Incr = IRB.CreateAdd(Incr, carry);
|
||||||
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IRB.CreateStore(Incr, MapPtrIdx)->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
|
||||||
|
/* Set prev_loc to cur_loc >> 1 */
|
||||||
|
/*
|
||||||
|
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
|
||||||
|
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
*/
|
||||||
|
|
||||||
|
total_instr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OKF("Instrumented %u locations (%llu, %llu) (%s mode)\n"/*", ratio %u%%)."*/,
|
||||||
|
total_instr, total_rs, total_hs,
|
||||||
|
getenv("AFL_HARDEN") ? "hardened" :
|
||||||
|
((getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) ?
|
||||||
|
"ASAN/MSAN" : "non-hardened")/*, inst_ratio*/);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}; // end of struct InsTrim
|
||||||
|
} // end of anonymous namespace
|
||||||
|
|
||||||
|
char InsTrim::ID = 0;
|
||||||
|
|
||||||
|
static void registerAFLPass(const PassManagerBuilder &,
|
||||||
|
legacy::PassManagerBase &PM) {
|
||||||
|
PM.add(new InsTrim());
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegisterStandardPasses RegisterAFLPass(
|
||||||
|
PassManagerBuilder::EP_OptimizerLast, registerAFLPass);
|
||||||
|
|
||||||
|
static RegisterStandardPasses RegisterAFLPass0(
|
||||||
|
PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass);
|
@ -16,6 +16,9 @@
|
|||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# For Heiko:
|
||||||
|
#TEST_MMAP=1
|
||||||
|
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
HELPER_PATH = $(PREFIX)/lib/afl
|
HELPER_PATH = $(PREFIX)/lib/afl
|
||||||
BIN_PATH = $(PREFIX)/bin
|
BIN_PATH = $(PREFIX)/bin
|
||||||
@ -23,17 +26,23 @@ BIN_PATH = $(PREFIX)/bin
|
|||||||
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2)
|
||||||
|
|
||||||
LLVM_CONFIG ?= llvm-config
|
LLVM_CONFIG ?= llvm-config
|
||||||
#LLVM_OK = $(shell $(LLVM_CONFIG) --version | egrep -q '^[5-6]' && echo 0 || echo 1 )
|
LLVMVER = $(shell $(LLVM_CONFIG) --version)
|
||||||
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^9|3.0' && echo 1 || echo 0 )
|
LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version | egrep -q '^9|3.0' && echo 1 || echo 0 )
|
||||||
|
LLVM_MAJOR = ($shell $(LLVM_CONFIG) --version | sed 's/\..*//')
|
||||||
|
|
||||||
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
ifeq "$(LLVM_UNSUPPORTED)" "1"
|
||||||
$(warn llvm_mode only supports versions 3.8.0 up to 8.x )
|
$(warn llvm_mode only supports versions 3.8.0 up to 8.x )
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# this is not visible yet:
|
||||||
|
ifeq "$(LLVM_MAJOR)" "9"
|
||||||
|
$(info llvm_mode deteted llvm 9, enabling neverZero implementation)
|
||||||
|
endif
|
||||||
|
|
||||||
CFLAGS ?= -O3 -funroll-loops
|
CFLAGS ?= -O3 -funroll-loops
|
||||||
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
|
CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
|
||||||
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
-DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \
|
||||||
-DVERSION=\"$(VERSION)\"
|
-DVERSION=\"$(VERSION)\"
|
||||||
ifdef AFL_TRACE_PC
|
ifdef AFL_TRACE_PC
|
||||||
CFLAGS += -DUSE_TRACE_PC=1
|
CFLAGS += -DUSE_TRACE_PC=1
|
||||||
endif
|
endif
|
||||||
@ -45,12 +54,16 @@ CXXFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \
|
|||||||
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
|
CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS)
|
||||||
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
|
CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS)
|
||||||
|
|
||||||
# User teor2345 reports that this is required to make things work on MacOS X.
|
|
||||||
|
|
||||||
|
# User teor2345 reports that this is required to make things work on MacOS X.
|
||||||
ifeq "$(shell uname)" "Darwin"
|
ifeq "$(shell uname)" "Darwin"
|
||||||
CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress
|
CLANG_LFL += -Wl,-flat_namespace -Wl,-undefined,suppress
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq "$(shell uname)" "OpenBSD"
|
||||||
|
CLANG_LFL += `$(LLVM_CONFIG) --libdir`/libLLVM.so.0.0
|
||||||
|
endif
|
||||||
|
|
||||||
# We were using llvm-config --bindir to get the location of clang, but
|
# We were using llvm-config --bindir to get the location of clang, but
|
||||||
# this seems to be busted on some distros, so using the one in $PATH is
|
# this seems to be busted on some distros, so using the one in $PATH is
|
||||||
# probably better.
|
# probably better.
|
||||||
@ -60,13 +73,53 @@ ifeq "$(origin CC)" "default"
|
|||||||
CXX = clang++
|
CXX = clang++
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# sanity check.
|
||||||
|
# Are versions of clang --version and llvm-config --version equal?
|
||||||
|
CLANGVER = $(shell $(CC) --version | sed -E -ne '/^.*([0-9]\.[0-9]\.[0-9]).*/s//\1/p')
|
||||||
|
|
||||||
|
|
||||||
|
ifeq "$(shell echo '\#include <sys/ipc.h>@\#include <sys/shm.h>@int main() { int _id = shmget(IPC_PRIVATE, 65536, IPC_CREAT | IPC_EXCL | 0600); shmctl(_id, IPC_RMID, 0); return 0;}' | tr @ '\n' | $(CC) -x c - -o .test2 2>/dev/null && echo 1 || echo 0 )" "1"
|
||||||
|
SHMAT_OK=1
|
||||||
|
else
|
||||||
|
SHMAT_OK=0
|
||||||
|
CFLAGS+=-DUSEMMAP=1
|
||||||
|
LDFLAGS += -lrt
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq "$(TEST_MMAP)" "1"
|
||||||
|
SHMAT_OK=0
|
||||||
|
CFLAGS+=-DUSEMMAP=1
|
||||||
|
LDFLAGS += -lrt
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifndef AFL_TRACE_PC
|
ifndef AFL_TRACE_PC
|
||||||
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so
|
PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../libLLVMInsTrim.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so
|
||||||
else
|
else
|
||||||
PROGS = ../afl-clang-fast ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so
|
PROGS = ../afl-clang-fast ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: test_deps $(PROGS) test_build all_done
|
ifneq "$(CLANGVER)" "$(LLVMVER)"
|
||||||
|
CC = $(shell llvm-config --bindir)/clang
|
||||||
|
CXX = $(shell llvm-config --bindir)/clang++
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: test_shm test_deps $(PROGS) test_build all_done
|
||||||
|
|
||||||
|
|
||||||
|
ifeq "$(SHMAT_OK)" "1"
|
||||||
|
|
||||||
|
test_shm:
|
||||||
|
@echo "[+] shmat seems to be working."
|
||||||
|
@rm -f .test2
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
test_shm:
|
||||||
|
@echo "[-] shmat seems not to be working, switching to mmap implementation"
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
test_deps:
|
test_deps:
|
||||||
ifndef AFL_TRACE_PC
|
ifndef AFL_TRACE_PC
|
||||||
@ -77,6 +130,13 @@ else
|
|||||||
endif
|
endif
|
||||||
@echo "[*] Checking for working '$(CC)'..."
|
@echo "[*] Checking for working '$(CC)'..."
|
||||||
@which $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
|
@which $(CC) >/dev/null 2>&1 || ( echo "[-] Oops, can't find '$(CC)'. Make sure that it's in your \$$PATH (or set \$$CC and \$$CXX)."; exit 1 )
|
||||||
|
@echo "[*] Checking for matching versions of '$(CC)' and '$(LLVM_CONFIG)'"
|
||||||
|
ifneq "$(CLANGVER)" "$(LLVMVER)"
|
||||||
|
@echo "[!] WARNING: we have llvm-config version $(LLVMVER) and a clang version $(CLANGVER)"
|
||||||
|
@echo "[!] Retrying with the clang compiler from llvm: CC=`llvm-config --bindir`/clang"
|
||||||
|
else
|
||||||
|
@echo "[*] We have llvm-config version $(LLVMVER) with a clang version $(CLANGVER), good."
|
||||||
|
endif
|
||||||
@echo "[*] Checking for '../afl-showmap'..."
|
@echo "[*] Checking for '../afl-showmap'..."
|
||||||
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
@test -f ../afl-showmap || ( echo "[-] Oops, can't find '../afl-showmap'. Be sure to compile AFL first."; exit 1 )
|
||||||
@echo "[+] All set and ready to build."
|
@echo "[+] All set and ready to build."
|
||||||
@ -85,8 +145,11 @@ endif
|
|||||||
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
||||||
ln -sf afl-clang-fast ../afl-clang-fast++
|
ln -sf afl-clang-fast ../afl-clang-fast++
|
||||||
|
|
||||||
|
../libLLVMInsTrim.so: LLVMInsTrim.so.cc MarkNodes.cc | test_deps
|
||||||
|
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=gnu++11 -shared $< MarkNodes.cc -o $@ $(CLANG_LFL)
|
||||||
|
|
||||||
../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps
|
../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps
|
||||||
$(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL)
|
$(CXX) $(CLANG_CFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=gnu++11 -shared $< -o $@ $(CLANG_LFL)
|
||||||
|
|
||||||
# laf
|
# laf
|
||||||
../split-switches-pass.so: split-switches-pass.so.cc | test_deps
|
../split-switches-pass.so: split-switches-pass.so.cc | test_deps
|
||||||
@ -110,7 +173,7 @@ endif
|
|||||||
|
|
||||||
test_build: $(PROGS)
|
test_build: $(PROGS)
|
||||||
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
@echo "[*] Testing the CC wrapper and instrumentation output..."
|
||||||
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) LAF_SPLIT_SWITCHES=1 LAF_TRANSFORM_COMPARES=1 LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
unset AFL_USE_ASAN AFL_USE_MSAN AFL_INST_RATIO; AFL_QUIET=1 AFL_PATH=. AFL_CC=$(CC) AFL_LLVM_LAF_SPLIT_SWITCHES=1 AFL_LLVM_LAF_TRANSFORM_COMPARES=1 AFL_LLVM_LAF_SPLIT_COMPARES=1 ../afl-clang-fast $(CFLAGS) ../test-instr.c -o test-instr $(LDFLAGS)
|
||||||
echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr
|
echo 0 | ../afl-showmap -m none -q -o .test-instr0 ./test-instr
|
||||||
echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
echo 1 | ../afl-showmap -m none -q -o .test-instr1 ./test-instr
|
||||||
@rm -f test-instr
|
@rm -f test-instr
|
||||||
@ -123,5 +186,5 @@ all_done: test_build
|
|||||||
.NOTPARALLEL: clean
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1
|
rm -f *.o *.so *~ a.out core core.[1-9][0-9]* .test2 test-instr .test-instr0 .test-instr1
|
||||||
rm -f $(PROGS) ../afl-clang-fast++
|
rm -f $(PROGS) ../afl-clang-fast++
|
||||||
|
355
llvm_mode/MarkNodes.cc
Normal file
355
llvm_mode/MarkNodes.cc
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/ADT/SmallVector.h"
|
||||||
|
#include "llvm/IR/BasicBlock.h"
|
||||||
|
#include "llvm/IR/CFG.h"
|
||||||
|
#include "llvm/IR/Constants.h"
|
||||||
|
#include "llvm/IR/Function.h"
|
||||||
|
#include "llvm/IR/IRBuilder.h"
|
||||||
|
#include "llvm/IR/Instructions.h"
|
||||||
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/Pass.h"
|
||||||
|
#include "llvm/Support/Debug.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
DenseMap<BasicBlock *, uint32_t> LMap;
|
||||||
|
std::vector<BasicBlock *> Blocks;
|
||||||
|
std::set<uint32_t> Marked , Markabove;
|
||||||
|
std::vector< std::vector<uint32_t> > Succs , Preds;
|
||||||
|
|
||||||
|
void reset(){
|
||||||
|
LMap.clear();
|
||||||
|
Blocks.clear();
|
||||||
|
Marked.clear();
|
||||||
|
Markabove.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t start_point;
|
||||||
|
|
||||||
|
void labelEachBlock(Function *F) {
|
||||||
|
// Fake single endpoint;
|
||||||
|
LMap[NULL] = Blocks.size();
|
||||||
|
Blocks.push_back(NULL);
|
||||||
|
|
||||||
|
// Assign the unique LabelID to each block;
|
||||||
|
for (auto I = F->begin(), E = F->end(); I != E; ++I) {
|
||||||
|
BasicBlock *BB = &*I;
|
||||||
|
LMap[BB] = Blocks.size();
|
||||||
|
Blocks.push_back(BB);
|
||||||
|
}
|
||||||
|
|
||||||
|
start_point = LMap[&F->getEntryBlock()];
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildCFG(Function *F) {
|
||||||
|
Succs.resize( Blocks.size() );
|
||||||
|
Preds.resize( Blocks.size() );
|
||||||
|
for( size_t i = 0 ; i < Succs.size() ; i ++ ){
|
||||||
|
Succs[ i ].clear();
|
||||||
|
Preds[ i ].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//uint32_t FakeID = 0;
|
||||||
|
for (auto S = F->begin(), E = F->end(); S != E; ++S) {
|
||||||
|
BasicBlock *BB = &*S;
|
||||||
|
uint32_t MyID = LMap[BB];
|
||||||
|
//if (succ_begin(BB) == succ_end(BB)) {
|
||||||
|
//Succs[MyID].push_back(FakeID);
|
||||||
|
//Marked.insert(MyID);
|
||||||
|
//}
|
||||||
|
for (auto I = succ_begin(BB), E = succ_end(BB); I != E; ++I) {
|
||||||
|
Succs[MyID].push_back(LMap[*I]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::vector<uint32_t> > tSuccs;
|
||||||
|
std::vector<bool> tag , indfs;
|
||||||
|
|
||||||
|
void DFStree(size_t now_id) {
|
||||||
|
if(tag[now_id]) return;
|
||||||
|
tag[now_id]=true;
|
||||||
|
indfs[now_id]=true;
|
||||||
|
for (auto succ: tSuccs[now_id]) {
|
||||||
|
if(tag[succ] and indfs[succ]) {
|
||||||
|
Marked.insert(succ);
|
||||||
|
Markabove.insert(succ);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Succs[now_id].push_back(succ);
|
||||||
|
Preds[succ].push_back(now_id);
|
||||||
|
DFStree(succ);
|
||||||
|
}
|
||||||
|
indfs[now_id]=false;
|
||||||
|
}
|
||||||
|
void turnCFGintoDAG(Function *F) {
|
||||||
|
tSuccs = Succs;
|
||||||
|
tag.resize(Blocks.size());
|
||||||
|
indfs.resize(Blocks.size());
|
||||||
|
for (size_t i = 0; i < Blocks.size(); ++ i) {
|
||||||
|
Succs[i].clear();
|
||||||
|
tag[i]=false;
|
||||||
|
indfs[i]=false;
|
||||||
|
}
|
||||||
|
DFStree(start_point);
|
||||||
|
for (size_t i = 0; i < Blocks.size(); ++ i)
|
||||||
|
if( Succs[i].empty() ){
|
||||||
|
Succs[i].push_back(0);
|
||||||
|
Preds[0].push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t timeStamp;
|
||||||
|
namespace DominatorTree{
|
||||||
|
std::vector< std::vector<uint32_t> > cov;
|
||||||
|
std::vector<uint32_t> dfn, nfd, par, sdom, idom, mom, mn;
|
||||||
|
|
||||||
|
bool Compare(uint32_t u, uint32_t v) {
|
||||||
|
return dfn[u] < dfn[v];
|
||||||
|
}
|
||||||
|
uint32_t eval(uint32_t u) {
|
||||||
|
if( mom[u] == u ) return u;
|
||||||
|
uint32_t res = eval( mom[u] );
|
||||||
|
if(Compare(sdom[mn[mom[u]]] , sdom[mn[u]])) {
|
||||||
|
mn[u] = mn[mom[u]];
|
||||||
|
}
|
||||||
|
return mom[u] = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DFS(uint32_t now) {
|
||||||
|
timeStamp += 1;
|
||||||
|
dfn[now] = timeStamp;
|
||||||
|
nfd[timeStamp - 1] = now;
|
||||||
|
for( auto succ : Succs[now] ) {
|
||||||
|
if( dfn[succ] == 0 ) {
|
||||||
|
par[succ] = now;
|
||||||
|
DFS(succ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DominatorTree(Function *F) {
|
||||||
|
if( Blocks.empty() ) return;
|
||||||
|
uint32_t s = start_point;
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
mn.resize(Blocks.size());
|
||||||
|
cov.resize(Blocks.size());
|
||||||
|
dfn.resize(Blocks.size());
|
||||||
|
nfd.resize(Blocks.size());
|
||||||
|
par.resize(Blocks.size());
|
||||||
|
mom.resize(Blocks.size());
|
||||||
|
sdom.resize(Blocks.size());
|
||||||
|
idom.resize(Blocks.size());
|
||||||
|
|
||||||
|
for( uint32_t i = 0 ; i < Blocks.size() ; i ++ ) {
|
||||||
|
dfn[i] = 0;
|
||||||
|
nfd[i] = Blocks.size();
|
||||||
|
cov[i].clear();
|
||||||
|
idom[i] = mom[i] = mn[i] = sdom[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeStamp = 0;
|
||||||
|
DFS(s);
|
||||||
|
|
||||||
|
for( uint32_t i = Blocks.size() - 1 ; i >= 1u ; i -- ) {
|
||||||
|
uint32_t now = nfd[i];
|
||||||
|
if( now == Blocks.size() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for( uint32_t pre : Preds[ now ] ) {
|
||||||
|
if( dfn[ pre ] ) {
|
||||||
|
eval(pre);
|
||||||
|
if( Compare(sdom[mn[pre]], sdom[now]) ) {
|
||||||
|
sdom[now] = sdom[mn[pre]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cov[sdom[now]].push_back(now);
|
||||||
|
mom[now] = par[now];
|
||||||
|
for( uint32_t x : cov[par[now]] ) {
|
||||||
|
eval(x);
|
||||||
|
if( Compare(sdom[mn[x]], par[now]) ) {
|
||||||
|
idom[x] = mn[x];
|
||||||
|
} else {
|
||||||
|
idom[x] = par[now];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for( uint32_t i = 1 ; i < Blocks.size() ; i += 1 ) {
|
||||||
|
uint32_t now = nfd[i];
|
||||||
|
if( now == Blocks.size() ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(idom[now] != sdom[now])
|
||||||
|
idom[now] = idom[idom[now]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // End of DominatorTree
|
||||||
|
|
||||||
|
std::vector<uint32_t> Visited, InStack;
|
||||||
|
std::vector<uint32_t> TopoOrder, InDeg;
|
||||||
|
std::vector< std::vector<uint32_t> > t_Succ , t_Pred;
|
||||||
|
|
||||||
|
void Go(uint32_t now, uint32_t tt) {
|
||||||
|
if( now == tt ) return;
|
||||||
|
Visited[now] = InStack[now] = timeStamp;
|
||||||
|
|
||||||
|
for(uint32_t nxt : Succs[now]) {
|
||||||
|
if(Visited[nxt] == timeStamp and InStack[nxt] == timeStamp) {
|
||||||
|
Marked.insert(nxt);
|
||||||
|
}
|
||||||
|
t_Succ[now].push_back(nxt);
|
||||||
|
t_Pred[nxt].push_back(now);
|
||||||
|
InDeg[nxt] += 1;
|
||||||
|
if(Visited[nxt] == timeStamp) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Go(nxt, tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
InStack[now] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopologicalSort(uint32_t ss, uint32_t tt) {
|
||||||
|
timeStamp += 1;
|
||||||
|
|
||||||
|
Go(ss, tt);
|
||||||
|
|
||||||
|
TopoOrder.clear();
|
||||||
|
std::queue<uint32_t> wait;
|
||||||
|
wait.push(ss);
|
||||||
|
while( not wait.empty() ) {
|
||||||
|
uint32_t now = wait.front(); wait.pop();
|
||||||
|
TopoOrder.push_back(now);
|
||||||
|
for(uint32_t nxt : t_Succ[now]) {
|
||||||
|
InDeg[nxt] -= 1;
|
||||||
|
if(InDeg[nxt] == 0u) {
|
||||||
|
wait.push(nxt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::set<uint32_t> > NextMarked;
|
||||||
|
bool Indistinguish(uint32_t node1, uint32_t node2) {
|
||||||
|
if(NextMarked[node1].size() > NextMarked[node2].size()){
|
||||||
|
uint32_t _swap = node1;
|
||||||
|
node1 = node2;
|
||||||
|
node2 = _swap;
|
||||||
|
}
|
||||||
|
for(uint32_t x : NextMarked[node1]) {
|
||||||
|
if( NextMarked[node2].find(x) != NextMarked[node2].end() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeUniq(uint32_t now) {
|
||||||
|
bool StopFlag = false;
|
||||||
|
if (Marked.find(now) == Marked.end()) {
|
||||||
|
for(uint32_t pred1 : t_Pred[now]) {
|
||||||
|
for(uint32_t pred2 : t_Pred[now]) {
|
||||||
|
if(pred1 == pred2) continue;
|
||||||
|
if(Indistinguish(pred1, pred2)) {
|
||||||
|
Marked.insert(now);
|
||||||
|
StopFlag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StopFlag) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Marked.find(now) != Marked.end()) {
|
||||||
|
NextMarked[now].insert(now);
|
||||||
|
} else {
|
||||||
|
for(uint32_t pred : t_Pred[now]) {
|
||||||
|
for(uint32_t x : NextMarked[pred]) {
|
||||||
|
NextMarked[now].insert(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkSubGraph(uint32_t ss, uint32_t tt) {
|
||||||
|
TopologicalSort(ss, tt);
|
||||||
|
if(TopoOrder.empty()) return;
|
||||||
|
|
||||||
|
for(uint32_t i : TopoOrder) {
|
||||||
|
NextMarked[i].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
NextMarked[TopoOrder[0]].insert(TopoOrder[0]);
|
||||||
|
for(uint32_t i = 1 ; i < TopoOrder.size() ; i += 1) {
|
||||||
|
MakeUniq(TopoOrder[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkVertice(Function *F) {
|
||||||
|
uint32_t s = start_point;
|
||||||
|
|
||||||
|
InDeg.resize(Blocks.size());
|
||||||
|
Visited.resize(Blocks.size());
|
||||||
|
InStack.resize(Blocks.size());
|
||||||
|
t_Succ.resize(Blocks.size());
|
||||||
|
t_Pred.resize(Blocks.size());
|
||||||
|
NextMarked.resize(Blocks.size());
|
||||||
|
|
||||||
|
for( uint32_t i = 0 ; i < Blocks.size() ; i += 1 ) {
|
||||||
|
Visited[i] = InStack[i] = InDeg[i] = 0;
|
||||||
|
t_Succ[i].clear();
|
||||||
|
t_Pred[i].clear();
|
||||||
|
}
|
||||||
|
timeStamp = 0;
|
||||||
|
uint32_t t = 0;
|
||||||
|
//MarkSubGraph(s, t);
|
||||||
|
//return;
|
||||||
|
|
||||||
|
while( s != t ) {
|
||||||
|
MarkSubGraph(DominatorTree::idom[t], t);
|
||||||
|
t = DominatorTree::idom[t];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// return {marked nodes}
|
||||||
|
std::pair<std::vector<BasicBlock *>,
|
||||||
|
std::vector<BasicBlock *> >markNodes(Function *F) {
|
||||||
|
assert(F->size() > 0 && "Function can not be empty");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
labelEachBlock(F);
|
||||||
|
buildCFG(F);
|
||||||
|
turnCFGintoDAG(F);
|
||||||
|
DominatorTree::DominatorTree(F);
|
||||||
|
MarkVertice(F);
|
||||||
|
|
||||||
|
std::vector<BasicBlock *> Result , ResultAbove;
|
||||||
|
for( uint32_t x : Markabove ) {
|
||||||
|
auto it = Marked.find( x );
|
||||||
|
if( it != Marked.end() )
|
||||||
|
Marked.erase( it );
|
||||||
|
if( x )
|
||||||
|
ResultAbove.push_back(Blocks[x]);
|
||||||
|
}
|
||||||
|
for( uint32_t x : Marked ) {
|
||||||
|
if (x == 0) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Result.push_back(Blocks[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { Result , ResultAbove };
|
||||||
|
}
|
11
llvm_mode/MarkNodes.h
Normal file
11
llvm_mode/MarkNodes.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __MARK_NODES__
|
||||||
|
#define __MARK_NODES__
|
||||||
|
|
||||||
|
#include "llvm/IR/BasicBlock.h"
|
||||||
|
#include "llvm/IR/Function.h"
|
||||||
|
#include<vector>
|
||||||
|
|
||||||
|
std::pair<std::vector<llvm::BasicBlock *>,
|
||||||
|
std::vector<llvm::BasicBlock *>> markNodes(llvm::Function *F);
|
||||||
|
|
||||||
|
#endif
|
26
llvm_mode/README.instrim
Normal file
26
llvm_mode/README.instrim
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# InsTrim
|
||||||
|
InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing
|
||||||
|
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
InsTrim uses CFG and markers to instrument just what is necessary in the
|
||||||
|
binary in llvm_mode. It is about 20-25% faster but as a cost has a lower
|
||||||
|
path discovery.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Set the environment variable AFL_LLVM_INSTRIM=1
|
||||||
|
|
||||||
|
There is also an advanced mode which instruments loops in a way so that
|
||||||
|
afl-fuzz can see which loop path has been selected but not being able to
|
||||||
|
see how often the loop has been rerun.
|
||||||
|
This again is a tradeoff for speed for less path information.
|
||||||
|
To enable this mode set AFL_LLVM_INSTRIM_LOOPHEAD=1
|
||||||
|
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
The paper: [InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing]
|
||||||
|
(https://www.ndss-symposium.org/wp-content/uploads/2018/07/bar2018_14_Hsu_paper.pdf)
|
@ -8,13 +8,13 @@ compile the target project.
|
|||||||
|
|
||||||
The following options exist:
|
The following options exist:
|
||||||
|
|
||||||
export LAF_SPLIT_SWITCHES=1 Enables the split-switches pass.
|
export AFL_LLVM_LAF_SPLIT_SWITCHES=1 Enables the split-switches pass.
|
||||||
|
|
||||||
export LAF_TRANSFORM_COMPARES=1 Enables the transform-compares pass
|
export AFL_LLVM_LAF_TRANSFORM_COMPARES=1 Enables the transform-compares pass
|
||||||
(strcmp, memcmp, strncmp, strcasecmp, strncasecmp).
|
(strcmp, memcmp, strncmp, strcasecmp, strncasecmp).
|
||||||
|
|
||||||
export LAF_SPLIT_COMPARES=1 Enables the split-compares pass.
|
export AFL_LLVM_LAF_SPLIT_COMPARES=1 Enables the split-compares pass.
|
||||||
By default it will split all compares with a bit width <= 64 bits.
|
By default it will split all compares with a bit width <= 64 bits.
|
||||||
You can change this behaviour by setting
|
You can change this behaviour by setting
|
||||||
export LAF_SPLIT_COMPARES_BITW=<bit_width>.
|
export AFL_LLVM_LAF_SPLIT_COMPARES_BITW=<bit_width>.
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ Fast LLVM-based instrumentation for afl-fuzz
|
|||||||
============================================
|
============================================
|
||||||
|
|
||||||
(See ../docs/README for the general instruction manual.)
|
(See ../docs/README for the general instruction manual.)
|
||||||
|
(See ../gcc_plugin/README.gcc for the GCC-based instrumentation.)
|
||||||
|
|
||||||
1) Introduction
|
1) Introduction
|
||||||
---------------
|
---------------
|
||||||
@ -30,7 +31,7 @@ several interesting properties:
|
|||||||
- The instrumentation can cope a bit better with multi-threaded targets.
|
- The instrumentation can cope a bit better with multi-threaded targets.
|
||||||
|
|
||||||
- Because the feature relies on the internals of LLVM, it is clang-specific
|
- Because the feature relies on the internals of LLVM, it is clang-specific
|
||||||
and will *not* work with GCC.
|
and will *not* work with GCC (see ../gcc_plugin/ for an alternative).
|
||||||
|
|
||||||
Once this implementation is shown to be sufficiently robust and portable, it
|
Once this implementation is shown to be sufficiently robust and portable, it
|
||||||
will probably replace afl-clang. For now, it can be built separately and
|
will probably replace afl-clang. For now, it can be built separately and
|
||||||
@ -38,8 +39,8 @@ co-exists with the original code.
|
|||||||
|
|
||||||
The idea and much of the implementation comes from Laszlo Szekeres.
|
The idea and much of the implementation comes from Laszlo Szekeres.
|
||||||
|
|
||||||
2) How to use
|
2) How to use this
|
||||||
-------------
|
------------------
|
||||||
|
|
||||||
In order to leverage this mechanism, you need to have clang installed on your
|
In order to leverage this mechanism, you need to have clang installed on your
|
||||||
system. You should also make sure that the llvm-config tool is in your path
|
system. You should also make sure that the llvm-config tool is in your path
|
||||||
@ -69,21 +70,47 @@ operating mode of AFL, e.g.:
|
|||||||
Be sure to also include CXX set to afl-clang-fast++ for C++ code.
|
Be sure to also include CXX set to afl-clang-fast++ for C++ code.
|
||||||
|
|
||||||
The tool honors roughly the same environmental variables as afl-gcc (see
|
The tool honors roughly the same environmental variables as afl-gcc (see
|
||||||
../docs/env_variables.txt). This includes AFL_INST_RATIO, AFL_USE_ASAN,
|
../docs/env_variables.txt). This includes AFL_USE_ASAN,
|
||||||
AFL_HARDEN, and AFL_DONT_OPTIMIZE.
|
AFL_HARDEN, and AFL_DONT_OPTIMIZE. However AFL_INST_RATIO is not honored
|
||||||
|
as it does not serve a good purpose with the more effective instrim CFG
|
||||||
|
analysis.
|
||||||
|
|
||||||
Note: if you want the LLVM helper to be installed on your system for all
|
Note: if you want the LLVM helper to be installed on your system for all
|
||||||
users, you need to build it before issuing 'make install' in the parent
|
users, you need to build it before issuing 'make install' in the parent
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
3) Gotchas, feedback, bugs
|
3) Options
|
||||||
|
|
||||||
|
Several options are present to make llvm_mode faster or help it rearrange
|
||||||
|
the code to make afl-fuzz path discovery easier.
|
||||||
|
|
||||||
|
If you need just to instrument specific parts of the code, you can whitelist
|
||||||
|
which C/C++ files to actually intrument. See README.whitelist
|
||||||
|
|
||||||
|
For splitting memcmp, strncmp, etc. please see README.laf-intel
|
||||||
|
|
||||||
|
Then there is an optimized instrumentation strategy that uses CFGs and
|
||||||
|
markers to just instrument what is needed. This increases speed by 20-25%
|
||||||
|
however has a lower path discovery.
|
||||||
|
If you want to use this, set AFL_LLVM_INSTRIM=1
|
||||||
|
See README.instrim
|
||||||
|
|
||||||
|
Finally if your llvm version is 8 or lower, you can activate a mode that
|
||||||
|
prevents that a counter overflow result in a 0 value. This is good for
|
||||||
|
path discovery, but the llvm implementation for intel for this functionality
|
||||||
|
is not optimal and was only fixed in llvm 9.
|
||||||
|
You can set this with AFL_LLVM_NOT_ZERO=1
|
||||||
|
See README.neverzero
|
||||||
|
|
||||||
|
|
||||||
|
4) Gotchas, feedback, bugs
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
This is an early-stage mechanism, so field reports are welcome. You can send bug
|
||||||
reports to <afl-users@googlegroups.com>.
|
reports to <afl-users@googlegroups.com>.
|
||||||
|
|
||||||
4) Bonus feature #1: deferred instrumentation
|
5) Bonus feature #1: deferred initialization
|
||||||
---------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
AFL tries to optimize performance by executing the targeted binary just once,
|
AFL tries to optimize performance by executing the targeted binary just once,
|
||||||
stopping it just before main(), and then cloning this "master" process to get
|
stopping it just before main(), and then cloning this "master" process to get
|
||||||
@ -129,7 +156,7 @@ will keep working normally when compiled with a tool other than afl-clang-fast.
|
|||||||
Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will
|
Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will
|
||||||
*not* generate a deferred-initialization binary) - and you should be all set!
|
*not* generate a deferred-initialization binary) - and you should be all set!
|
||||||
|
|
||||||
5) Bonus feature #2: persistent mode
|
6) Bonus feature #2: persistent mode
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
Some libraries provide APIs that are stateless, or whose state can be reset in
|
Some libraries provide APIs that are stateless, or whose state can be reset in
|
||||||
@ -169,7 +196,7 @@ PS. Because there are task switches still involved, the mode isn't as fast as
|
|||||||
faster than the normal fork() model, and compared to in-process fuzzing,
|
faster than the normal fork() model, and compared to in-process fuzzing,
|
||||||
should be a lot more robust.
|
should be a lot more robust.
|
||||||
|
|
||||||
6) Bonus feature #3: new 'trace-pc-guard' mode
|
8) Bonus feature #3: new 'trace-pc-guard' mode
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
Recent versions of LLVM are shipping with a built-in execution tracing feature
|
Recent versions of LLVM are shipping with a built-in execution tracing feature
|
||||||
@ -178,10 +205,8 @@ post-process the assembly or install any compiler plugins. See:
|
|||||||
|
|
||||||
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
|
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
|
||||||
|
|
||||||
As of this writing, the feature is only available on SVN trunk, and is yet to
|
If you have a sufficiently recent compiler and want to give it a try, build
|
||||||
make it to an official release of LLVM. Nevertheless, if you have a
|
afl-clang-fast this way:
|
||||||
sufficiently recent compiler and want to give it a try, build afl-clang-fast
|
|
||||||
this way:
|
|
||||||
|
|
||||||
AFL_TRACE_PC=1 make clean all
|
AFL_TRACE_PC=1 make clean all
|
||||||
|
|
||||||
|
22
llvm_mode/README.neverzero
Normal file
22
llvm_mode/README.neverzero
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
In larger, complex or reiterative programs the map that collects the edge pairs
|
||||||
|
can easily fill up and wrap.
|
||||||
|
This is not that much of an issue - unless by chance it wraps just to a 0
|
||||||
|
when the program execution ends.
|
||||||
|
In this case afl-fuzz is not able to see that the pair has been accessed and
|
||||||
|
will ignore it.
|
||||||
|
|
||||||
|
NeverZero prevents this behaviour. If a counter wraps, it jumps over the 0
|
||||||
|
directly to a 1. This improves path discovery (by a very little amount)
|
||||||
|
at a very little cost (one instruction per edge).
|
||||||
|
|
||||||
|
This is implemented in afl-gcc, however for llvm_mode this is optional if
|
||||||
|
the llvm version is below 9 - as there is a perfomance bug that is only fixed
|
||||||
|
in version 9 and onwards.
|
||||||
|
|
||||||
|
If you want to enable this for llvm < 9 then set
|
||||||
|
|
||||||
|
export AFL_LLVM_NOT_ZERO=1
|
||||||
|
|
@ -32,6 +32,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
static u8* obj_path; /* Path to runtime libraries */
|
static u8* obj_path; /* Path to runtime libraries */
|
||||||
static u8** cc_params; /* Parameters passed to the real CC */
|
static u8** cc_params; /* Parameters passed to the real CC */
|
||||||
@ -87,7 +88,7 @@ static void find_obj(u8* argv0) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so'. Please set AFL_PATH");
|
FATAL("Unable to find 'afl-llvm-rt.o' or 'afl-llvm-pass.so.cc'. Please set AFL_PATH");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,29 +113,29 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
cc_params[0] = alt_cc ? alt_cc : (u8*)"clang";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There are two ways to compile afl-clang-fast. In the traditional mode, we
|
/* There are three ways to compile with afl-clang-fast. In the traditional
|
||||||
use afl-llvm-pass.so to inject instrumentation. In the experimental
|
mode, we use afl-llvm-pass.so, then there is libLLVMInsTrim.so which is
|
||||||
|
much faster but has less coverage. Finally tere is the experimental
|
||||||
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
|
'trace-pc-guard' mode, we use native LLVM instrumentation callbacks
|
||||||
instead. The latter is a very recent addition - see:
|
instead. For trace-pc-guard see:
|
||||||
|
|
||||||
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */
|
http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */
|
||||||
|
|
||||||
// laf
|
// laf
|
||||||
if (getenv("LAF_SPLIT_SWITCHES")) {
|
if (getenv("LAF_SPLIT_SWITCHES")||getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = alloc_printf("%s/split-switches-pass.so", obj_path);
|
cc_params[cc_par_cnt++] = alloc_printf("%s/split-switches-pass.so", obj_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("LAF_TRANSFORM_COMPARES")) {
|
if (getenv("LAF_TRANSFORM_COMPARES")||getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = alloc_printf("%s/compare-transform-pass.so", obj_path);
|
cc_params[cc_par_cnt++] = alloc_printf("%s/compare-transform-pass.so", obj_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("LAF_SPLIT_COMPARES")) {
|
if (getenv("LAF_SPLIT_COMPARES")||getenv("AFL_LLVM_LAF_SPLIT_COMPARES")) {
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
@ -143,14 +144,18 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
// /laf
|
// /laf
|
||||||
|
|
||||||
#ifdef USE_TRACE_PC
|
#ifdef USE_TRACE_PC
|
||||||
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
|
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default
|
||||||
cc_params[cc_par_cnt++] = "-mllvm";
|
//cc_params[cc_par_cnt++] = "-mllvm";
|
||||||
cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
//cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-cmp,trace-div,trace-gep";
|
||||||
|
//cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
|
||||||
#else
|
#else
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = "-load";
|
cc_params[cc_par_cnt++] = "-load";
|
||||||
cc_params[cc_par_cnt++] = "-Xclang";
|
cc_params[cc_par_cnt++] = "-Xclang";
|
||||||
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
|
if (getenv("AFL_LLVM_INSTRIM") != NULL || getenv("INSTRIM_LIB") != NULL)
|
||||||
|
cc_params[cc_par_cnt++] = alloc_printf("%s/libLLVMInsTrim.so", obj_path);
|
||||||
|
else
|
||||||
|
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
|
||||||
#endif /* ^USE_TRACE_PC */
|
#endif /* ^USE_TRACE_PC */
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-Qunused-arguments";
|
cc_params[cc_par_cnt++] = "-Qunused-arguments";
|
||||||
@ -246,6 +251,10 @@ static void edit_params(u32 argc, char** argv) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USEMMAP
|
||||||
|
cc_params[cc_par_cnt++] = "-lrt";
|
||||||
|
#endif
|
||||||
|
|
||||||
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
|
cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1";
|
||||||
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
|
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
|
||||||
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
|
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
|
||||||
|
@ -118,6 +118,10 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
char* neverZero_counters_str = getenv("AFL_LLVM_NOT_ZERO");
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Get globals for the SHM region and the previous location. Note that
|
/* Get globals for the SHM region and the previous location. Note that
|
||||||
__afl_prev_loc is thread-local. */
|
__afl_prev_loc is thread-local. */
|
||||||
|
|
||||||
@ -227,21 +231,69 @@ bool AFLCoverage::runOnModule(Module &M) {
|
|||||||
|
|
||||||
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr);
|
||||||
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
Value *MapPtrIdx =
|
Value *MapPtrIdx = IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
|
||||||
IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc));
|
|
||||||
|
|
||||||
/* Update bitmap */
|
/* Update bitmap */
|
||||||
|
|
||||||
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
LoadInst *Counter = IRB.CreateLoad(MapPtrIdx);
|
||||||
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
|
||||||
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1));
|
||||||
IRB.CreateStore(Incr, MapPtrIdx)
|
|
||||||
->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
if (neverZero_counters_str != NULL) { // with llvm 9 we make this the default as the bug in llvm is then fixed
|
||||||
|
#endif
|
||||||
|
/* hexcoder: Realize a counter that skips zero during overflow.
|
||||||
|
* Once this counter reaches its maximum value, it next increments to 1
|
||||||
|
*
|
||||||
|
* Instead of
|
||||||
|
* Counter + 1 -> Counter
|
||||||
|
* we inject now this
|
||||||
|
* Counter + 1 -> {Counter, OverflowFlag}
|
||||||
|
* Counter + OverflowFlag -> Counter
|
||||||
|
*/
|
||||||
|
/* // we keep the old solutions just in case
|
||||||
|
// Solution #1
|
||||||
|
if (neverZero_counters_str[0] == '1') {
|
||||||
|
CallInst *AddOv = IRB.CreateBinaryIntrinsic(Intrinsic::uadd_with_overflow, Counter, ConstantInt::get(Int8Ty, 1));
|
||||||
|
AddOv->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
Value *SumWithOverflowBit = AddOv;
|
||||||
|
Incr = IRB.CreateAdd(IRB.CreateExtractValue(SumWithOverflowBit, 0), // sum
|
||||||
|
IRB.CreateZExt( // convert from one bit type to 8 bits type
|
||||||
|
IRB.CreateExtractValue(SumWithOverflowBit, 1), // overflow
|
||||||
|
Int8Ty));
|
||||||
|
// Solution #2
|
||||||
|
} else if (neverZero_counters_str[0] == '2') {
|
||||||
|
auto cf = IRB.CreateICmpEQ(Counter, ConstantInt::get(Int8Ty, 255));
|
||||||
|
Value *HowMuch = IRB.CreateAdd(ConstantInt::get(Int8Ty, 1), cf);
|
||||||
|
Incr = IRB.CreateAdd(Counter, HowMuch);
|
||||||
|
// Solution #3
|
||||||
|
} else if (neverZero_counters_str[0] == '3') {
|
||||||
|
*/
|
||||||
|
// this is the solution we choose because llvm9 should do the right thing here
|
||||||
|
auto cf = IRB.CreateICmpEQ(Incr, ConstantInt::get(Int8Ty, 0));
|
||||||
|
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||||
|
Incr = IRB.CreateAdd(Incr, carry);
|
||||||
|
/*
|
||||||
|
// Solution #4
|
||||||
|
} else if (neverZero_counters_str[0] == '4') {
|
||||||
|
auto cf = IRB.CreateICmpULT(Incr, ConstantInt::get(Int8Ty, 1));
|
||||||
|
auto carry = IRB.CreateZExt(cf, Int8Ty);
|
||||||
|
Incr = IRB.CreateAdd(Incr, carry);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error: unknown value for AFL_NZERO_COUNTS: %s (valid is 1-4)\n", neverZero_counters_str);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#if LLVM_VERSION_MAJOR < 9
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IRB.CreateStore(Incr, MapPtrIdx)->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
|
||||||
/* Set prev_loc to cur_loc >> 1 */
|
/* Set prev_loc to cur_loc >> 1 */
|
||||||
|
|
||||||
StoreInst *Store =
|
StoreInst *Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
|
||||||
IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc);
|
|
||||||
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None));
|
||||||
|
|
||||||
inst_blocks++;
|
inst_blocks++;
|
||||||
|
@ -44,6 +44,9 @@
|
|||||||
# define CONST_PRIO 0
|
# define CONST_PRIO 0
|
||||||
#endif /* ^USE_TRACE_PC */
|
#endif /* ^USE_TRACE_PC */
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
|
||||||
/* Globals needed by the injected instrumentation. The __afl_area_initial region
|
/* Globals needed by the injected instrumentation. The __afl_area_initial region
|
||||||
is used for instrumentation output before __afl_map_shm() has a chance to run.
|
is used for instrumentation output before __afl_map_shm() has a chance to run.
|
||||||
@ -71,10 +74,34 @@ static void __afl_map_shm(void) {
|
|||||||
hacky .init code to work correctly in projects such as OpenSSL. */
|
hacky .init code to work correctly in projects such as OpenSSL. */
|
||||||
|
|
||||||
if (id_str) {
|
if (id_str) {
|
||||||
|
#ifdef USEMMAP
|
||||||
|
const char *shm_file_path = id_str;
|
||||||
|
int shm_fd = -1;
|
||||||
|
unsigned char *shm_base = NULL;
|
||||||
|
|
||||||
|
/* create the shared memory segment as if it was a file */
|
||||||
|
shm_fd = shm_open(shm_file_path, O_RDWR, 0600);
|
||||||
|
if (shm_fd == -1) {
|
||||||
|
printf("shm_open() failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* map the shared memory segment to the address space of the process */
|
||||||
|
shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||||
|
if (shm_base == MAP_FAILED) {
|
||||||
|
close(shm_fd);
|
||||||
|
shm_fd = -1;
|
||||||
|
|
||||||
|
printf("mmap() failed\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
__afl_area_ptr = shm_base;
|
||||||
|
#else
|
||||||
u32 shm_id = atoi(id_str);
|
u32 shm_id = atoi(id_str);
|
||||||
|
|
||||||
__afl_area_ptr = shmat(shm_id, NULL, 0);
|
__afl_area_ptr = shmat(shm_id, NULL, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Whooooops. */
|
/* Whooooops. */
|
||||||
|
|
||||||
|
@ -257,6 +257,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
|
|||||||
std::vector<Value *> args;
|
std::vector<Value *> args;
|
||||||
args.push_back(load);
|
args.push_back(load);
|
||||||
load = IRB.CreateCall(tolowerFn, args, "tmp");
|
load = IRB.CreateCall(tolowerFn, args, "tmp");
|
||||||
|
load = IRB.CreateTrunc(load, Int8Ty);
|
||||||
}
|
}
|
||||||
Value *isub;
|
Value *isub;
|
||||||
if (HasStr1)
|
if (HasStr1)
|
||||||
@ -272,14 +273,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
|
|||||||
next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
|
next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb);
|
||||||
BranchInst::Create(end_bb, next_bb);
|
BranchInst::Create(end_bb, next_bb);
|
||||||
|
|
||||||
#if LLVM_VERSION_MAJOR < 8
|
|
||||||
TerminatorInst *term = cur_bb->getTerminator();
|
|
||||||
#else
|
|
||||||
Instruction *term = cur_bb->getTerminator();
|
|
||||||
#endif
|
|
||||||
Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0));
|
Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0));
|
||||||
IRB.CreateCondBr(icmp, next_bb, end_bb);
|
IRB.CreateCondBr(icmp, next_bb, end_bb);
|
||||||
term->eraseFromParent();
|
cur_bb->getTerminator()->eraseFromParent();
|
||||||
} else {
|
} else {
|
||||||
//IRB.CreateBr(end_bb);
|
//IRB.CreateBr(end_bb);
|
||||||
}
|
}
|
||||||
@ -304,7 +300,8 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const
|
|||||||
|
|
||||||
bool CompareTransform::runOnModule(Module &M) {
|
bool CompareTransform::runOnModule(Module &M) {
|
||||||
|
|
||||||
llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n";
|
if (getenv("AFL_QUIET") == NULL)
|
||||||
|
llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n";
|
||||||
transformCmps(M, true, true, true, true, true);
|
transformCmps(M, true, true, true, true, true);
|
||||||
verifyModule(M);
|
verifyModule(M);
|
||||||
|
|
||||||
|
@ -477,6 +477,8 @@ bool SplitComparesTransform::runOnModule(Module &M) {
|
|||||||
int bitw = 64;
|
int bitw = 64;
|
||||||
|
|
||||||
char* bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
|
char* bitw_env = getenv("LAF_SPLIT_COMPARES_BITW");
|
||||||
|
if (!bitw_env)
|
||||||
|
bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW");
|
||||||
if (bitw_env) {
|
if (bitw_env) {
|
||||||
bitw = atoi(bitw_env);
|
bitw = atoi(bitw_env);
|
||||||
}
|
}
|
||||||
@ -485,7 +487,8 @@ bool SplitComparesTransform::runOnModule(Module &M) {
|
|||||||
|
|
||||||
simplifySignedness(M);
|
simplifySignedness(M);
|
||||||
|
|
||||||
errs() << "Split-compare-pass by laf.intel@gmail.com\n";
|
if (getenv("AFL_QUIET") == NULL)
|
||||||
|
errs() << "Split-compare-pass by laf.intel@gmail.com\n";
|
||||||
|
|
||||||
switch (bitw) {
|
switch (bitw) {
|
||||||
case 64:
|
case 64:
|
||||||
|
@ -244,7 +244,8 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
|||||||
|
|
||||||
/* If there is only the default destination or the condition checks 8 bit or less, don't bother with the code below. */
|
/* If there is only the default destination or the condition checks 8 bit or less, don't bother with the code below. */
|
||||||
if (!SI->getNumCases() || bitw <= 8) {
|
if (!SI->getNumCases() || bitw <= 8) {
|
||||||
errs() << "skip trivial switch..\n";
|
if (getenv("AFL_QUIET") == NULL)
|
||||||
|
errs() << "skip trivial switch..\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +303,8 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) {
|
|||||||
|
|
||||||
bool SplitSwitchesTransform::runOnModule(Module &M) {
|
bool SplitSwitchesTransform::runOnModule(Module &M) {
|
||||||
|
|
||||||
llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
|
if (getenv("AFL_QUIET") == NULL)
|
||||||
|
llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n";
|
||||||
splitSwitches(M);
|
splitSwitches(M);
|
||||||
verifyModule(M);
|
verifyModule(M);
|
||||||
|
|
||||||
|
@ -9,3 +9,7 @@ simple-chunk-replace.py - this is a simple example where chunks are replaced
|
|||||||
|
|
||||||
common.py - this can be used for common functions and helpers.
|
common.py - this can be used for common functions and helpers.
|
||||||
the examples do not use this though. But you can :)
|
the examples do not use this though. But you can :)
|
||||||
|
|
||||||
|
wrapper_afl_min.py - mutation of XML documents, loads XmlMutatorMin.py
|
||||||
|
|
||||||
|
XmlMutatorMin.py - module for XML mutation
|
||||||
|
331
python_mutators/XmlMutatorMin.py
Normal file
331
python_mutators/XmlMutatorMin.py
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
""" Mutation of XML documents, should be called from one of its wrappers (CLI, AFL, ...) """
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
from copy import deepcopy
|
||||||
|
from lxml import etree as ET
|
||||||
|
import random, re, io
|
||||||
|
|
||||||
|
###########################
|
||||||
|
# The XmlMutatorMin class #
|
||||||
|
###########################
|
||||||
|
|
||||||
|
class XmlMutatorMin:
|
||||||
|
|
||||||
|
"""
|
||||||
|
Optionals parameters:
|
||||||
|
seed Seed used by the PRNG (default: "RANDOM")
|
||||||
|
verbose Verbosity (default: False)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, seed="RANDOM", verbose=False):
|
||||||
|
|
||||||
|
""" Initialize seed, database and mutators """
|
||||||
|
|
||||||
|
# Verbosity
|
||||||
|
self.verbose = verbose
|
||||||
|
|
||||||
|
# Initialize PRNG
|
||||||
|
self.seed = str(seed)
|
||||||
|
if self.seed == "RANDOM":
|
||||||
|
random.seed()
|
||||||
|
else:
|
||||||
|
if self.verbose:
|
||||||
|
print("Static seed '%s'" % self.seed)
|
||||||
|
random.seed(self.seed)
|
||||||
|
|
||||||
|
# Initialize input and output documents
|
||||||
|
self.input_tree = None
|
||||||
|
self.tree = None
|
||||||
|
|
||||||
|
# High-level mutators (no database needed)
|
||||||
|
hl_mutators_delete = [ "del_node_and_children", "del_node_but_children", "del_attribute", "del_content" ] # Delete items
|
||||||
|
hl_mutators_fuzz = ["fuzz_attribute"] # Randomly change attribute values
|
||||||
|
|
||||||
|
# Exposed mutators
|
||||||
|
self.hl_mutators_all = hl_mutators_fuzz + hl_mutators_delete
|
||||||
|
|
||||||
|
def __parse_xml (self, xml):
|
||||||
|
|
||||||
|
""" Parse an XML string. Basic wrapper around lxml.parse() """
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Function parse() takes care of comments / DTD / processing instructions / ...
|
||||||
|
tree = ET.parse(io.BytesIO(xml))
|
||||||
|
except ET.ParseError:
|
||||||
|
raise RuntimeError("XML isn't well-formed!")
|
||||||
|
except LookupError as e:
|
||||||
|
raise RuntimeError(e)
|
||||||
|
|
||||||
|
# Return a document wrapper
|
||||||
|
return tree
|
||||||
|
|
||||||
|
def __exec_among (self, module, functions, min_times, max_times):
|
||||||
|
|
||||||
|
""" Randomly execute $functions between $min and $max times """
|
||||||
|
|
||||||
|
for i in xrange (random.randint (min_times, max_times)):
|
||||||
|
# Function names are mangled because they are "private"
|
||||||
|
getattr (module, "_XmlMutatorMin__" + random.choice(functions)) ()
|
||||||
|
|
||||||
|
def __serialize_xml (self, tree):
|
||||||
|
|
||||||
|
""" Serialize a XML document. Basic wrapper around lxml.tostring() """
|
||||||
|
|
||||||
|
return ET.tostring(tree, with_tail=False, xml_declaration=True, encoding=tree.docinfo.encoding)
|
||||||
|
|
||||||
|
def __ver (self, version):
|
||||||
|
|
||||||
|
""" Helper for displaying lxml version numbers """
|
||||||
|
|
||||||
|
return ".".join(map(str, version))
|
||||||
|
|
||||||
|
def reset (self):
|
||||||
|
|
||||||
|
""" Reset the mutator """
|
||||||
|
|
||||||
|
self.tree = deepcopy(self.input_tree)
|
||||||
|
|
||||||
|
def init_from_string (self, input_string):
|
||||||
|
|
||||||
|
""" Initialize the mutator from a XML string """
|
||||||
|
|
||||||
|
# Get a pointer to the top-element
|
||||||
|
self.input_tree = self.__parse_xml(input_string)
|
||||||
|
|
||||||
|
# Get a working copy
|
||||||
|
self.tree = deepcopy(self.input_tree)
|
||||||
|
|
||||||
|
def save_to_string (self):
|
||||||
|
|
||||||
|
""" Return the current XML document as UTF-8 string """
|
||||||
|
|
||||||
|
# Return a text version of the tree
|
||||||
|
return self.__serialize_xml(self.tree)
|
||||||
|
|
||||||
|
def __pick_element (self, exclude_root_node = False):
|
||||||
|
|
||||||
|
""" Pick a random element from the current document """
|
||||||
|
|
||||||
|
# Get a list of all elements, but nodes like PI and comments
|
||||||
|
elems = list(self.tree.getroot().iter(tag=ET.Element))
|
||||||
|
|
||||||
|
# Is the root node excluded?
|
||||||
|
if exclude_root_node:
|
||||||
|
start = 1
|
||||||
|
else:
|
||||||
|
start = 0
|
||||||
|
|
||||||
|
# Pick a random element
|
||||||
|
try:
|
||||||
|
elem_id = random.randint (start, len(elems) - 1)
|
||||||
|
elem = elems[elem_id]
|
||||||
|
except ValueError:
|
||||||
|
# Should only occurs if "exclude_root_node = True"
|
||||||
|
return (None, None)
|
||||||
|
|
||||||
|
return (elem_id, elem)
|
||||||
|
|
||||||
|
def __fuzz_attribute (self):
|
||||||
|
|
||||||
|
""" Fuzz (part of) an attribute value """
|
||||||
|
|
||||||
|
# Select a node to modify
|
||||||
|
(rand_elem_id, rand_elem) = self.__pick_element()
|
||||||
|
|
||||||
|
# Get all the attributes
|
||||||
|
attribs = rand_elem.keys()
|
||||||
|
|
||||||
|
# Is there attributes?
|
||||||
|
if len(attribs) < 1:
|
||||||
|
if self.verbose:
|
||||||
|
print("No attribute: can't replace!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Pick a random attribute
|
||||||
|
rand_attrib_id = random.randint (0, len(attribs) - 1)
|
||||||
|
rand_attrib = attribs[rand_attrib_id]
|
||||||
|
|
||||||
|
# We have the attribute to modify
|
||||||
|
# Get its value
|
||||||
|
attrib_value = rand_elem.get(rand_attrib);
|
||||||
|
# print("- Value: " + attrib_value)
|
||||||
|
|
||||||
|
# Should we work on the whole value?
|
||||||
|
func_call = "(?P<func>[a-zA-Z:\-]+)\((?P<args>.*?)\)"
|
||||||
|
p = re.compile(func_call)
|
||||||
|
l = p.findall(attrib_value)
|
||||||
|
if random.choice((True,False)) and l:
|
||||||
|
# Randomly pick one the function calls
|
||||||
|
(func, args) = random.choice(l)
|
||||||
|
# Split by "," and randomly pick one of the arguments
|
||||||
|
value = random.choice(args.split(','))
|
||||||
|
# Remove superfluous characters
|
||||||
|
unclean_value = value
|
||||||
|
value = value.strip(" ").strip("'")
|
||||||
|
# print("Selected argument: [%s]" % value)
|
||||||
|
else:
|
||||||
|
value = attrib_value
|
||||||
|
|
||||||
|
# For each type, define some possible replacement values
|
||||||
|
choices_number = ( \
|
||||||
|
"0", \
|
||||||
|
"11111", \
|
||||||
|
"-128", \
|
||||||
|
"2", \
|
||||||
|
"-1", \
|
||||||
|
"1/3", \
|
||||||
|
"42/0", \
|
||||||
|
"1094861636 idiv 1.0", \
|
||||||
|
"-1123329771506872 idiv 3.8", \
|
||||||
|
"17=$numericRTF", \
|
||||||
|
str(3 + random.randrange(0, 100)), \
|
||||||
|
)
|
||||||
|
|
||||||
|
choices_letter = ( \
|
||||||
|
"P" * (25 * random.randrange(1, 100)), \
|
||||||
|
"%s%s%s%s%s%s", \
|
||||||
|
"foobar", \
|
||||||
|
)
|
||||||
|
|
||||||
|
choices_alnum = ( \
|
||||||
|
"Abc123", \
|
||||||
|
"020F0302020204030204", \
|
||||||
|
"020F0302020204030204" * (random.randrange(5, 20)), \
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fuzz the value
|
||||||
|
if random.choice((True,False)) and value == "":
|
||||||
|
|
||||||
|
# Empty
|
||||||
|
new_value = value
|
||||||
|
|
||||||
|
elif random.choice((True,False)) and value.isdigit():
|
||||||
|
|
||||||
|
# Numbers
|
||||||
|
new_value = random.choice(choices_number)
|
||||||
|
|
||||||
|
elif random.choice((True,False)) and value.isalpha():
|
||||||
|
|
||||||
|
# Letters
|
||||||
|
new_value = random.choice(choices_letter)
|
||||||
|
|
||||||
|
elif random.choice((True,False)) and value.isalnum():
|
||||||
|
|
||||||
|
# Alphanumeric
|
||||||
|
new_value = random.choice(choices_alnum)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# Default type
|
||||||
|
new_value = random.choice(choices_alnum + choices_letter + choices_number)
|
||||||
|
|
||||||
|
# If we worked on a substring, apply changes to the whole string
|
||||||
|
if value != attrib_value:
|
||||||
|
# No ' around empty values
|
||||||
|
if new_value != "" and value != "":
|
||||||
|
new_value = "'" + new_value + "'"
|
||||||
|
# Apply changes
|
||||||
|
new_value = attrib_value.replace(unclean_value, new_value)
|
||||||
|
|
||||||
|
# Log something
|
||||||
|
if self.verbose:
|
||||||
|
print("Fuzzing attribute #%i '%s' of tag #%i '%s'" % (rand_attrib_id, rand_attrib, rand_elem_id, rand_elem.tag))
|
||||||
|
|
||||||
|
# Modify the attribute
|
||||||
|
rand_elem.set(rand_attrib, new_value.decode("utf-8"))
|
||||||
|
|
||||||
|
def __del_node_and_children (self):
|
||||||
|
|
||||||
|
""" High-level minimizing mutator
|
||||||
|
Delete a random node and its children (i.e. delete a random tree) """
|
||||||
|
|
||||||
|
self.__del_node(True)
|
||||||
|
|
||||||
|
def __del_node_but_children (self):
|
||||||
|
|
||||||
|
""" High-level minimizing mutator
|
||||||
|
Delete a random node but its children (i.e. link them to the parent of the deleted node) """
|
||||||
|
|
||||||
|
self.__del_node(False)
|
||||||
|
|
||||||
|
def __del_node (self, delete_children):
|
||||||
|
|
||||||
|
""" Called by the __del_node_* mutators """
|
||||||
|
|
||||||
|
# Select a node to modify (but the root one)
|
||||||
|
(rand_elem_id, rand_elem) = self.__pick_element (exclude_root_node = True)
|
||||||
|
|
||||||
|
# If the document includes only a top-level element
|
||||||
|
# Then we can't pick a element (given that "exclude_root_node = True")
|
||||||
|
|
||||||
|
# Is the document deep enough?
|
||||||
|
if rand_elem is None:
|
||||||
|
if self.verbose:
|
||||||
|
print("Can't delete a node: document not deep enough!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Log something
|
||||||
|
if self.verbose:
|
||||||
|
but_or_and = "and" if delete_children else "but"
|
||||||
|
print("Deleting tag #%i '%s' %s its children" % (rand_elem_id, rand_elem.tag, but_or_and))
|
||||||
|
|
||||||
|
if delete_children is False:
|
||||||
|
# Link children of the random (soon to be deleted) node to its parent
|
||||||
|
for child in rand_elem:
|
||||||
|
rand_elem.getparent().append(child)
|
||||||
|
|
||||||
|
# Remove the node
|
||||||
|
rand_elem.getparent().remove(rand_elem)
|
||||||
|
|
||||||
|
def __del_content (self):
|
||||||
|
|
||||||
|
""" High-level minimizing mutator
|
||||||
|
Delete the attributes and children of a random node """
|
||||||
|
|
||||||
|
# Select a node to modify
|
||||||
|
(rand_elem_id, rand_elem) = self.__pick_element()
|
||||||
|
|
||||||
|
# Log something
|
||||||
|
if self.verbose:
|
||||||
|
print("Reseting tag #%i '%s'" % (rand_elem_id, rand_elem.tag))
|
||||||
|
|
||||||
|
# Reset the node
|
||||||
|
rand_elem.clear()
|
||||||
|
|
||||||
|
def __del_attribute (self):
|
||||||
|
|
||||||
|
""" High-level minimizing mutator
|
||||||
|
Delete a random attribute from a random node """
|
||||||
|
|
||||||
|
# Select a node to modify
|
||||||
|
(rand_elem_id, rand_elem) = self.__pick_element()
|
||||||
|
|
||||||
|
# Get all the attributes
|
||||||
|
attribs = rand_elem.keys()
|
||||||
|
|
||||||
|
# Is there attributes?
|
||||||
|
if len(attribs) < 1:
|
||||||
|
if self.verbose:
|
||||||
|
print("No attribute: can't delete!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Pick a random attribute
|
||||||
|
rand_attrib_id = random.randint (0, len(attribs) - 1)
|
||||||
|
rand_attrib = attribs[rand_attrib_id]
|
||||||
|
|
||||||
|
# Log something
|
||||||
|
if self.verbose:
|
||||||
|
print("Deleting attribute #%i '%s' of tag #%i '%s'" % (rand_attrib_id, rand_attrib, rand_elem_id, rand_elem.tag))
|
||||||
|
|
||||||
|
# Delete the attribute
|
||||||
|
rand_elem.attrib.pop(rand_attrib)
|
||||||
|
|
||||||
|
def mutate (self, min=1, max=5):
|
||||||
|
|
||||||
|
""" Execute some high-level mutators between $min and $max times, then some medium-level ones """
|
||||||
|
|
||||||
|
# High-level mutation
|
||||||
|
self.__exec_among(self, self.hl_mutators_all, min, max)
|
||||||
|
|
117
python_mutators/wrapper_afl_min.py
Normal file
117
python_mutators/wrapper_afl_min.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from XmlMutatorMin import XmlMutatorMin
|
||||||
|
|
||||||
|
# Default settings (production mode)
|
||||||
|
|
||||||
|
__mutator__ = None
|
||||||
|
__seed__ = "RANDOM"
|
||||||
|
__log__ = False
|
||||||
|
__log_file__ = "wrapper.log"
|
||||||
|
|
||||||
|
# AFL functions
|
||||||
|
|
||||||
|
def log(text):
|
||||||
|
"""
|
||||||
|
Logger
|
||||||
|
"""
|
||||||
|
|
||||||
|
global __seed__
|
||||||
|
global __log__
|
||||||
|
global __log_file__
|
||||||
|
|
||||||
|
if __log__:
|
||||||
|
with open(__log_file__, "a") as logf:
|
||||||
|
logf.write("[%s] %s\n" % (__seed__, text))
|
||||||
|
|
||||||
|
def init(seed):
|
||||||
|
"""
|
||||||
|
Called once when AFL starts up. Seed is used to identify the AFL instance in log files
|
||||||
|
"""
|
||||||
|
|
||||||
|
global __mutator__
|
||||||
|
global __seed__
|
||||||
|
|
||||||
|
# Get the seed
|
||||||
|
__seed__ = seed
|
||||||
|
|
||||||
|
# Create a global mutation class
|
||||||
|
try:
|
||||||
|
__mutator__ = XmlMutatorMin(__seed__, verbose=__log__)
|
||||||
|
log("init(): Mutator created")
|
||||||
|
except RuntimeError as e:
|
||||||
|
log("init(): Can't create mutator: %s" % e.message)
|
||||||
|
|
||||||
|
def fuzz(buf, add_buf):
|
||||||
|
"""
|
||||||
|
Called for each fuzzing iteration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
global __mutator__
|
||||||
|
|
||||||
|
# Do we have a working mutator object?
|
||||||
|
if __mutator__ is None:
|
||||||
|
log("fuzz(): Can't fuzz, no mutator available")
|
||||||
|
return buf
|
||||||
|
|
||||||
|
# Try to use the AFL buffer
|
||||||
|
via_buffer = True
|
||||||
|
|
||||||
|
# Interpret the AFL buffer (an array of bytes) as a string
|
||||||
|
if via_buffer:
|
||||||
|
try:
|
||||||
|
buf_str = str(buf)
|
||||||
|
log("fuzz(): AFL buffer converted to a string")
|
||||||
|
except:
|
||||||
|
via_buffer = False
|
||||||
|
log("fuzz(): Can't convert AFL buffer to a string")
|
||||||
|
|
||||||
|
# Load XML from the AFL string
|
||||||
|
if via_buffer:
|
||||||
|
try:
|
||||||
|
__mutator__.init_from_string(buf_str)
|
||||||
|
log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str))
|
||||||
|
except:
|
||||||
|
via_buffer = False
|
||||||
|
log("fuzz(): Can't initialize mutator with AFL buffer")
|
||||||
|
|
||||||
|
# If init from AFL buffer wasn't succesful
|
||||||
|
if not via_buffer:
|
||||||
|
log("fuzz(): Returning unmodified AFL buffer")
|
||||||
|
return buf
|
||||||
|
|
||||||
|
# Sucessful initialization -> mutate
|
||||||
|
try:
|
||||||
|
__mutator__.mutate(max=5)
|
||||||
|
log("fuzz(): Input mutated")
|
||||||
|
except:
|
||||||
|
log("fuzz(): Can't mutate input => returning buf")
|
||||||
|
return buf
|
||||||
|
|
||||||
|
# Convert mutated data to a array of bytes
|
||||||
|
try:
|
||||||
|
data = bytearray(__mutator__.save_to_string())
|
||||||
|
log("fuzz(): Mutated data converted as bytes")
|
||||||
|
except:
|
||||||
|
log("fuzz(): Can't convert mutated data to bytes => returning buf")
|
||||||
|
return buf
|
||||||
|
|
||||||
|
# Everything went fine, returning mutated content
|
||||||
|
log("fuzz(): Returning %d bytes" % len(data))
|
||||||
|
return data
|
||||||
|
|
||||||
|
# Main (for debug)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
__log__ = True
|
||||||
|
__log_file__ = "/dev/stdout"
|
||||||
|
__seed__ = "RANDOM"
|
||||||
|
|
||||||
|
init(__seed__)
|
||||||
|
|
||||||
|
in_1 = bytearray("<foo ddd='eeee'>ffff<a b='c' d='456' eee='ffffff'>zzzzzzzzzzzz</a><b yyy='YYY' zzz='ZZZ'></b></foo>")
|
||||||
|
in_2 = bytearray("<abc abc123='456' abcCBA='ppppppppppppppppppppppppppppp'/>")
|
||||||
|
out = fuzz(in_1, in_2)
|
||||||
|
print(out)
|
||||||
|
|
@ -117,7 +117,7 @@ program control flow without actually executing each and every code path.
|
|||||||
If you want to experiment with this mode of operation, there is a module
|
If you want to experiment with this mode of operation, there is a module
|
||||||
contributed by Aleksandar Nikolich:
|
contributed by Aleksandar Nikolich:
|
||||||
|
|
||||||
https://github.com/vrtadmin/moflow/tree/master/afl-dyninst
|
https://github.com/vanhauser-thc/afl-dyninst
|
||||||
https://groups.google.com/forum/#!topic/afl-users/HlSQdbOTlpg
|
https://groups.google.com/forum/#!topic/afl-users/HlSQdbOTlpg
|
||||||
|
|
||||||
At this point, the author reports the possibility of hiccups with stripped
|
At this point, the author reports the possibility of hiccups with stripped
|
||||||
|
@ -133,6 +133,7 @@ patch -p1 <../patches/cpu-exec.diff || exit 1
|
|||||||
patch -p1 <../patches/syscall.diff || exit 1
|
patch -p1 <../patches/syscall.diff || exit 1
|
||||||
patch -p1 <../patches/translate-all.diff || exit 1
|
patch -p1 <../patches/translate-all.diff || exit 1
|
||||||
patch -p1 <../patches/tcg.diff || exit 1
|
patch -p1 <../patches/tcg.diff || exit 1
|
||||||
|
patch -p1 <../patches/i386-translate.diff || exit 1
|
||||||
|
|
||||||
echo "[+] Patching done."
|
echo "[+] Patching done."
|
||||||
|
|
||||||
|
42
qemu_mode/libcompcov/Makefile
Normal file
42
qemu_mode/libcompcov/Makefile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#
|
||||||
|
# american fuzzy lop - libcompcov
|
||||||
|
# --------------------------------
|
||||||
|
#
|
||||||
|
# Written by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
#
|
||||||
|
# Copyright 2019 Andrea Fioraldi. 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
|
||||||
|
CFLAGS += -Wall -Wno-unused-result -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign
|
||||||
|
LDFLAGS += -ldl
|
||||||
|
|
||||||
|
all: libcompcov.so compcovtest
|
||||||
|
|
||||||
|
libcompcov.so: libcompcov.so.c ../../config.h
|
||||||
|
$(CC) $(CFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
.NOTPARALLEL: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.so *~ a.out core core.[1-9][0-9]*
|
||||||
|
rm -f libcompcov.so compcovtest
|
||||||
|
|
||||||
|
compcovtest: compcovtest.cc
|
||||||
|
$(CXX) $< -o $@
|
||||||
|
|
||||||
|
install: all
|
||||||
|
install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH)
|
||||||
|
install -m 644 README.compcov $${DESTDIR}$(HELPER_PATH)
|
||||||
|
|
33
qemu_mode/libcompcov/README.compcov
Normal file
33
qemu_mode/libcompcov/README.compcov
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
================================================================
|
||||||
|
strcmp() / memcmp() CompareCoverage library for AFLplusplus-QEMU
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
Written by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
|
||||||
|
This Linux-only companion library allows you to instrument strcmp(), memcmp(),
|
||||||
|
and related functions to log the CompareCoverage of these libcalls.
|
||||||
|
|
||||||
|
Use this with caution. While this can speedup a lot the bypass of hard
|
||||||
|
branch conditions it can also waste a lot of time and take up unnecessary space
|
||||||
|
in the shared memory when logging the coverage related to functions that
|
||||||
|
doesn't process input-related data.
|
||||||
|
|
||||||
|
To use the library, you *need* to make sure that your fuzzing target is linked
|
||||||
|
dynamically and make use of strcmp(), memcmp(), and related functions.
|
||||||
|
For optimized binaries this is an issue, those functions are often inlined
|
||||||
|
and this module is not capable to log the coverage in this case.
|
||||||
|
|
||||||
|
If you have the source code of the fuzzing target you should nto use this
|
||||||
|
library and QEMU but build ot with afl-clang-fast and the laf-intel options.
|
||||||
|
|
||||||
|
To use this library make sure to preload it with AFL_PRELOAD.
|
||||||
|
|
||||||
|
export AFL_PRELOAD=/path/to/libcompcov.so
|
||||||
|
export AFL_QEMU_COMPCOV=1
|
||||||
|
|
||||||
|
afl-fuzz -Q -i input -o output <your options> -- <target args>
|
||||||
|
|
||||||
|
The library make use of https://github.com/ouadev/proc_maps_parser and so it is
|
||||||
|
Linux specific. However this is not a strict dependency, other UNIX operating
|
||||||
|
systems can be supported simply replacing the code related to the
|
||||||
|
/proc/self/maps parsing.
|
63
qemu_mode/libcompcov/compcovtest.cc
Normal file
63
qemu_mode/libcompcov/compcovtest.cc
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Author: Mateusz Jurczyk (mjurczyk@google.com)
|
||||||
|
//
|
||||||
|
// Copyright 2019 Google LLC
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
// https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
// solution: echo -ne 'The quick brown fox jumps over the lazy dog\xbe\xba\xfe\xca\xbe\xba\xfe\xca\xde\xc0\xad\xde\xef\xbe' | ./compcovtest
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
char buffer[44] = { /* zero padding */ };
|
||||||
|
fread(buffer, 1, sizeof(buffer) - 1, stdin);
|
||||||
|
|
||||||
|
if (memcmp(&buffer[0], "The quick brown fox ", 20) != 0 ||
|
||||||
|
strncmp(&buffer[20], "jumps over ", 11) != 0 ||
|
||||||
|
strcmp(&buffer[31], "the lazy dog") != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t x = 0;
|
||||||
|
fread(&x, sizeof(x), 1, stdin);
|
||||||
|
if (x != 0xCAFEBABECAFEBABE) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t y = 0;
|
||||||
|
fread(&y, sizeof(y), 1, stdin);
|
||||||
|
if (y != 0xDEADC0DE) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t z = 0;
|
||||||
|
fread(&z, sizeof(z), 1, stdin);
|
||||||
|
|
||||||
|
switch (z) {
|
||||||
|
case 0xBEEF:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Puzzle solved, congrats!\n");
|
||||||
|
abort();
|
||||||
|
return 0;
|
||||||
|
}
|
279
qemu_mode/libcompcov/libcompcov.so.c
Normal file
279
qemu_mode/libcompcov/libcompcov.so.c
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
american fuzzy lop++ - strcmp() / memcmp() CompareCoverage library
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
Written and maintained by Andrea Fioraldi <andreafioraldi@gmail.com>
|
||||||
|
|
||||||
|
Copyright 2019 Andrea Fioraldi. 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 get compare coverage.
|
||||||
|
See README.compcov for more info.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
|
||||||
|
#include "../../types.h"
|
||||||
|
#include "../../config.h"
|
||||||
|
|
||||||
|
#include "pmparser.h"
|
||||||
|
|
||||||
|
#ifndef __linux__
|
||||||
|
# error "Sorry, this library is Linux-specific for now!"
|
||||||
|
#endif /* !__linux__ */
|
||||||
|
|
||||||
|
/* Change this value to tune the compare coverage */
|
||||||
|
|
||||||
|
#define MAX_CMP_LENGTH 32
|
||||||
|
|
||||||
|
static void *__compcov_code_start,
|
||||||
|
*__compcov_code_end;
|
||||||
|
|
||||||
|
static u8 *__compcov_afl_map;
|
||||||
|
|
||||||
|
static int (*__libc_strcmp)(const char*, const char*);
|
||||||
|
static int (*__libc_strncmp)(const char*, const char*, size_t);
|
||||||
|
static int (*__libc_strcasecmp)(const char*, const char*);
|
||||||
|
static int (*__libc_strncasecmp)(const char*, const char*, size_t);
|
||||||
|
static int (*__libc_memcmp)(const void*, const void*, size_t);
|
||||||
|
|
||||||
|
static int debug_fd = -1;
|
||||||
|
|
||||||
|
|
||||||
|
static size_t __strlen2(const char *s1, const char *s2, size_t max_length) {
|
||||||
|
// from https://github.com/googleprojectzero/CompareCoverage
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
for (; len < max_length && s1[len] != '\0' && s2[len] != '\0'; len++) { }
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Identify the binary boundaries in the memory mapping */
|
||||||
|
|
||||||
|
static void __compcov_load(void) {
|
||||||
|
|
||||||
|
__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");
|
||||||
|
|
||||||
|
char *id_str = getenv(SHM_ENV_VAR);
|
||||||
|
int shm_id;
|
||||||
|
|
||||||
|
if (id_str) {
|
||||||
|
|
||||||
|
shm_id = atoi(id_str);
|
||||||
|
__compcov_afl_map = shmat(shm_id, NULL, 0);
|
||||||
|
|
||||||
|
if (__compcov_afl_map == (void*)-1) exit(1);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
__compcov_afl_map = calloc(1, MAP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getenv("AFL_INST_LIBS")) {
|
||||||
|
|
||||||
|
__compcov_code_start = (void*)0;
|
||||||
|
__compcov_code_end = (void*)-1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bin_name = getenv("AFL_COMPCOV_BINNAME");
|
||||||
|
|
||||||
|
procmaps_iterator* maps = pmparser_parse(-1);
|
||||||
|
procmaps_struct* maps_tmp = NULL;
|
||||||
|
|
||||||
|
while ((maps_tmp = pmparser_next(maps)) != NULL) {
|
||||||
|
|
||||||
|
/* If AFL_COMPCOV_BINNAME is not set pick the first executable segment */
|
||||||
|
if (!bin_name || strstr(maps_tmp->pathname, bin_name) != NULL) {
|
||||||
|
|
||||||
|
if (maps_tmp->is_x) {
|
||||||
|
if (!__compcov_code_start)
|
||||||
|
__compcov_code_start = maps_tmp->addr_start;
|
||||||
|
if (!__compcov_code_end)
|
||||||
|
__compcov_code_end = maps_tmp->addr_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pmparser_free(maps);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void __compcov_trace(u64 cur_loc, const u8* v0, const u8* v1, size_t n) {
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (debug_fd != 1) {
|
||||||
|
char debugbuf[4096];
|
||||||
|
snprintf(debugbuf, sizeof(debugbuf), "0x%llx %s %s %lu\n", cur_loc, v0 == NULL ? "(null)" : (char*)v0, v1 == NULL ? "(null)" : (char*)v1, n);
|
||||||
|
write(debug_fd, debugbuf, strlen(debugbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n && v0[i] == v1[i]; ++i) {
|
||||||
|
|
||||||
|
__compcov_afl_map[cur_loc +i]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check an address against the list of read-only mappings. */
|
||||||
|
|
||||||
|
static u8 __compcov_is_in_bound(const void* ptr) {
|
||||||
|
|
||||||
|
return ptr >= __compcov_code_start && ptr < __compcov_code_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
|
|
||||||
|
void* retaddr = __builtin_return_address(0);
|
||||||
|
|
||||||
|
if (__compcov_is_in_bound(retaddr)) {
|
||||||
|
|
||||||
|
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||||
|
|
||||||
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
|
u64 cur_loc = (u64)retaddr;
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return __libc_strcmp(str1, str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef strncmp
|
||||||
|
|
||||||
|
int strncmp(const char* str1, const char* str2, size_t len) {
|
||||||
|
|
||||||
|
void* retaddr = __builtin_return_address(0);
|
||||||
|
|
||||||
|
if (__compcov_is_in_bound(retaddr)) {
|
||||||
|
|
||||||
|
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||||
|
n = MIN(n, len);
|
||||||
|
|
||||||
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
|
u64 cur_loc = (u64)retaddr;
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return __libc_strncmp(str1, str2, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef strcasecmp
|
||||||
|
|
||||||
|
int strcasecmp(const char* str1, const char* str2) {
|
||||||
|
|
||||||
|
void* retaddr = __builtin_return_address(0);
|
||||||
|
|
||||||
|
if (__compcov_is_in_bound(retaddr)) {
|
||||||
|
/* Fallback to strcmp, maybe improve in future */
|
||||||
|
|
||||||
|
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||||
|
|
||||||
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
|
u64 cur_loc = (u64)retaddr;
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return __libc_strcasecmp(str1, str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef strncasecmp
|
||||||
|
|
||||||
|
int strncasecmp(const char* str1, const char* str2, size_t len) {
|
||||||
|
|
||||||
|
void* retaddr = __builtin_return_address(0);
|
||||||
|
|
||||||
|
if (__compcov_is_in_bound(retaddr)) {
|
||||||
|
/* Fallback to strncmp, maybe improve in future */
|
||||||
|
|
||||||
|
size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
|
||||||
|
n = MIN(n, len);
|
||||||
|
|
||||||
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
|
u64 cur_loc = (u64)retaddr;
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
__compcov_trace(cur_loc, str1, str2, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return __libc_strncasecmp(str1, str2, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef memcmp
|
||||||
|
|
||||||
|
int memcmp(const void* mem1, const void* mem2, size_t len) {
|
||||||
|
|
||||||
|
void* retaddr = __builtin_return_address(0);
|
||||||
|
|
||||||
|
if (__compcov_is_in_bound(retaddr)) {
|
||||||
|
|
||||||
|
size_t n = len;
|
||||||
|
|
||||||
|
if (n <= MAX_CMP_LENGTH) {
|
||||||
|
|
||||||
|
u64 cur_loc = (u64)retaddr;
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
__compcov_trace(cur_loc, mem1, mem2, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return __libc_memcmp(mem1, mem2, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init code to open init the library. */
|
||||||
|
|
||||||
|
__attribute__((constructor)) void __compcov_init(void) {
|
||||||
|
|
||||||
|
if (getenv("AFL_QEMU_COMPCOV_DEBUG") != NULL)
|
||||||
|
debug_fd = open("compcov.debug", O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
|
||||||
|
|
||||||
|
__compcov_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
280
qemu_mode/libcompcov/pmparser.h
Normal file
280
qemu_mode/libcompcov/pmparser.h
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
/*
|
||||||
|
@Author : ouadimjamal@gmail.com
|
||||||
|
@date : December 2015
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this software and its
|
||||||
|
documentation for any purpose is hereby granted without fee, provided that
|
||||||
|
the above copyright notice appear in all copies and that both that
|
||||||
|
copyright notice and this permission notice appear in supporting
|
||||||
|
documentation. No representations are made about the suitability of this
|
||||||
|
software for any purpose. It is provided "as is" without express or
|
||||||
|
implied warranty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef H_PMPARSER
|
||||||
|
#define H_PMPARSER
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
//maximum line length in a procmaps file
|
||||||
|
#define PROCMAPS_LINE_MAX_LENGTH (PATH_MAX + 100)
|
||||||
|
/**
|
||||||
|
* procmaps_struct
|
||||||
|
* @desc hold all the information about an area in the process's VM
|
||||||
|
*/
|
||||||
|
typedef struct procmaps_struct{
|
||||||
|
void* addr_start; //< start address of the area
|
||||||
|
void* addr_end; //< end address
|
||||||
|
unsigned long length; //< size of the range
|
||||||
|
|
||||||
|
char perm[5]; //< permissions rwxp
|
||||||
|
short is_r; //< rewrote of perm with short flags
|
||||||
|
short is_w;
|
||||||
|
short is_x;
|
||||||
|
short is_p;
|
||||||
|
|
||||||
|
long offset; //< offset
|
||||||
|
char dev[12]; //< dev major:minor
|
||||||
|
int inode; //< inode of the file that backs the area
|
||||||
|
|
||||||
|
char pathname[600]; //< the path of the file that backs the area
|
||||||
|
//chained list
|
||||||
|
struct procmaps_struct* next; //<handler of the chinaed list
|
||||||
|
} procmaps_struct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* procmaps_iterator
|
||||||
|
* @desc holds iterating information
|
||||||
|
*/
|
||||||
|
typedef struct procmaps_iterator{
|
||||||
|
procmaps_struct* head;
|
||||||
|
procmaps_struct* current;
|
||||||
|
} procmaps_iterator;
|
||||||
|
/**
|
||||||
|
* pmparser_parse
|
||||||
|
* @param pid the process id whose memory map to be parser. the current process if pid<0
|
||||||
|
* @return an iterator over all the nodes
|
||||||
|
*/
|
||||||
|
procmaps_iterator* pmparser_parse(int pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pmparser_next
|
||||||
|
* @description move between areas
|
||||||
|
* @param p_procmaps_it the iterator to move on step in the chained list
|
||||||
|
* @return a procmaps structure filled with information about this VM area
|
||||||
|
*/
|
||||||
|
procmaps_struct* pmparser_next(procmaps_iterator* p_procmaps_it);
|
||||||
|
/**
|
||||||
|
* pmparser_free
|
||||||
|
* @description should be called at the end to free the resources
|
||||||
|
* @param p_procmaps_it the iterator structure returned by pmparser_parse
|
||||||
|
*/
|
||||||
|
void pmparser_free(procmaps_iterator* p_procmaps_it);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _pmparser_split_line
|
||||||
|
* @description internal usage
|
||||||
|
*/
|
||||||
|
void _pmparser_split_line(char*buf,char*addr1,char*addr2,char*perm, char* offset, char* device,char*inode,char* pathname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pmparser_print
|
||||||
|
* @param map the head of the list
|
||||||
|
* @order the order of the area to print, -1 to print everything
|
||||||
|
*/
|
||||||
|
void pmparser_print(procmaps_struct* map,int order);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gobal variables
|
||||||
|
*/
|
||||||
|
//procmaps_struct* g_last_head=NULL;
|
||||||
|
//procmaps_struct* g_current=NULL;
|
||||||
|
|
||||||
|
|
||||||
|
procmaps_iterator* pmparser_parse(int pid){
|
||||||
|
procmaps_iterator* maps_it = malloc(sizeof(procmaps_iterator));
|
||||||
|
char maps_path[500];
|
||||||
|
if(pid>=0 ){
|
||||||
|
sprintf(maps_path,"/proc/%d/maps",pid);
|
||||||
|
}else{
|
||||||
|
sprintf(maps_path,"/proc/self/maps");
|
||||||
|
}
|
||||||
|
FILE* file=fopen(maps_path,"r");
|
||||||
|
if(!file){
|
||||||
|
fprintf(stderr,"pmparser : cannot open the memory maps, %s\n",strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int ind=0;char buf[PROCMAPS_LINE_MAX_LENGTH];
|
||||||
|
//int c;
|
||||||
|
procmaps_struct* list_maps=NULL;
|
||||||
|
procmaps_struct* tmp;
|
||||||
|
procmaps_struct* current_node=list_maps;
|
||||||
|
char addr1[20],addr2[20], perm[8], offset[20], dev[10],inode[30],pathname[PATH_MAX];
|
||||||
|
while( !feof(file) ){
|
||||||
|
fgets(buf,PROCMAPS_LINE_MAX_LENGTH,file);
|
||||||
|
//allocate a node
|
||||||
|
tmp=(procmaps_struct*)malloc(sizeof(procmaps_struct));
|
||||||
|
//fill the node
|
||||||
|
_pmparser_split_line(buf,addr1,addr2,perm,offset, dev,inode,pathname);
|
||||||
|
//printf("#%s",buf);
|
||||||
|
//printf("%s-%s %s %s %s %s\t%s\n",addr1,addr2,perm,offset,dev,inode,pathname);
|
||||||
|
//addr_start & addr_end
|
||||||
|
//unsigned long l_addr_start;
|
||||||
|
sscanf(addr1,"%lx",(long unsigned *)&tmp->addr_start );
|
||||||
|
sscanf(addr2,"%lx",(long unsigned *)&tmp->addr_end );
|
||||||
|
//size
|
||||||
|
tmp->length=(unsigned long)(tmp->addr_end-tmp->addr_start);
|
||||||
|
//perm
|
||||||
|
strcpy(tmp->perm,perm);
|
||||||
|
tmp->is_r=(perm[0]=='r');
|
||||||
|
tmp->is_w=(perm[1]=='w');
|
||||||
|
tmp->is_x=(perm[2]=='x');
|
||||||
|
tmp->is_p=(perm[3]=='p');
|
||||||
|
|
||||||
|
//offset
|
||||||
|
sscanf(offset,"%lx",&tmp->offset );
|
||||||
|
//device
|
||||||
|
strcpy(tmp->dev,dev);
|
||||||
|
//inode
|
||||||
|
tmp->inode=atoi(inode);
|
||||||
|
//pathname
|
||||||
|
strcpy(tmp->pathname,pathname);
|
||||||
|
tmp->next=NULL;
|
||||||
|
//attach the node
|
||||||
|
if(ind==0){
|
||||||
|
list_maps=tmp;
|
||||||
|
list_maps->next=NULL;
|
||||||
|
current_node=list_maps;
|
||||||
|
}
|
||||||
|
current_node->next=tmp;
|
||||||
|
current_node=tmp;
|
||||||
|
ind++;
|
||||||
|
//printf("%s",buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//close file
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
|
||||||
|
//g_last_head=list_maps;
|
||||||
|
maps_it->head = list_maps;
|
||||||
|
maps_it->current = list_maps;
|
||||||
|
return maps_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
procmaps_struct* pmparser_next(procmaps_iterator* p_procmaps_it){
|
||||||
|
if(p_procmaps_it->current == NULL)
|
||||||
|
return NULL;
|
||||||
|
procmaps_struct* p_current = p_procmaps_it->current;
|
||||||
|
p_procmaps_it->current = p_procmaps_it->current->next;
|
||||||
|
return p_current;
|
||||||
|
/*
|
||||||
|
if(g_current==NULL){
|
||||||
|
g_current=g_last_head;
|
||||||
|
}else
|
||||||
|
g_current=g_current->next;
|
||||||
|
|
||||||
|
return g_current;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void pmparser_free(procmaps_iterator* p_procmaps_it){
|
||||||
|
procmaps_struct* maps_list = p_procmaps_it->head;
|
||||||
|
if(maps_list==NULL) return ;
|
||||||
|
procmaps_struct* act=maps_list;
|
||||||
|
procmaps_struct* nxt=act->next;
|
||||||
|
while(act!=NULL){
|
||||||
|
free(act);
|
||||||
|
act=nxt;
|
||||||
|
if(nxt!=NULL)
|
||||||
|
nxt=nxt->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _pmparser_split_line(
|
||||||
|
char*buf,char*addr1,char*addr2,
|
||||||
|
char*perm,char* offset,char* device,char*inode,
|
||||||
|
char* pathname){
|
||||||
|
//
|
||||||
|
int orig=0;
|
||||||
|
int i=0;
|
||||||
|
//addr1
|
||||||
|
while(buf[i]!='-'){
|
||||||
|
addr1[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
addr1[i]='\0';
|
||||||
|
i++;
|
||||||
|
//addr2
|
||||||
|
orig=i;
|
||||||
|
while(buf[i]!='\t' && buf[i]!=' '){
|
||||||
|
addr2[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
addr2[i-orig]='\0';
|
||||||
|
|
||||||
|
//perm
|
||||||
|
while(buf[i]=='\t' || buf[i]==' ')
|
||||||
|
i++;
|
||||||
|
orig=i;
|
||||||
|
while(buf[i]!='\t' && buf[i]!=' '){
|
||||||
|
perm[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
perm[i-orig]='\0';
|
||||||
|
//offset
|
||||||
|
while(buf[i]=='\t' || buf[i]==' ')
|
||||||
|
i++;
|
||||||
|
orig=i;
|
||||||
|
while(buf[i]!='\t' && buf[i]!=' '){
|
||||||
|
offset[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
offset[i-orig]='\0';
|
||||||
|
//dev
|
||||||
|
while(buf[i]=='\t' || buf[i]==' ')
|
||||||
|
i++;
|
||||||
|
orig=i;
|
||||||
|
while(buf[i]!='\t' && buf[i]!=' '){
|
||||||
|
device[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
device[i-orig]='\0';
|
||||||
|
//inode
|
||||||
|
while(buf[i]=='\t' || buf[i]==' ')
|
||||||
|
i++;
|
||||||
|
orig=i;
|
||||||
|
while(buf[i]!='\t' && buf[i]!=' '){
|
||||||
|
inode[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
inode[i-orig]='\0';
|
||||||
|
//pathname
|
||||||
|
pathname[0]='\0';
|
||||||
|
while(buf[i]=='\t' || buf[i]==' ')
|
||||||
|
i++;
|
||||||
|
orig=i;
|
||||||
|
while(buf[i]!='\t' && buf[i]!=' ' && buf[i]!='\n'){
|
||||||
|
pathname[i-orig]=buf[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
pathname[i-orig]='\0';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
|
|
||||||
|
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||||
<andreafioraldi@gmail.com>
|
<andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||||
@ -65,6 +66,8 @@ abi_ulong afl_entry_point, /* ELF entry point (_start) */
|
|||||||
afl_start_code, /* .text start pointer */
|
afl_start_code, /* .text start pointer */
|
||||||
afl_end_code; /* .text end pointer */
|
afl_end_code; /* .text end pointer */
|
||||||
|
|
||||||
|
u8 afl_enable_compcov;
|
||||||
|
|
||||||
/* Set in the child process in forkserver mode: */
|
/* Set in the child process in forkserver mode: */
|
||||||
|
|
||||||
static int forkserver_installed = 0;
|
static int forkserver_installed = 0;
|
||||||
@ -147,7 +150,6 @@ static void afl_setup(void) {
|
|||||||
|
|
||||||
if (inst_r) afl_area_ptr[0] = 1;
|
if (inst_r) afl_area_ptr[0] = 1;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("AFL_INST_LIBS")) {
|
if (getenv("AFL_INST_LIBS")) {
|
||||||
@ -156,6 +158,11 @@ static void afl_setup(void) {
|
|||||||
afl_end_code = (abi_ulong)-1;
|
afl_end_code = (abi_ulong)-1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getenv("AFL_QEMU_COMPCOV")) {
|
||||||
|
|
||||||
|
afl_enable_compcov = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
|
/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
|
||||||
not entirely sure what is the cause. This disables that
|
not entirely sure what is the cause. This disables that
|
||||||
@ -271,6 +278,25 @@ static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags, ui
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check if an address is valid in the current mapping */
|
||||||
|
|
||||||
|
static inline int is_valid_addr(target_ulong addr) {
|
||||||
|
|
||||||
|
int l, flags;
|
||||||
|
target_ulong page;
|
||||||
|
void * p;
|
||||||
|
|
||||||
|
page = addr & TARGET_PAGE_MASK;
|
||||||
|
l = (page + TARGET_PAGE_SIZE) - addr;
|
||||||
|
|
||||||
|
flags = page_get_flags(page);
|
||||||
|
if (!(flags & PAGE_VALID) || !(flags & PAGE_READ))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* This is the other side of the same channel. Since timeouts are handled by
|
/* This is the other side of the same channel. Since timeouts are handled by
|
||||||
afl-fuzz simply killing the child, we can just wait until the pipe breaks. */
|
afl-fuzz simply killing the child, we can just wait until the pipe breaks. */
|
||||||
|
|
||||||
@ -282,6 +308,8 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
u8 invalid_pc = 0;
|
||||||
|
|
||||||
/* Broken pipe means it's time to return to the fork server routine. */
|
/* Broken pipe means it's time to return to the fork server routine. */
|
||||||
|
|
||||||
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||||
@ -290,19 +318,34 @@ static void afl_wait_tsl(CPUState *cpu, int fd) {
|
|||||||
tb = tb_htable_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
|
tb = tb_htable_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
|
||||||
|
|
||||||
if(!tb) {
|
if(!tb) {
|
||||||
mmap_lock();
|
|
||||||
tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, 0);
|
/* The child may request to transate a block of memory that is not
|
||||||
mmap_unlock();
|
mapped in the parent (e.g. jitted code or dlopened code).
|
||||||
|
This causes a SIGSEV in gen_intermediate_code() and associated
|
||||||
|
subroutines. We simply avoid caching of such blocks. */
|
||||||
|
|
||||||
|
if (is_valid_addr(t.tb.pc)) {
|
||||||
|
|
||||||
|
mmap_lock();
|
||||||
|
tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, 0);
|
||||||
|
mmap_unlock();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
invalid_pc = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t.is_chain) {
|
if (t.is_chain) {
|
||||||
if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
last_tb = tb_htable_lookup(cpu, c.last_tb.pc, c.last_tb.cs_base,
|
if (!invalid_pc) {
|
||||||
c.last_tb.flags, c.cf_mask);
|
|
||||||
if (last_tb) {
|
last_tb = tb_htable_lookup(cpu, c.last_tb.pc, c.last_tb.cs_base,
|
||||||
tb_add_jump(last_tb, c.tb_exit, tb);
|
c.last_tb.flags, c.cf_mask);
|
||||||
|
if (last_tb) {
|
||||||
|
tb_add_jump(last_tb, c.tb_exit, tb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
125
qemu_mode/patches/afl-qemu-cpu-translate-inl.h
Normal file
125
qemu_mode/patches/afl-qemu-cpu-translate-inl.h
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
american fuzzy lop - high-performance binary-only instrumentation
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
|
Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
|
Idea & design very much by Andrew Griffiths.
|
||||||
|
|
||||||
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
|
<andrea.biondo965@gmail.com>
|
||||||
|
|
||||||
|
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||||
|
<andreafioraldi@gmail.com>
|
||||||
|
|
||||||
|
Copyright 2015, 2016, 2017 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
|
||||||
|
|
||||||
|
This code is a shim patched into the separately-distributed source
|
||||||
|
code of QEMU 3.1.0. It leverages the built-in QEMU tracing functionality
|
||||||
|
to implement AFL-style instrumentation and to take care of the remaining
|
||||||
|
parts of the AFL fork server logic.
|
||||||
|
|
||||||
|
The resulting QEMU binary is essentially a standalone instrumentation
|
||||||
|
tool; for an example of how to leverage it for other purposes, you can
|
||||||
|
have a look at afl-showmap.c.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../../config.h"
|
||||||
|
#include "tcg.h"
|
||||||
|
#include "tcg-op.h"
|
||||||
|
|
||||||
|
/* Declared in afl-qemu-cpu-inl.h */
|
||||||
|
extern unsigned char *afl_area_ptr;
|
||||||
|
extern unsigned int afl_inst_rms;
|
||||||
|
extern abi_ulong afl_start_code, afl_end_code;
|
||||||
|
extern u8 afl_enable_compcov;
|
||||||
|
|
||||||
|
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
|
||||||
|
TCGv_i64 arg1, TCGv_i64 arg2);
|
||||||
|
|
||||||
|
static void afl_compcov_log_16(target_ulong cur_loc, target_ulong arg1,
|
||||||
|
target_ulong arg2) {
|
||||||
|
|
||||||
|
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||||
|
afl_area_ptr[cur_loc]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void afl_compcov_log_32(target_ulong cur_loc, target_ulong arg1,
|
||||||
|
target_ulong arg2) {
|
||||||
|
|
||||||
|
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||||
|
afl_area_ptr[cur_loc]++;
|
||||||
|
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||||
|
afl_area_ptr[cur_loc +1]++;
|
||||||
|
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
||||||
|
afl_area_ptr[cur_loc +2]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
|
||||||
|
target_ulong arg2) {
|
||||||
|
|
||||||
|
if ((arg1 & 0xff) == (arg2 & 0xff)) {
|
||||||
|
afl_area_ptr[cur_loc]++;
|
||||||
|
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
|
||||||
|
afl_area_ptr[cur_loc +1]++;
|
||||||
|
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
|
||||||
|
afl_area_ptr[cur_loc +2]++;
|
||||||
|
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
|
||||||
|
afl_area_ptr[cur_loc +3]++;
|
||||||
|
if ((arg1 & 0xffffffffff) == (arg2 & 0xffffffffff)) {
|
||||||
|
afl_area_ptr[cur_loc +4]++;
|
||||||
|
if ((arg1 & 0xffffffffffff) == (arg2 & 0xffffffffffff)) {
|
||||||
|
afl_area_ptr[cur_loc +5]++;
|
||||||
|
if ((arg1 & 0xffffffffffffff) == (arg2 & 0xffffffffffffff)) {
|
||||||
|
afl_area_ptr[cur_loc +6]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
|
||||||
|
TCGMemOp ot) {
|
||||||
|
|
||||||
|
void *func;
|
||||||
|
|
||||||
|
if (!afl_enable_compcov || cur_loc > afl_end_code || cur_loc < afl_start_code)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (ot) {
|
||||||
|
case MO_64:
|
||||||
|
func = &afl_compcov_log_64;
|
||||||
|
break;
|
||||||
|
case MO_32:
|
||||||
|
func = &afl_compcov_log_32;
|
||||||
|
break;
|
||||||
|
case MO_16:
|
||||||
|
func = &afl_compcov_log_16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
if (cur_loc >= afl_inst_rms) return;
|
||||||
|
|
||||||
|
tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
|
||||||
|
}
|
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
|
|
||||||
|
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||||
<andreafioraldi@gmail.com>
|
<andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||||
@ -42,10 +43,10 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
|
|||||||
unsigned sizemask, flags;
|
unsigned sizemask, flags;
|
||||||
TCGOp *op;
|
TCGOp *op;
|
||||||
|
|
||||||
TCGTemp *arg = tcgv_ptr_temp( tcg_const_tl(cur_loc) );
|
TCGTemp *arg = tcgv_i64_temp( tcg_const_tl(cur_loc) );
|
||||||
|
|
||||||
flags = 0;
|
flags = 0;
|
||||||
sizemask = dh_sizemask(void, 0) | dh_sizemask(ptr, 1);
|
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
|
||||||
|
|
||||||
#if defined(__sparc__) && !defined(__arch64__) \
|
#if defined(__sparc__) && !defined(__arch64__) \
|
||||||
&& !defined(CONFIG_TCG_INTERPRETER)
|
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||||
@ -151,7 +152,7 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
|
|||||||
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
||||||
Note that describing these as TCGv_i64 eliminates an unnecessary
|
Note that describing these as TCGv_i64 eliminates an unnecessary
|
||||||
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
||||||
tcg_gen_concat32_i64(temp_tcgv_i64(ret), retl, reth);
|
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
|
||||||
tcg_temp_free_i64(retl);
|
tcg_temp_free_i64(retl);
|
||||||
tcg_temp_free_i64(reth);
|
tcg_temp_free_i64(reth);
|
||||||
}
|
}
|
||||||
@ -163,3 +164,143 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
|
|||||||
#endif /* TCG_TARGET_EXTEND_ARGS */
|
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2)
|
||||||
|
{
|
||||||
|
int i, real_args, nb_rets, pi;
|
||||||
|
unsigned sizemask, flags;
|
||||||
|
TCGOp *op;
|
||||||
|
|
||||||
|
const int nargs = 3;
|
||||||
|
TCGTemp *args[3] = { tcgv_i64_temp( tcg_const_tl(cur_loc) ),
|
||||||
|
tcgv_i64_temp(arg1),
|
||||||
|
tcgv_i64_temp(arg2) };
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
|
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1) |
|
||||||
|
dh_sizemask(i64, 2) | dh_sizemask(i64, 3);
|
||||||
|
|
||||||
|
#if defined(__sparc__) && !defined(__arch64__) \
|
||||||
|
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||||
|
/* We have 64-bit values in one register, but need to pass as two
|
||||||
|
separate parameters. Split them. */
|
||||||
|
int orig_sizemask = sizemask;
|
||||||
|
int orig_nargs = nargs;
|
||||||
|
TCGv_i64 retl, reth;
|
||||||
|
TCGTemp *split_args[MAX_OPC_PARAM];
|
||||||
|
|
||||||
|
retl = NULL;
|
||||||
|
reth = NULL;
|
||||||
|
if (sizemask != 0) {
|
||||||
|
for (i = real_args = 0; i < nargs; ++i) {
|
||||||
|
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||||
|
if (is_64bit) {
|
||||||
|
TCGv_i64 orig = temp_tcgv_i64(args[i]);
|
||||||
|
TCGv_i32 h = tcg_temp_new_i32();
|
||||||
|
TCGv_i32 l = tcg_temp_new_i32();
|
||||||
|
tcg_gen_extr_i64_i32(l, h, orig);
|
||||||
|
split_args[real_args++] = tcgv_i32_temp(h);
|
||||||
|
split_args[real_args++] = tcgv_i32_temp(l);
|
||||||
|
} else {
|
||||||
|
split_args[real_args++] = args[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nargs = real_args;
|
||||||
|
args = split_args;
|
||||||
|
sizemask = 0;
|
||||||
|
}
|
||||||
|
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||||
|
for (i = 0; i < nargs; ++i) {
|
||||||
|
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||||
|
int is_signed = sizemask & (2 << (i+1)*2);
|
||||||
|
if (!is_64bit) {
|
||||||
|
TCGv_i64 temp = tcg_temp_new_i64();
|
||||||
|
TCGv_i64 orig = temp_tcgv_i64(args[i]);
|
||||||
|
if (is_signed) {
|
||||||
|
tcg_gen_ext32s_i64(temp, orig);
|
||||||
|
} else {
|
||||||
|
tcg_gen_ext32u_i64(temp, orig);
|
||||||
|
}
|
||||||
|
args[i] = tcgv_i64_temp(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||||
|
|
||||||
|
op = tcg_emit_op(INDEX_op_call);
|
||||||
|
|
||||||
|
pi = 0;
|
||||||
|
nb_rets = 0;
|
||||||
|
TCGOP_CALLO(op) = nb_rets;
|
||||||
|
|
||||||
|
real_args = 0;
|
||||||
|
for (i = 0; i < nargs; i++) {
|
||||||
|
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||||
|
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
|
||||||
|
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
|
||||||
|
/* some targets want aligned 64 bit args */
|
||||||
|
if (real_args & 1) {
|
||||||
|
op->args[pi++] = TCG_CALL_DUMMY_ARG;
|
||||||
|
real_args++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* If stack grows up, then we will be placing successive
|
||||||
|
arguments at lower addresses, which means we need to
|
||||||
|
reverse the order compared to how we would normally
|
||||||
|
treat either big or little-endian. For those arguments
|
||||||
|
that will wind up in registers, this still works for
|
||||||
|
HPPA (the only current STACK_GROWSUP target) since the
|
||||||
|
argument registers are *also* allocated in decreasing
|
||||||
|
order. If another such target is added, this logic may
|
||||||
|
have to get more complicated to differentiate between
|
||||||
|
stack arguments and register arguments. */
|
||||||
|
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
|
||||||
|
op->args[pi++] = temp_arg(args[i] + 1);
|
||||||
|
op->args[pi++] = temp_arg(args[i]);
|
||||||
|
#else
|
||||||
|
op->args[pi++] = temp_arg(args[i]);
|
||||||
|
op->args[pi++] = temp_arg(args[i] + 1);
|
||||||
|
#endif
|
||||||
|
real_args += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
op->args[pi++] = temp_arg(args[i]);
|
||||||
|
real_args++;
|
||||||
|
}
|
||||||
|
op->args[pi++] = (uintptr_t)func;
|
||||||
|
op->args[pi++] = flags;
|
||||||
|
TCGOP_CALLI(op) = real_args;
|
||||||
|
|
||||||
|
/* Make sure the fields didn't overflow. */
|
||||||
|
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
|
||||||
|
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
|
||||||
|
|
||||||
|
#if defined(__sparc__) && !defined(__arch64__) \
|
||||||
|
&& !defined(CONFIG_TCG_INTERPRETER)
|
||||||
|
/* Free all of the parts we allocated above. */
|
||||||
|
for (i = real_args = 0; i < orig_nargs; ++i) {
|
||||||
|
int is_64bit = orig_sizemask & (1 << (i+1)*2);
|
||||||
|
if (is_64bit) {
|
||||||
|
tcg_temp_free_internal(args[real_args++]);
|
||||||
|
tcg_temp_free_internal(args[real_args++]);
|
||||||
|
} else {
|
||||||
|
real_args++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (orig_sizemask & 1) {
|
||||||
|
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
|
||||||
|
Note that describing these as TCGv_i64 eliminates an unnecessary
|
||||||
|
zero-extension that tcg_gen_concat_i32_i64 would create. */
|
||||||
|
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
|
||||||
|
tcg_temp_free_i64(retl);
|
||||||
|
tcg_temp_free_i64(reth);
|
||||||
|
}
|
||||||
|
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
|
||||||
|
for (i = 0; i < nargs; ++i) {
|
||||||
|
int is_64bit = sizemask & (1 << (i+1)*2);
|
||||||
|
if (!is_64bit) {
|
||||||
|
tcg_temp_free_internal(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* TCG_TARGET_EXTEND_ARGS */
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
TCG instrumentation and block chaining support by Andrea Biondo
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
<andrea.biondo965@gmail.com>
|
<andrea.biondo965@gmail.com>
|
||||||
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
|
|
||||||
|
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
|
||||||
<andreafioraldi@gmail.com>
|
<andreafioraldi@gmail.com>
|
||||||
|
|
||||||
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
|
||||||
@ -41,12 +42,12 @@ extern abi_ulong afl_start_code, afl_end_code;
|
|||||||
|
|
||||||
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
|
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
|
||||||
|
|
||||||
void afl_maybe_log(void* cur_loc) {
|
void afl_maybe_log(target_ulong cur_loc) {
|
||||||
|
|
||||||
static __thread abi_ulong prev_loc;
|
static __thread abi_ulong prev_loc;
|
||||||
|
|
||||||
afl_area_ptr[(abi_ulong)cur_loc ^ prev_loc]++;
|
afl_area_ptr[cur_loc ^ prev_loc]++;
|
||||||
prev_loc = (abi_ulong)cur_loc >> 1;
|
prev_loc = cur_loc >> 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ static void afl_gen_trace(target_ulong cur_loc) {
|
|||||||
if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer
|
if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Looks like QEMU always maps to fixed locations, so ASAN is not a
|
/* Looks like QEMU always maps to fixed locations, so ASLR is not a
|
||||||
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
||||||
the value to get something quasi-uniform. */
|
the value to get something quasi-uniform. */
|
||||||
|
|
||||||
|
33
qemu_mode/patches/i386-translate.diff
Normal file
33
qemu_mode/patches/i386-translate.diff
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
diff --git a/target/i386/translate.c b/target/i386/translate.c
|
||||||
|
index 0dd5fbe4..b95d341e 100644
|
||||||
|
--- a/target/i386/translate.c
|
||||||
|
+++ b/target/i386/translate.c
|
||||||
|
@@ -32,6 +32,8 @@
|
||||||
|
#include "trace-tcg.h"
|
||||||
|
#include "exec/log.h"
|
||||||
|
|
||||||
|
+#include "../patches/afl-qemu-cpu-translate-inl.h"
|
||||||
|
+
|
||||||
|
#define PREFIX_REPZ 0x01
|
||||||
|
#define PREFIX_REPNZ 0x02
|
||||||
|
#define PREFIX_LOCK 0x04
|
||||||
|
@@ -1343,9 +1345,11 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
|
||||||
|
tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0,
|
||||||
|
s1->mem_index, ot | MO_LE);
|
||||||
|
tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1);
|
||||||
|
+ afl_gen_compcov(s1->pc, s1->cc_srcT, s1->T1, ot);
|
||||||
|
} else {
|
||||||
|
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
||||||
|
tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
|
||||||
|
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
|
||||||
|
gen_op_st_rm_T0_A0(s1, ot, d);
|
||||||
|
}
|
||||||
|
gen_op_update2_cc(s1);
|
||||||
|
@@ -1389,6 +1393,7 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
|
||||||
|
tcg_gen_mov_tl(cpu_cc_src, s1->T1);
|
||||||
|
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
|
||||||
|
tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1);
|
||||||
|
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
|
||||||
|
set_cc_op(s1, CC_OP_SUBB + ot);
|
||||||
|
break;
|
||||||
|
}
|
137
sharedmem.c
Normal file
137
sharedmem.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AFL_MAIN
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "alloc-inl.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "sharedmem.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#ifndef USEMMAP
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern unsigned char*trace_bits;
|
||||||
|
|
||||||
|
#ifdef USEMMAP
|
||||||
|
/* ================ Proteas ================ */
|
||||||
|
int g_shm_fd = -1;
|
||||||
|
unsigned char *g_shm_base = NULL;
|
||||||
|
char g_shm_file_path[L_tmpnam];
|
||||||
|
/* ========================================= */
|
||||||
|
#else
|
||||||
|
static s32 shm_id; /* ID of the SHM region */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Get rid of shared memory (atexit handler). */
|
||||||
|
|
||||||
|
void remove_shm(void) {
|
||||||
|
#ifdef USEMMAP
|
||||||
|
if (g_shm_base != NULL) {
|
||||||
|
munmap(g_shm_base, MAP_SIZE);
|
||||||
|
g_shm_base = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_shm_fd != -1) {
|
||||||
|
close(g_shm_fd);
|
||||||
|
g_shm_fd = -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
shmctl(shm_id, IPC_RMID, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Configure shared memory. */
|
||||||
|
|
||||||
|
void setup_shm(unsigned char dumb_mode) {
|
||||||
|
#ifdef USEMMAP
|
||||||
|
/* generate random file name for multi instance */
|
||||||
|
|
||||||
|
/* thanks to f*cking glibc we can not use tmpnam securely, it generates a security warning that cannot be suppressed */
|
||||||
|
/* so we do this worse workaround */
|
||||||
|
snprintf(g_shm_file_path, L_tmpnam, "/afl_%d_%ld", getpid(), random());
|
||||||
|
|
||||||
|
/* create the shared memory segment as if it was a file */
|
||||||
|
g_shm_fd = shm_open(g_shm_file_path, O_CREAT | O_RDWR | O_EXCL, 0600);
|
||||||
|
if (g_shm_fd == -1) {
|
||||||
|
PFATAL("shm_open() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* configure the size of the shared memory segment */
|
||||||
|
if (ftruncate(g_shm_fd, MAP_SIZE)) {
|
||||||
|
PFATAL("setup_shm(): ftruncate() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* map the shared memory segment to the address space of the process */
|
||||||
|
g_shm_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_shm_fd, 0);
|
||||||
|
if (g_shm_base == MAP_FAILED) {
|
||||||
|
close(g_shm_fd);
|
||||||
|
g_shm_fd = -1;
|
||||||
|
PFATAL("mmap() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
atexit(remove_shm);
|
||||||
|
|
||||||
|
/* If somebody is asking us to fuzz instrumented binaries in dumb mode,
|
||||||
|
we don't want them to detect instrumentation, since we won't be sending
|
||||||
|
fork server commands. This should be replaced with better auto-detection
|
||||||
|
later on, perhaps? */
|
||||||
|
|
||||||
|
if (!dumb_mode) setenv(SHM_ENV_VAR, g_shm_file_path, 1);
|
||||||
|
|
||||||
|
trace_bits = g_shm_base;
|
||||||
|
|
||||||
|
if (!trace_bits) PFATAL("mmap() failed");
|
||||||
|
|
||||||
|
#else
|
||||||
|
u8* shm_str;
|
||||||
|
|
||||||
|
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
|
||||||
|
|
||||||
|
if (shm_id < 0) PFATAL("shmget() failed");
|
||||||
|
|
||||||
|
atexit(remove_shm);
|
||||||
|
|
||||||
|
shm_str = alloc_printf("%d", shm_id);
|
||||||
|
|
||||||
|
setenv(SHM_ENV_VAR, shm_str, 1);
|
||||||
|
|
||||||
|
/* If somebody is asking us to fuzz instrumented binaries in dumb mode,
|
||||||
|
we don't want them to detect instrumentation, since we won't be sending
|
||||||
|
fork server commands. This should be replaced with better auto-detection
|
||||||
|
later on, perhaps? */
|
||||||
|
|
||||||
|
if (!dumb_mode) setenv(SHM_ENV_VAR, shm_str, 1);
|
||||||
|
|
||||||
|
ck_free(shm_str);
|
||||||
|
|
||||||
|
trace_bits = shmat(shm_id, NULL, 0);
|
||||||
|
|
||||||
|
if (!trace_bits) PFATAL("shmat() failed");
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
6
sharedmem.h
Normal file
6
sharedmem.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __SHAREDMEM_H
|
||||||
|
#define __SHAREDMEM_H
|
||||||
|
|
||||||
|
void setup_shm(unsigned char dumb_mode);
|
||||||
|
void remove_shm(void);
|
||||||
|
#endif
|
11
types.h
11
types.h
@ -78,9 +78,14 @@ typedef int64_t s64;
|
|||||||
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
|
||||||
|
|
||||||
#define MEM_BARRIER() \
|
#define MEM_BARRIER() \
|
||||||
asm volatile("" ::: "memory")
|
__asm__ volatile("" ::: "memory")
|
||||||
|
|
||||||
#define likely(_x) __builtin_expect(!!(_x), 1)
|
#if __GNUC__ < 6
|
||||||
#define unlikely(_x) __builtin_expect(!!(_x), 0)
|
#define likely(_x) (_x)
|
||||||
|
#define unlikely(_x) (_x)
|
||||||
|
#else
|
||||||
|
#define likely(_x) __builtin_expect(!!(_x), 1)
|
||||||
|
#define unlikely(_x) __builtin_expect(!!(_x), 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* ! _HAVE_TYPES_H */
|
#endif /* ! _HAVE_TYPES_H */
|
||||||
|
21
unicorn_mode/README.md
Normal file
21
unicorn_mode/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
```
|
||||||
|
__ _ _
|
||||||
|
__ _ / _| | _ _ _ __ (_) ___ ___ _ __ _ __
|
||||||
|
/ _` | |_| |___| | | | '_ \| |/ __/ _ \| '__| '_ \
|
||||||
|
| (_| | _| |___| |_| | | | | | (_| (_) | | | | | |
|
||||||
|
\__,_|_| |_| \__,_|_| |_|_|\___\___/|_| |_| |_|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
afl-unicorn lets you fuzz any piece of binary that can be emulated by
|
||||||
|
[Unicorn Engine](http://www.unicorn-engine.org/).
|
||||||
|
|
||||||
|
For the full readme please see docs/unicorn_mode.txt
|
||||||
|
|
||||||
|
For an in-depth description of what this is, how to install it, and how to use
|
||||||
|
it check out this [blog post](https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf).
|
||||||
|
|
||||||
|
For general help with AFL, please refer to the documents in the ./docs/ directory.
|
||||||
|
|
||||||
|
Created by Nathan Voss, originally funded by
|
||||||
|
[Battelle](https://www.battelle.org/cyber).
|
191
unicorn_mode/build_unicorn_support.sh
Normal file
191
unicorn_mode/build_unicorn_support.sh
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# american fuzzy lop - Unicorn-Mode build script
|
||||||
|
# --------------------------------------
|
||||||
|
#
|
||||||
|
# Written by Nathan Voss <njvoss99@gmail.com>
|
||||||
|
#
|
||||||
|
# Adapted from code by Andrew Griffiths <agriffiths@google.com> and
|
||||||
|
# Michal Zalewski <lcamtuf@google.com>
|
||||||
|
#
|
||||||
|
# Adapted for Afl++ by Dominik Maier <mail@dmnk.co>
|
||||||
|
#
|
||||||
|
# Copyright 2017 Battelle Memorial Institute. 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 script downloads, patches, and builds a version of Unicorn with
|
||||||
|
# minor tweaks to allow Unicorn-emulated binaries to be run under
|
||||||
|
# afl-fuzz.
|
||||||
|
#
|
||||||
|
# The modifications reside in patches/*. The standalone Unicorn library
|
||||||
|
# will be written to /usr/lib/libunicornafl.so, and the Python bindings
|
||||||
|
# will be installed system-wide.
|
||||||
|
#
|
||||||
|
# You must make sure that Unicorn Engine is not already installed before
|
||||||
|
# running this script. If it is, please uninstall it first.
|
||||||
|
|
||||||
|
UNICORN_URL="https://github.com/unicorn-engine/unicorn/archive/24f55a7973278f20f0de21b904851d99d4716263.tar.gz"
|
||||||
|
UNICORN_SHA384="7180d47ca52c99b4c073a343a2ead91da1a829fdc3809f3ceada5d872e162962eab98873a8bc7971449d5f34f41fdb93"
|
||||||
|
|
||||||
|
echo "================================================="
|
||||||
|
echo "Unicorn-AFL build script"
|
||||||
|
echo "================================================="
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "[*] Performing basic sanity checks..."
|
||||||
|
|
||||||
|
if [ ! "`uname -s`" = "Linux" ]; then
|
||||||
|
|
||||||
|
echo "[-] Error: Unicorn instrumentation is supported only on Linux."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "patches/afl-unicorn-cpu-inl.h" -o ! -f "../config.h" ]; then
|
||||||
|
|
||||||
|
echo "[-] Error: key files not found - wrong working directory?"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "../afl-showmap" ]; then
|
||||||
|
|
||||||
|
echo "[-] Error: ../afl-showmap not found - compile AFL first!"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in wget python automake autoconf sha384sum; do
|
||||||
|
|
||||||
|
T=`which "$i" 2>/dev/null`
|
||||||
|
|
||||||
|
if [ "$T" = "" ]; then
|
||||||
|
|
||||||
|
echo "[-] Error: '$i' not found. Run 'sudo apt-get install $i'."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! which easy_install > /dev/null; then
|
||||||
|
|
||||||
|
# work around for unusual installs
|
||||||
|
if [ '!' -e /usr/lib/python2.7/dist-packages/easy_install.py ]; then
|
||||||
|
|
||||||
|
echo "[-] Error: Python setup-tools not found. Run 'sudo apt-get install python-setuptools'."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CC" | grep -qF /afl-; then
|
||||||
|
|
||||||
|
echo "[-] Error: do not use afl-gcc or afl-clang to compile this tool."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[+] All checks passed!"
|
||||||
|
|
||||||
|
ARCHIVE="`basename -- "$UNICORN_URL"`"
|
||||||
|
|
||||||
|
CKSUM=`sha384sum -- "$ARCHIVE" 2>/dev/null | cut -d' ' -f1`
|
||||||
|
|
||||||
|
if [ ! "$CKSUM" = "$UNICORN_SHA384" ]; then
|
||||||
|
|
||||||
|
echo "[*] Downloading Unicorn v1.0.1 from the web..."
|
||||||
|
rm -f "$ARCHIVE"
|
||||||
|
wget -O "$ARCHIVE" -- "$UNICORN_URL" || exit 1
|
||||||
|
|
||||||
|
CKSUM=`sha384sum -- "$ARCHIVE" 2>/dev/null | cut -d' ' -f1`
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CKSUM" = "$UNICORN_SHA384" ]; then
|
||||||
|
|
||||||
|
echo "[+] Cryptographic signature on $ARCHIVE checks out."
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
echo "[-] Error: signature mismatch on $ARCHIVE (perhaps download error?)."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[*] Uncompressing archive (this will take a while)..."
|
||||||
|
|
||||||
|
rm -rf "unicorn" || exit 1
|
||||||
|
mkdir "unicorn" || exit 1
|
||||||
|
tar xzf "$ARCHIVE" -C ./unicorn --strip-components=1 || exit 1
|
||||||
|
|
||||||
|
echo "[+] Unpacking successful."
|
||||||
|
|
||||||
|
rm -rf "$ARCHIVE" || exit 1
|
||||||
|
|
||||||
|
echo "[*] Applying patches..."
|
||||||
|
|
||||||
|
cp patches/afl-unicorn-cpu-inl.h unicorn || exit 1
|
||||||
|
patch -p1 --directory unicorn <patches/patches.diff || exit 1
|
||||||
|
|
||||||
|
echo "[+] Patching done."
|
||||||
|
|
||||||
|
echo "[*] Configuring Unicorn build..."
|
||||||
|
|
||||||
|
cd "unicorn" || exit 1
|
||||||
|
|
||||||
|
echo "[+] Configuration complete."
|
||||||
|
|
||||||
|
echo "[*] Attempting to build Unicorn (fingers crossed!)..."
|
||||||
|
|
||||||
|
UNICORN_QEMU_FLAGS='--python=python2' make || exit 1
|
||||||
|
|
||||||
|
echo "[+] Build process successful!"
|
||||||
|
|
||||||
|
echo "[*] Installing Unicorn python bindings..."
|
||||||
|
cd bindings/python || exit 1
|
||||||
|
if [ -z "$VIRTUAL_ENV" ]; then
|
||||||
|
echo "[*] Info: Installing python unicorn using --user"
|
||||||
|
python setup.py install --user || exit 1
|
||||||
|
else
|
||||||
|
echo "[*] Info: Installing python unicorn to virtualenv: $VIRTUAL_ENV"
|
||||||
|
python setup.py install || exit 1
|
||||||
|
fi
|
||||||
|
export LIBUNICORN_PATH='$(pwd)' # in theory, this allows to switch between afl-unicorn and unicorn so files.
|
||||||
|
|
||||||
|
cd ../../ || exit 1
|
||||||
|
|
||||||
|
echo "[+] Unicorn bindings installed successfully."
|
||||||
|
|
||||||
|
# Compile the sample, run it, verify that it works!
|
||||||
|
echo "[*] Testing unicorn-mode functionality by running a sample test harness under afl-unicorn"
|
||||||
|
|
||||||
|
cd ../samples/simple || exit 1
|
||||||
|
|
||||||
|
# Run afl-showmap on the sample application. If anything comes out then it must have worked!
|
||||||
|
unset AFL_INST_RATIO
|
||||||
|
echo 0 | ../../../afl-showmap -U -m none -q -o .test-instr0 -- python simple_test_harness.py ./sample_inputs/sample1.bin || exit 1
|
||||||
|
|
||||||
|
if [ -s .test-instr0 ]
|
||||||
|
then
|
||||||
|
|
||||||
|
echo "[+] Instrumentation tests passed. "
|
||||||
|
echo "[+] All set, you can now use Unicorn mode (-U) in afl-fuzz!"
|
||||||
|
RETVAL=0
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
echo "[-] Error: Unicorn mode doesn't seem to work!"
|
||||||
|
RETVAL=1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f .test-instr0
|
||||||
|
|
||||||
|
exit $RETVAL
|
104
unicorn_mode/helper_scripts/template_test_harness.py
Normal file
104
unicorn_mode/helper_scripts/template_test_harness.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
"""
|
||||||
|
template_test_harness.py
|
||||||
|
|
||||||
|
Template which loads the context of a process into a Unicorn Engine,
|
||||||
|
instance, loads a custom (mutated) inputs, and executes the
|
||||||
|
desired code. Designed to be used in conjunction with one of the
|
||||||
|
Unicorn Context Dumper scripts.
|
||||||
|
|
||||||
|
Author:
|
||||||
|
Nathan Voss <njvoss299@gmail.com>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from unicorn import *
|
||||||
|
from unicorn.x86_const import * # TODO: Set correct architecture here as necessary
|
||||||
|
|
||||||
|
import unicorn_loader
|
||||||
|
|
||||||
|
# Simple stand-in heap to prevent OS/kernel issues
|
||||||
|
unicorn_heap = None
|
||||||
|
|
||||||
|
# Start and end address of emulation
|
||||||
|
START_ADDRESS = # TODO: Set start address here
|
||||||
|
END_ADDRESS = # TODO: Set end address here
|
||||||
|
|
||||||
|
"""
|
||||||
|
Implement target-specific hooks in here.
|
||||||
|
Stub out, skip past, and re-implement necessary functionality as appropriate
|
||||||
|
"""
|
||||||
|
def unicorn_hook_instruction(uc, address, size, user_data):
|
||||||
|
|
||||||
|
# TODO: Setup hooks and handle anything you need to here
|
||||||
|
# - For example, hook malloc/free/etc. and handle it internally
|
||||||
|
pass
|
||||||
|
|
||||||
|
#------------------------
|
||||||
|
#---- Main test function
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('context_dir', type=str, help="Directory containing process context")
|
||||||
|
parser.add_argument('input_file', type=str, help="Path to the file containing the mutated input content")
|
||||||
|
parser.add_argument('-d', '--debug', default=False, action="store_true", help="Dump trace info")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
print("Loading context from {}".format(args.context_dir))
|
||||||
|
uc = unicorn_loader.AflUnicornEngine(args.context_dir, enable_trace=args.debug, debug_print=False)
|
||||||
|
|
||||||
|
# Instantiate the hook function to avoid emulation errors
|
||||||
|
global unicorn_heap
|
||||||
|
unicorn_heap = unicorn_loader.UnicornSimpleHeap(uc, debug_print=True)
|
||||||
|
uc.hook_add(UC_HOOK_CODE, unicorn_hook_instruction)
|
||||||
|
|
||||||
|
# Execute 1 instruction just to startup the forkserver
|
||||||
|
# NOTE: This instruction will be executed again later, so be sure that
|
||||||
|
# there are no negative consequences to the overall execution state.
|
||||||
|
# If there are, change the later call to emu_start to no re-execute
|
||||||
|
# the first instruction.
|
||||||
|
print("Starting the forkserver by executing 1 instruction")
|
||||||
|
try:
|
||||||
|
uc.emu_start(START_ADDRESS, 0, 0, count=1)
|
||||||
|
except UcError as e:
|
||||||
|
print("ERROR: Failed to execute a single instruction (error: {})!".format(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Allocate a buffer and load a mutated input and put it into the right spot
|
||||||
|
if args.input_file:
|
||||||
|
print("Loading input content from {}".format(args.input_file))
|
||||||
|
input_file = open(args.input_file, 'rb')
|
||||||
|
input_content = input_file.read()
|
||||||
|
input_file.close()
|
||||||
|
|
||||||
|
# TODO: Apply constraints to mutated input here
|
||||||
|
raise exceptions.NotImplementedError('No constraints on the mutated inputs have been set!')
|
||||||
|
|
||||||
|
# Allocate a new buffer and put the input into it
|
||||||
|
buf_addr = unicorn_heap.malloc(len(input_content))
|
||||||
|
uc.mem_write(buf_addr, input_content)
|
||||||
|
print("Allocated mutated input buffer @ 0x{0:016x}".format(buf_addr))
|
||||||
|
|
||||||
|
# TODO: Set the input into the state so it will be handled
|
||||||
|
raise exceptions.NotImplementedError('The mutated input was not loaded into the Unicorn state!')
|
||||||
|
|
||||||
|
# Run the test
|
||||||
|
print("Executing from 0x{0:016x} to 0x{1:016x}".format(START_ADDRESS, END_ADDRESS))
|
||||||
|
try:
|
||||||
|
result = uc.emu_start(START_ADDRESS, END_ADDRESS, timeout=0, count=0)
|
||||||
|
except UcError as e:
|
||||||
|
# If something went wrong during emulation a signal is raised to force this
|
||||||
|
# script to crash in a way that AFL can detect ('uc.force_crash()' should be
|
||||||
|
# called for any condition that you want AFL to treat as a crash).
|
||||||
|
print("Execution failed with error: {}".format(e))
|
||||||
|
uc.dump_regs()
|
||||||
|
uc.force_crash(e)
|
||||||
|
|
||||||
|
print("Final register state:")
|
||||||
|
uc.dump_regs()
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
190
unicorn_mode/helper_scripts/unicorn_dumper_gdb.py
Normal file
190
unicorn_mode/helper_scripts/unicorn_dumper_gdb.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
"""
|
||||||
|
unicorn_dumper_gdb.py
|
||||||
|
|
||||||
|
When run with GDB sitting at a debug breakpoint, this
|
||||||
|
dumps the current state (registers/memory/etc) of
|
||||||
|
the process to a directory consisting of an index
|
||||||
|
file with register and segment information and
|
||||||
|
sub-files containing all actual process memory.
|
||||||
|
|
||||||
|
The output of this script is expected to be used
|
||||||
|
to initialize context for Unicorn emulation.
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
In order to run this script, GEF needs to be running in the GDB session (gef.py)
|
||||||
|
# HELPERS from: https://github.com/hugsy/gef/blob/master/gef.py
|
||||||
|
It can be loaded with:
|
||||||
|
source <path_to_gef>/gef.py
|
||||||
|
|
||||||
|
Call this function when at a breakpoint in your process with:
|
||||||
|
source unicorn_dumper_gdb.py
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# GDB Python SDK
|
||||||
|
import gdb
|
||||||
|
|
||||||
|
# Maximum segment size that we'll store
|
||||||
|
# Yep, this could break stuff pretty quickly if we
|
||||||
|
# omit something that's used during emulation.
|
||||||
|
MAX_SEG_SIZE = 128 * 1024 * 1024
|
||||||
|
|
||||||
|
# Name of the index file
|
||||||
|
INDEX_FILE_NAME = "_index.json"
|
||||||
|
|
||||||
|
#----------------------
|
||||||
|
#---- Helper Functions
|
||||||
|
|
||||||
|
def map_arch():
|
||||||
|
arch = get_arch() # from GEF
|
||||||
|
if 'x86_64' in arch or 'x86-64' in arch:
|
||||||
|
return "x64"
|
||||||
|
elif 'x86' in arch or 'i386' in arch:
|
||||||
|
return "x86"
|
||||||
|
elif 'aarch64' in arch or 'arm64' in arch:
|
||||||
|
return "arm64le"
|
||||||
|
elif 'aarch64_be' in arch:
|
||||||
|
return "arm64be"
|
||||||
|
elif 'armeb' in arch:
|
||||||
|
# check for THUMB mode
|
||||||
|
cpsr = get_register('cpsr')
|
||||||
|
if (cpsr & (1 << 5)):
|
||||||
|
return "armbethumb"
|
||||||
|
else:
|
||||||
|
return "armbe"
|
||||||
|
elif 'arm' in arch:
|
||||||
|
# check for THUMB mode
|
||||||
|
cpsr = get_register('cpsr')
|
||||||
|
if (cpsr & (1 << 5)):
|
||||||
|
return "armlethumb"
|
||||||
|
else:
|
||||||
|
return "armle"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------
|
||||||
|
#---- Dumping functions
|
||||||
|
|
||||||
|
def dump_arch_info():
|
||||||
|
arch_info = {}
|
||||||
|
arch_info["arch"] = map_arch()
|
||||||
|
return arch_info
|
||||||
|
|
||||||
|
|
||||||
|
def dump_regs():
|
||||||
|
reg_state = {}
|
||||||
|
for reg in current_arch.all_registers:
|
||||||
|
reg_val = get_register(reg)
|
||||||
|
# current dumper script looks for register values to be hex strings
|
||||||
|
# reg_str = "0x{:08x}".format(reg_val)
|
||||||
|
# if "64" in get_arch():
|
||||||
|
# reg_str = "0x{:016x}".format(reg_val)
|
||||||
|
# reg_state[reg.strip().strip('$')] = reg_str
|
||||||
|
reg_state[reg.strip().strip('$')] = reg_val
|
||||||
|
return reg_state
|
||||||
|
|
||||||
|
|
||||||
|
def dump_process_memory(output_dir):
|
||||||
|
# Segment information dictionary
|
||||||
|
final_segment_list = []
|
||||||
|
|
||||||
|
# GEF:
|
||||||
|
vmmap = get_process_maps()
|
||||||
|
if not vmmap:
|
||||||
|
print("No address mapping information found")
|
||||||
|
return final_segment_list
|
||||||
|
|
||||||
|
for entry in vmmap:
|
||||||
|
if entry.page_start == entry.page_end:
|
||||||
|
continue
|
||||||
|
|
||||||
|
seg_info = {'start': entry.page_start, 'end': entry.page_end, 'name': entry.path, 'permissions': {
|
||||||
|
"r": entry.is_readable() > 0,
|
||||||
|
"w": entry.is_writable() > 0,
|
||||||
|
"x": entry.is_executable() > 0
|
||||||
|
}, 'content_file': ''}
|
||||||
|
|
||||||
|
# "(deleted)" may or may not be valid, but don't push it.
|
||||||
|
if entry.is_readable() and not '(deleted)' in entry.path:
|
||||||
|
try:
|
||||||
|
# Compress and dump the content to a file
|
||||||
|
seg_content = read_memory(entry.page_start, entry.size)
|
||||||
|
if(seg_content == None):
|
||||||
|
print("Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format(entry.page_start, entry.path))
|
||||||
|
else:
|
||||||
|
print("Dumping segment @0x{0:016x} (size:0x{1:x}): {2} [{3}]".format(entry.page_start, len(seg_content), entry.path, repr(seg_info['permissions'])))
|
||||||
|
compressed_seg_content = zlib.compress(seg_content)
|
||||||
|
md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin"
|
||||||
|
seg_info["content_file"] = md5_sum
|
||||||
|
|
||||||
|
# Write the compressed contents to disk
|
||||||
|
out_file = open(os.path.join(output_dir, md5_sum), 'wb')
|
||||||
|
out_file.write(compressed_seg_content)
|
||||||
|
out_file.close()
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("Exception reading segment ({}): {}".format(entry.path, sys.exc_info()[0]))
|
||||||
|
else:
|
||||||
|
print("Skipping segment {0}@0x{1:016x}".format(entry.path, entry.page_start))
|
||||||
|
|
||||||
|
# Add the segment to the list
|
||||||
|
final_segment_list.append(seg_info)
|
||||||
|
|
||||||
|
|
||||||
|
return final_segment_list
|
||||||
|
|
||||||
|
#----------
|
||||||
|
#---- Main
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("----- Unicorn Context Dumper -----")
|
||||||
|
print("You must be actively debugging before running this!")
|
||||||
|
print("If it fails, double check that you are actively debugging before running.")
|
||||||
|
try:
|
||||||
|
GEF_TEST = set_arch()
|
||||||
|
except Exception as e:
|
||||||
|
print("!!! GEF not running in GDB. Please run gef.py by executing:")
|
||||||
|
print('\tpython execfile ("<path_to_gef>/gef.py")')
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Create the output directory
|
||||||
|
timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d_%H%M%S')
|
||||||
|
output_path = "UnicornContext_" + timestamp
|
||||||
|
if not os.path.exists(output_path):
|
||||||
|
os.makedirs(output_path)
|
||||||
|
print("Process context will be output to {}".format(output_path))
|
||||||
|
|
||||||
|
# Get the context
|
||||||
|
context = {
|
||||||
|
"arch": dump_arch_info(),
|
||||||
|
"regs": dump_regs(),
|
||||||
|
"segments": dump_process_memory(output_path),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write the index file
|
||||||
|
index_file = open(os.path.join(output_path, INDEX_FILE_NAME), 'w')
|
||||||
|
index_file.write(json.dumps(context, indent=4))
|
||||||
|
index_file.close()
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("!!! ERROR:\n\t{}".format(repr(e)))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
209
unicorn_mode/helper_scripts/unicorn_dumper_ida.py
Normal file
209
unicorn_mode/helper_scripts/unicorn_dumper_ida.py
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
"""
|
||||||
|
unicorn_dumper_ida.py
|
||||||
|
|
||||||
|
When run with IDA (<v7) sitting at a debug breakpoint,
|
||||||
|
dumps the current state (registers/memory/etc) of
|
||||||
|
the process to a directory consisting of an index
|
||||||
|
file with register and segment information and
|
||||||
|
sub-files containing all actual process memory.
|
||||||
|
|
||||||
|
The output of this script is expected to be used
|
||||||
|
to initialize context for Unicorn emulation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# IDA Python SDK
|
||||||
|
from idaapi import *
|
||||||
|
from idc import *
|
||||||
|
|
||||||
|
# Maximum segment size that we'll store
|
||||||
|
# Yep, this could break stuff pretty quickly if we
|
||||||
|
# omit something that's used during emulation.
|
||||||
|
MAX_SEG_SIZE = 128 * 1024 * 1024
|
||||||
|
|
||||||
|
# Name of the index file
|
||||||
|
INDEX_FILE_NAME = "_index.json"
|
||||||
|
|
||||||
|
#----------------------
|
||||||
|
#---- Helper Functions
|
||||||
|
|
||||||
|
def get_arch():
|
||||||
|
if ph.id == PLFM_386 and ph.flag & PR_USE64:
|
||||||
|
return "x64"
|
||||||
|
elif ph.id == PLFM_386 and ph.flag & PR_USE32:
|
||||||
|
return "x86"
|
||||||
|
elif ph.id == PLFM_ARM and ph.flag & PR_USE64:
|
||||||
|
if cvar.inf.is_be():
|
||||||
|
return "arm64be"
|
||||||
|
else:
|
||||||
|
return "arm64le"
|
||||||
|
elif ph.id == PLFM_ARM and ph.flag & PR_USE32:
|
||||||
|
if cvar.inf.is_be():
|
||||||
|
return "armbe"
|
||||||
|
else:
|
||||||
|
return "armle"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_register_list(arch):
|
||||||
|
if arch == "arm64le" or arch == "arm64be":
|
||||||
|
arch = "arm64"
|
||||||
|
elif arch == "armle" or arch == "armbe":
|
||||||
|
arch = "arm"
|
||||||
|
|
||||||
|
registers = {
|
||||||
|
"x64" : [
|
||||||
|
"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp",
|
||||||
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||||
|
"rip", "rsp", "efl",
|
||||||
|
"cs", "ds", "es", "fs", "gs", "ss",
|
||||||
|
],
|
||||||
|
"x86" : [
|
||||||
|
"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
|
||||||
|
"eip", "esp", "efl",
|
||||||
|
"cs", "ds", "es", "fs", "gs", "ss",
|
||||||
|
],
|
||||||
|
"arm" : [
|
||||||
|
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
|
||||||
|
"R8", "R9", "R10", "R11", "R12", "PC", "SP", "LR",
|
||||||
|
"PSR",
|
||||||
|
],
|
||||||
|
"arm64" : [
|
||||||
|
"X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7",
|
||||||
|
"X8", "X9", "X10", "X11", "X12", "X13", "X14",
|
||||||
|
"X15", "X16", "X17", "X18", "X19", "X20", "X21",
|
||||||
|
"X22", "X23", "X24", "X25", "X26", "X27", "X28",
|
||||||
|
"PC", "SP", "FP", "LR", "CPSR"
|
||||||
|
# "NZCV",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return registers[arch]
|
||||||
|
|
||||||
|
#-----------------------
|
||||||
|
#---- Dumping functions
|
||||||
|
|
||||||
|
def dump_arch_info():
|
||||||
|
arch_info = {}
|
||||||
|
arch_info["arch"] = get_arch()
|
||||||
|
return arch_info
|
||||||
|
|
||||||
|
def dump_regs():
|
||||||
|
reg_state = {}
|
||||||
|
for reg in get_register_list(get_arch()):
|
||||||
|
reg_state[reg] = GetRegValue(reg)
|
||||||
|
return reg_state
|
||||||
|
|
||||||
|
def dump_process_memory(output_dir):
|
||||||
|
# Segment information dictionary
|
||||||
|
segment_list = []
|
||||||
|
|
||||||
|
# Loop over the segments, fill in the info dictionary
|
||||||
|
for seg_ea in Segments():
|
||||||
|
seg_start = SegStart(seg_ea)
|
||||||
|
seg_end = SegEnd(seg_ea)
|
||||||
|
seg_size = seg_end - seg_start
|
||||||
|
|
||||||
|
seg_info = {}
|
||||||
|
seg_info["name"] = SegName(seg_ea)
|
||||||
|
seg_info["start"] = seg_start
|
||||||
|
seg_info["end"] = seg_end
|
||||||
|
|
||||||
|
perms = getseg(seg_ea).perm
|
||||||
|
seg_info["permissions"] = {
|
||||||
|
"r": False if (perms & SEGPERM_READ) == 0 else True,
|
||||||
|
"w": False if (perms & SEGPERM_WRITE) == 0 else True,
|
||||||
|
"x": False if (perms & SEGPERM_EXEC) == 0 else True,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perms & SEGPERM_READ) and seg_size <= MAX_SEG_SIZE and isLoaded(seg_start):
|
||||||
|
try:
|
||||||
|
# Compress and dump the content to a file
|
||||||
|
seg_content = get_many_bytes(seg_start, seg_end - seg_start)
|
||||||
|
if(seg_content == None):
|
||||||
|
print("Segment empty: {0}@0x{1:016x} (size:UNKNOWN)".format(SegName(seg_ea), seg_ea))
|
||||||
|
seg_info["content_file"] = ""
|
||||||
|
else:
|
||||||
|
print("Dumping segment {0}@0x{1:016x} (size:{2})".format(SegName(seg_ea), seg_ea, len(seg_content)))
|
||||||
|
compressed_seg_content = zlib.compress(seg_content)
|
||||||
|
md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin"
|
||||||
|
seg_info["content_file"] = md5_sum
|
||||||
|
|
||||||
|
# Write the compressed contents to disk
|
||||||
|
out_file = open(os.path.join(output_dir, md5_sum), 'wb')
|
||||||
|
out_file.write(compressed_seg_content)
|
||||||
|
out_file.close()
|
||||||
|
except:
|
||||||
|
print("Exception reading segment: {}".format(sys.exc_info()[0]))
|
||||||
|
seg_info["content_file"] = ""
|
||||||
|
else:
|
||||||
|
print("Skipping segment {0}@0x{1:016x}".format(SegName(seg_ea), seg_ea))
|
||||||
|
seg_info["content_file"] = ""
|
||||||
|
|
||||||
|
# Add the segment to the list
|
||||||
|
segment_list.append(seg_info)
|
||||||
|
|
||||||
|
return segment_list
|
||||||
|
|
||||||
|
"""
|
||||||
|
TODO: FINISH IMPORT DUMPING
|
||||||
|
def import_callback(ea, name, ord):
|
||||||
|
if not name:
|
||||||
|
else:
|
||||||
|
|
||||||
|
# True -> Continue enumeration
|
||||||
|
# False -> End enumeration
|
||||||
|
return True
|
||||||
|
|
||||||
|
def dump_imports():
|
||||||
|
import_dict = {}
|
||||||
|
|
||||||
|
for i in xrange(0, number_of_import_modules):
|
||||||
|
enum_import_names(i, import_callback)
|
||||||
|
|
||||||
|
return import_dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
#----------
|
||||||
|
#---- Main
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("----- Unicorn Context Dumper -----")
|
||||||
|
print("You must be actively debugging before running this!")
|
||||||
|
print("If it fails, double check that you are actively debugging before running.")
|
||||||
|
|
||||||
|
# Create the output directory
|
||||||
|
timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d_%H%M%S')
|
||||||
|
output_path = os.path.dirname(os.path.abspath(GetIdbPath()))
|
||||||
|
output_path = os.path.join(output_path, "UnicornContext_" + timestamp)
|
||||||
|
if not os.path.exists(output_path):
|
||||||
|
os.makedirs(output_path)
|
||||||
|
print("Process context will be output to {}".format(output_path))
|
||||||
|
|
||||||
|
# Get the context
|
||||||
|
context = {
|
||||||
|
"arch": dump_arch_info(),
|
||||||
|
"regs": dump_regs(),
|
||||||
|
"segments": dump_process_memory(output_path),
|
||||||
|
#"imports": dump_imports(),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write the index file
|
||||||
|
index_file = open(os.path.join(output_path, INDEX_FILE_NAME), 'w')
|
||||||
|
index_file.write(json.dumps(context, indent=4))
|
||||||
|
index_file.close()
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
print("!!! ERROR:\n\t{}".format(str(e)))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
299
unicorn_mode/helper_scripts/unicorn_dumper_lldb.py
Normal file
299
unicorn_mode/helper_scripts/unicorn_dumper_lldb.py
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
"""
|
||||||
|
unicorn_dumper_lldb.py
|
||||||
|
|
||||||
|
When run with LLDB sitting at a debug breakpoint, this
|
||||||
|
dumps the current state (registers/memory/etc) of
|
||||||
|
the process to a directory consisting of an index
|
||||||
|
file with register and segment information and
|
||||||
|
sub-files containing all actual process memory.
|
||||||
|
|
||||||
|
The output of this script is expected to be used
|
||||||
|
to initialize context for Unicorn emulation.
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Call this function when at a breakpoint in your process with:
|
||||||
|
command script import -r unicorn_dumper_lldb
|
||||||
|
|
||||||
|
If there is trouble with "split on a NoneType", issue the following command:
|
||||||
|
script lldb.target.triple
|
||||||
|
|
||||||
|
and try to import the script again.
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# LLDB Python SDK
|
||||||
|
import lldb
|
||||||
|
|
||||||
|
# Maximum segment size that we'll store
|
||||||
|
# Yep, this could break stuff pretty quickly if we
|
||||||
|
# omit something that's used during emulation.
|
||||||
|
MAX_SEG_SIZE = 128 * 1024 * 1024
|
||||||
|
|
||||||
|
# Name of the index file
|
||||||
|
INDEX_FILE_NAME = "_index.json"
|
||||||
|
DEBUG_MEM_FILE_NAME = "_memory.json"
|
||||||
|
|
||||||
|
# Page size required by Unicorn
|
||||||
|
UNICORN_PAGE_SIZE = 0x1000
|
||||||
|
|
||||||
|
# Alignment functions to align all memory segments to Unicorn page boundaries (4KB pages only)
|
||||||
|
ALIGN_PAGE_DOWN = lambda x: x & ~(UNICORN_PAGE_SIZE - 1)
|
||||||
|
ALIGN_PAGE_UP = lambda x: (x + UNICORN_PAGE_SIZE - 1) & ~(UNICORN_PAGE_SIZE-1)
|
||||||
|
|
||||||
|
#----------------------
|
||||||
|
#---- Helper Functions
|
||||||
|
|
||||||
|
def overlap_alignments(segments, memory):
|
||||||
|
final_list = []
|
||||||
|
curr_seg_idx = 0
|
||||||
|
curr_end_addr = 0
|
||||||
|
curr_node = None
|
||||||
|
current_segment = None
|
||||||
|
sorted_segments = sorted(segments, key=lambda k: (k['start'], k['end']))
|
||||||
|
if curr_seg_idx < len(sorted_segments):
|
||||||
|
current_segment = sorted_segments[curr_seg_idx]
|
||||||
|
for mem in sorted(memory, key=lambda k: (k['start'], -k['end'])):
|
||||||
|
if curr_node is None:
|
||||||
|
if current_segment is not None and current_segment['start'] == mem['start']:
|
||||||
|
curr_node = deepcopy(current_segment)
|
||||||
|
curr_node['permissions'] = mem['permissions']
|
||||||
|
else:
|
||||||
|
curr_node = deepcopy(mem)
|
||||||
|
|
||||||
|
curr_end_addr = curr_node['end']
|
||||||
|
|
||||||
|
while curr_end_addr <= mem['end']:
|
||||||
|
if curr_node['end'] == mem['end']:
|
||||||
|
if current_segment is not None and current_segment['start'] > curr_node['start'] and current_segment['start'] < curr_node['end']:
|
||||||
|
curr_node['end'] = current_segment['start']
|
||||||
|
if(curr_node['end'] > curr_node['start']):
|
||||||
|
final_list.append(curr_node)
|
||||||
|
curr_node = deepcopy(current_segment)
|
||||||
|
curr_node['permissions'] = mem['permissions']
|
||||||
|
curr_end_addr = curr_node['end']
|
||||||
|
else:
|
||||||
|
if(curr_node['end'] > curr_node['start']):
|
||||||
|
final_list.append(curr_node)
|
||||||
|
# if curr_node is a segment
|
||||||
|
if current_segment is not None and current_segment['end'] == mem['end']:
|
||||||
|
curr_seg_idx += 1
|
||||||
|
if curr_seg_idx < len(sorted_segments):
|
||||||
|
current_segment = sorted_segments[curr_seg_idx]
|
||||||
|
else:
|
||||||
|
current_segment = None
|
||||||
|
|
||||||
|
curr_node = None
|
||||||
|
break
|
||||||
|
# could only be a segment
|
||||||
|
else:
|
||||||
|
if curr_node['end'] < mem['end']:
|
||||||
|
# check for remaining segments and valid segments
|
||||||
|
if(curr_node['end'] > curr_node['start']):
|
||||||
|
final_list.append(curr_node)
|
||||||
|
|
||||||
|
curr_seg_idx += 1
|
||||||
|
if curr_seg_idx < len(sorted_segments):
|
||||||
|
current_segment = sorted_segments[curr_seg_idx]
|
||||||
|
else:
|
||||||
|
current_segment = None
|
||||||
|
|
||||||
|
if current_segment is not None and current_segment['start'] <= curr_end_addr and current_segment['start'] < mem['end']:
|
||||||
|
curr_node = deepcopy(current_segment)
|
||||||
|
curr_node['permissions'] = mem['permissions']
|
||||||
|
else:
|
||||||
|
# no more segments
|
||||||
|
curr_node = deepcopy(mem)
|
||||||
|
|
||||||
|
curr_node['start'] = curr_end_addr
|
||||||
|
curr_end_addr = curr_node['end']
|
||||||
|
|
||||||
|
return final_list
|
||||||
|
|
||||||
|
# https://github.com/llvm-mirror/llvm/blob/master/include/llvm/ADT/Triple.h
|
||||||
|
def get_arch():
|
||||||
|
arch, arch_vendor, arch_os = lldb.target.GetTriple().split('-')
|
||||||
|
if arch == 'x86_64':
|
||||||
|
return "x64"
|
||||||
|
elif arch == 'x86' or arch == 'i386':
|
||||||
|
return "x86"
|
||||||
|
elif arch == 'aarch64' or arch == 'arm64':
|
||||||
|
return "arm64le"
|
||||||
|
elif arch == 'aarch64_be':
|
||||||
|
return "arm64be"
|
||||||
|
elif arch == 'armeb':
|
||||||
|
return "armbe"
|
||||||
|
elif arch == 'arm':
|
||||||
|
return "armle"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------
|
||||||
|
#---- Dumping functions
|
||||||
|
|
||||||
|
def dump_arch_info():
|
||||||
|
arch_info = {}
|
||||||
|
arch_info["arch"] = get_arch()
|
||||||
|
return arch_info
|
||||||
|
|
||||||
|
|
||||||
|
def dump_regs():
|
||||||
|
reg_state = {}
|
||||||
|
for reg_list in lldb.frame.GetRegisters():
|
||||||
|
if 'general purpose registers' in reg_list.GetName().lower():
|
||||||
|
for reg in reg_list:
|
||||||
|
reg_state[reg.GetName()] = int(reg.GetValue(), 16)
|
||||||
|
return reg_state
|
||||||
|
|
||||||
|
def get_section_info(sec):
|
||||||
|
name = sec.name if sec.name is not None else ''
|
||||||
|
if sec.GetParent().name is not None:
|
||||||
|
name = sec.GetParent().name + '.' + sec.name
|
||||||
|
|
||||||
|
module_name = sec.addr.module.file.GetFilename()
|
||||||
|
module_name = module_name if module_name is not None else ''
|
||||||
|
long_name = module_name + '.' + name
|
||||||
|
|
||||||
|
return sec.addr.load_addr, (sec.addr.load_addr + sec.size), sec.size, long_name
|
||||||
|
|
||||||
|
|
||||||
|
def dump_process_memory(output_dir):
|
||||||
|
# Segment information dictionary
|
||||||
|
raw_segment_list = []
|
||||||
|
raw_memory_list = []
|
||||||
|
|
||||||
|
# 1st pass:
|
||||||
|
# Loop over the segments, fill in the segment info dictionary
|
||||||
|
for module in lldb.target.module_iter():
|
||||||
|
for seg_ea in module.section_iter():
|
||||||
|
seg_info = {'module': module.file.GetFilename() }
|
||||||
|
seg_info['start'], seg_info['end'], seg_size, seg_info['name'] = get_section_info(seg_ea)
|
||||||
|
# TODO: Ugly hack for -1 LONG address on 32-bit
|
||||||
|
if seg_info['start'] >= sys.maxint or seg_size <= 0:
|
||||||
|
print "Throwing away page: {}".format(seg_info['name'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Page-align segment
|
||||||
|
seg_info['start'] = ALIGN_PAGE_DOWN(seg_info['start'])
|
||||||
|
seg_info['end'] = ALIGN_PAGE_UP(seg_info['end'])
|
||||||
|
print("Appending: {}".format(seg_info['name']))
|
||||||
|
raw_segment_list.append(seg_info)
|
||||||
|
|
||||||
|
# Add the stack memory region (just hardcode 0x1000 around the current SP)
|
||||||
|
sp = lldb.frame.GetSP()
|
||||||
|
start_sp = ALIGN_PAGE_DOWN(sp)
|
||||||
|
raw_segment_list.append({'start': start_sp, 'end': start_sp + 0x1000, 'name': 'STACK'})
|
||||||
|
|
||||||
|
# Write the original memory to file for debugging
|
||||||
|
index_file = open(os.path.join(output_dir, DEBUG_MEM_FILE_NAME), 'w')
|
||||||
|
index_file.write(json.dumps(raw_segment_list, indent=4))
|
||||||
|
index_file.close()
|
||||||
|
|
||||||
|
# Loop over raw memory regions
|
||||||
|
mem_info = lldb.SBMemoryRegionInfo()
|
||||||
|
start_addr = -1
|
||||||
|
next_region_addr = 0
|
||||||
|
while next_region_addr > start_addr:
|
||||||
|
err = lldb.process.GetMemoryRegionInfo(next_region_addr, mem_info)
|
||||||
|
# TODO: Should check err.success. If False, what do we do?
|
||||||
|
if not err.success:
|
||||||
|
break
|
||||||
|
next_region_addr = mem_info.GetRegionEnd()
|
||||||
|
if next_region_addr >= sys.maxsize:
|
||||||
|
break
|
||||||
|
|
||||||
|
start_addr = mem_info.GetRegionBase()
|
||||||
|
end_addr = mem_info.GetRegionEnd()
|
||||||
|
|
||||||
|
# Unknown region name
|
||||||
|
region_name = 'UNKNOWN'
|
||||||
|
|
||||||
|
# Ignore regions that aren't even mapped
|
||||||
|
if mem_info.IsMapped() and mem_info.IsReadable():
|
||||||
|
mem_info_obj = {'start': start_addr, 'end': end_addr, 'name': region_name, 'permissions': {
|
||||||
|
"r": mem_info.IsReadable(),
|
||||||
|
"w": mem_info.IsWritable(),
|
||||||
|
"x": mem_info.IsExecutable()
|
||||||
|
}}
|
||||||
|
|
||||||
|
raw_memory_list.append(mem_info_obj)
|
||||||
|
|
||||||
|
final_segment_list = overlap_alignments(raw_segment_list, raw_memory_list)
|
||||||
|
|
||||||
|
for seg_info in final_segment_list:
|
||||||
|
try:
|
||||||
|
seg_info['content_file'] = ''
|
||||||
|
start_addr = seg_info['start']
|
||||||
|
end_addr = seg_info['end']
|
||||||
|
region_name = seg_info['name']
|
||||||
|
# Compress and dump the content to a file
|
||||||
|
err = lldb.SBError()
|
||||||
|
seg_content = lldb.process.ReadMemory(start_addr, end_addr - start_addr, err)
|
||||||
|
if(seg_content == None):
|
||||||
|
print("Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format(start_addr, region_name))
|
||||||
|
seg_info['content_file'] = ''
|
||||||
|
else:
|
||||||
|
print("Dumping segment @0x{0:016x} (size:0x{1:x}): {2} [{3}]".format(start_addr, len(seg_content), region_name, repr(seg_info['permissions'])))
|
||||||
|
compressed_seg_content = zlib.compress(seg_content)
|
||||||
|
md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin"
|
||||||
|
seg_info['content_file'] = md5_sum
|
||||||
|
|
||||||
|
# Write the compressed contents to disk
|
||||||
|
out_file = open(os.path.join(output_dir, md5_sum), 'wb')
|
||||||
|
out_file.write(compressed_seg_content)
|
||||||
|
out_file.close()
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("Exception reading segment ({}): {}".format(region_name, sys.exc_info()[0]))
|
||||||
|
|
||||||
|
return final_segment_list
|
||||||
|
|
||||||
|
#----------
|
||||||
|
#---- Main
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("----- Unicorn Context Dumper -----")
|
||||||
|
print("You must be actively debugging before running this!")
|
||||||
|
print("If it fails, double check that you are actively debugging before running.")
|
||||||
|
|
||||||
|
# Create the output directory
|
||||||
|
timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d_%H%M%S')
|
||||||
|
output_path = "UnicornContext_" + timestamp
|
||||||
|
if not os.path.exists(output_path):
|
||||||
|
os.makedirs(output_path)
|
||||||
|
print("Process context will be output to {}".format(output_path))
|
||||||
|
|
||||||
|
# Get the context
|
||||||
|
context = {
|
||||||
|
"arch": dump_arch_info(),
|
||||||
|
"regs": dump_regs(),
|
||||||
|
"segments": dump_process_memory(output_path),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write the index file
|
||||||
|
index_file = open(os.path.join(output_path, INDEX_FILE_NAME), 'w')
|
||||||
|
index_file.write(json.dumps(context, indent=4))
|
||||||
|
index_file.close()
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
print("!!! ERROR:\n\t{}".format(repr(e)))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
elif lldb.debugger:
|
||||||
|
main()
|
224
unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py
Normal file
224
unicorn_mode/helper_scripts/unicorn_dumper_pwndbg.py
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
"""
|
||||||
|
unicorn_dumper_pwndbg.py
|
||||||
|
|
||||||
|
When run with GDB sitting at a debug breakpoint, this
|
||||||
|
dumps the current state (registers/memory/etc) of
|
||||||
|
the process to a directory consisting of an index
|
||||||
|
file with register and segment information and
|
||||||
|
sub-files containing all actual process memory.
|
||||||
|
|
||||||
|
The output of this script is expected to be used
|
||||||
|
to initialize context for Unicorn emulation.
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
In order to run this script, PWNDBG needs to be running in the GDB session (gdbinit.py)
|
||||||
|
# HELPERS from: https://github.com/pwndbg/pwndbg
|
||||||
|
It can be loaded with:
|
||||||
|
source <path_to_pwndbg>/gdbinit.py
|
||||||
|
|
||||||
|
Call this function when at a breakpoint in your process with:
|
||||||
|
source unicorn_dumper_pwndbg.py
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# GDB Python SDK
|
||||||
|
import gdb
|
||||||
|
|
||||||
|
pwndbg_loaded = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pwndbg.arch
|
||||||
|
import pwndbg.regs
|
||||||
|
import pwndbg.vmmap
|
||||||
|
import pwndbg.memory
|
||||||
|
|
||||||
|
pwndbg_loaded = True
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
print("!!! PWNGDB not running in GDB. Please run gdbinit.py by executing:")
|
||||||
|
print('\tpython execfile ("<path_to_pwndbg>/gdbinit.py")')
|
||||||
|
|
||||||
|
# Maximum segment size that we'll store
|
||||||
|
# Yep, this could break stuff pretty quickly if we
|
||||||
|
# omit something that's used during emulation.
|
||||||
|
MAX_SEG_SIZE = 128 * 1024 * 1024
|
||||||
|
|
||||||
|
# Name of the index file
|
||||||
|
INDEX_FILE_NAME = "_index.json"
|
||||||
|
|
||||||
|
#----------------------
|
||||||
|
#---- Helper Functions
|
||||||
|
|
||||||
|
def map_arch():
|
||||||
|
arch = pwndbg.arch.current # from PWNDBG
|
||||||
|
if 'x86_64' in arch or 'x86-64' in arch:
|
||||||
|
return "x64"
|
||||||
|
elif 'x86' in arch or 'i386' in arch:
|
||||||
|
return "x86"
|
||||||
|
elif 'aarch64' in arch or 'arm64' in arch:
|
||||||
|
return "arm64le"
|
||||||
|
elif 'aarch64_be' in arch:
|
||||||
|
return "arm64be"
|
||||||
|
elif 'arm' in arch:
|
||||||
|
cpsr = pwndbg.regs['cpsr']
|
||||||
|
# check endianess
|
||||||
|
if pwndbg.arch.endian == 'big':
|
||||||
|
# check for THUMB mode
|
||||||
|
if (cpsr & (1 << 5)):
|
||||||
|
return "armbethumb"
|
||||||
|
else:
|
||||||
|
return "armbe"
|
||||||
|
else:
|
||||||
|
# check for THUMB mode
|
||||||
|
if (cpsr & (1 << 5)):
|
||||||
|
return "armlethumb"
|
||||||
|
else:
|
||||||
|
return "armle"
|
||||||
|
elif 'mips' in arch:
|
||||||
|
if pwndbg.arch.endian == 'little':
|
||||||
|
return 'mipsel'
|
||||||
|
else:
|
||||||
|
return 'mips'
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------
|
||||||
|
#---- Dumping functions
|
||||||
|
|
||||||
|
def dump_arch_info():
|
||||||
|
arch_info = {}
|
||||||
|
arch_info["arch"] = map_arch()
|
||||||
|
return arch_info
|
||||||
|
|
||||||
|
|
||||||
|
def dump_regs():
|
||||||
|
reg_state = {}
|
||||||
|
for reg in pwndbg.regs.all:
|
||||||
|
reg_val = pwndbg.regs[reg]
|
||||||
|
# current dumper script looks for register values to be hex strings
|
||||||
|
# reg_str = "0x{:08x}".format(reg_val)
|
||||||
|
# if "64" in get_arch():
|
||||||
|
# reg_str = "0x{:016x}".format(reg_val)
|
||||||
|
# reg_state[reg.strip().strip('$')] = reg_str
|
||||||
|
reg_state[reg.strip().strip('$')] = reg_val
|
||||||
|
return reg_state
|
||||||
|
|
||||||
|
|
||||||
|
def dump_process_memory(output_dir):
|
||||||
|
# Segment information dictionary
|
||||||
|
final_segment_list = []
|
||||||
|
|
||||||
|
# PWNDBG:
|
||||||
|
vmmap = pwndbg.vmmap.get()
|
||||||
|
|
||||||
|
# Pointer to end of last dumped memory segment
|
||||||
|
segment_last_addr = 0x0;
|
||||||
|
|
||||||
|
start = None
|
||||||
|
end = None
|
||||||
|
|
||||||
|
if not vmmap:
|
||||||
|
print("No address mapping information found")
|
||||||
|
return final_segment_list
|
||||||
|
|
||||||
|
# Assume segment entries are sorted by start address
|
||||||
|
for entry in vmmap:
|
||||||
|
if entry.start == entry.end:
|
||||||
|
continue
|
||||||
|
|
||||||
|
start = entry.start
|
||||||
|
end = entry.end
|
||||||
|
|
||||||
|
if (segment_last_addr > entry.start): # indicates overlap
|
||||||
|
if (segment_last_addr > entry.end): # indicates complete overlap, so we skip the segment entirely
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
start = segment_last_addr
|
||||||
|
|
||||||
|
|
||||||
|
seg_info = {'start': start, 'end': end, 'name': entry.objfile, 'permissions': {
|
||||||
|
"r": entry.read,
|
||||||
|
"w": entry.write,
|
||||||
|
"x": entry.execute
|
||||||
|
}, 'content_file': ''}
|
||||||
|
|
||||||
|
# "(deleted)" may or may not be valid, but don't push it.
|
||||||
|
if entry.read and not '(deleted)' in entry.objfile:
|
||||||
|
try:
|
||||||
|
# Compress and dump the content to a file
|
||||||
|
seg_content = pwndbg.memory.read(start, end - start)
|
||||||
|
if(seg_content == None):
|
||||||
|
print("Segment empty: @0x{0:016x} (size:UNKNOWN) {1}".format(entry.start, entry.objfile))
|
||||||
|
else:
|
||||||
|
print("Dumping segment @0x{0:016x} (size:0x{1:x}): {2} [{3}]".format(entry.start, len(seg_content), entry.objfile, repr(seg_info['permissions'])))
|
||||||
|
compressed_seg_content = zlib.compress(seg_content)
|
||||||
|
md5_sum = hashlib.md5(compressed_seg_content).hexdigest() + ".bin"
|
||||||
|
seg_info["content_file"] = md5_sum
|
||||||
|
|
||||||
|
# Write the compressed contents to disk
|
||||||
|
out_file = open(os.path.join(output_dir, md5_sum), 'wb')
|
||||||
|
out_file.write(compressed_seg_content)
|
||||||
|
out_file.close()
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("Exception reading segment ({}): {}".format(entry.objfile, sys.exc_info()[0]))
|
||||||
|
else:
|
||||||
|
print("Skipping segment {0}@0x{1:016x}".format(entry.objfile, entry.start))
|
||||||
|
|
||||||
|
segment_last_addr = end
|
||||||
|
|
||||||
|
# Add the segment to the list
|
||||||
|
final_segment_list.append(seg_info)
|
||||||
|
|
||||||
|
|
||||||
|
return final_segment_list
|
||||||
|
|
||||||
|
#----------
|
||||||
|
#---- Main
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("----- Unicorn Context Dumper -----")
|
||||||
|
print("You must be actively debugging before running this!")
|
||||||
|
print("If it fails, double check that you are actively debugging before running.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Create the output directory
|
||||||
|
timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d_%H%M%S')
|
||||||
|
output_path = "UnicornContext_" + timestamp
|
||||||
|
if not os.path.exists(output_path):
|
||||||
|
os.makedirs(output_path)
|
||||||
|
print("Process context will be output to {}".format(output_path))
|
||||||
|
|
||||||
|
# Get the context
|
||||||
|
context = {
|
||||||
|
"arch": dump_arch_info(),
|
||||||
|
"regs": dump_regs(),
|
||||||
|
"segments": dump_process_memory(output_path),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write the index file
|
||||||
|
index_file = open(os.path.join(output_path, INDEX_FILE_NAME), 'w')
|
||||||
|
index_file.write(json.dumps(context, indent=4))
|
||||||
|
index_file.close()
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("!!! ERROR:\n\t{}".format(repr(e)))
|
||||||
|
|
||||||
|
if __name__ == "__main__" and pwndbg_loaded:
|
||||||
|
main()
|
||||||
|
|
560
unicorn_mode/helper_scripts/unicorn_loader.py
Normal file
560
unicorn_mode/helper_scripts/unicorn_loader.py
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
"""
|
||||||
|
unicorn_loader.py
|
||||||
|
|
||||||
|
Loads a process context dumped created using a
|
||||||
|
Unicorn Context Dumper script into a Unicorn Engine
|
||||||
|
instance. Once this is performed emulation can be
|
||||||
|
started.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import binascii
|
||||||
|
from collections import namedtuple
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# Unicorn imports
|
||||||
|
from unicorn import *
|
||||||
|
from unicorn.arm_const import *
|
||||||
|
from unicorn.arm64_const import *
|
||||||
|
from unicorn.x86_const import *
|
||||||
|
from unicorn.mips_const import *
|
||||||
|
|
||||||
|
# Name of the index file
|
||||||
|
INDEX_FILE_NAME = "_index.json"
|
||||||
|
|
||||||
|
# Page size required by Unicorn
|
||||||
|
UNICORN_PAGE_SIZE = 0x1000
|
||||||
|
|
||||||
|
# Max allowable segment size (1G)
|
||||||
|
MAX_ALLOWABLE_SEG_SIZE = 1024 * 1024 * 1024
|
||||||
|
|
||||||
|
# Alignment functions to align all memory segments to Unicorn page boundaries (4KB pages only)
|
||||||
|
ALIGN_PAGE_DOWN = lambda x: x & ~(UNICORN_PAGE_SIZE - 1)
|
||||||
|
ALIGN_PAGE_UP = lambda x: (x + UNICORN_PAGE_SIZE - 1) & ~(UNICORN_PAGE_SIZE-1)
|
||||||
|
|
||||||
|
#---------------------------------------
|
||||||
|
#---- Unicorn-based heap implementation
|
||||||
|
|
||||||
|
class UnicornSimpleHeap(object):
|
||||||
|
""" Use this class to provide a simple heap implementation. This should
|
||||||
|
be used if malloc/free calls break things during emulation. This heap also
|
||||||
|
implements basic guard-page capabilities which enable immediate notice of
|
||||||
|
heap overflow and underflows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Helper data-container used to track chunks
|
||||||
|
class HeapChunk(object):
|
||||||
|
def __init__(self, actual_addr, total_size, data_size):
|
||||||
|
self.total_size = total_size # Total size of the chunk (including padding and guard page)
|
||||||
|
self.actual_addr = actual_addr # Actual start address of the chunk
|
||||||
|
self.data_size = data_size # Size requested by the caller of actual malloc call
|
||||||
|
self.data_addr = actual_addr + UNICORN_PAGE_SIZE # Address where data actually starts
|
||||||
|
|
||||||
|
# Returns true if the specified buffer is completely within the chunk, else false
|
||||||
|
def is_buffer_in_chunk(self, addr, size):
|
||||||
|
if addr >= self.data_addr and ((addr + size) <= (self.data_addr + self.data_size)):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Skip the zero-page to avoid weird potential issues with segment registers
|
||||||
|
HEAP_MIN_ADDR = 0x00002000
|
||||||
|
HEAP_MAX_ADDR = 0xFFFFFFFF
|
||||||
|
|
||||||
|
_uc = None # Unicorn engine instance to interact with
|
||||||
|
_chunks = [] # List of all known chunks
|
||||||
|
_debug_print = False # True to print debug information
|
||||||
|
|
||||||
|
def __init__(self, uc, debug_print=False):
|
||||||
|
self._uc = uc
|
||||||
|
self._debug_print = debug_print
|
||||||
|
|
||||||
|
# Add the watchpoint hook that will be used to implement psuedo-guard page support
|
||||||
|
self._uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, self.__check_mem_access)
|
||||||
|
|
||||||
|
def malloc(self, size):
|
||||||
|
# Figure out the overall size to be allocated/mapped
|
||||||
|
# - Allocate at least 1 4k page of memory to make Unicorn happy
|
||||||
|
# - Add guard pages at the start and end of the region
|
||||||
|
total_chunk_size = UNICORN_PAGE_SIZE + ALIGN_PAGE_UP(size) + UNICORN_PAGE_SIZE
|
||||||
|
# Gross but efficient way to find space for the chunk:
|
||||||
|
chunk = None
|
||||||
|
for addr in xrange(self.HEAP_MIN_ADDR, self.HEAP_MAX_ADDR, UNICORN_PAGE_SIZE):
|
||||||
|
try:
|
||||||
|
self._uc.mem_map(addr, total_chunk_size, UC_PROT_READ | UC_PROT_WRITE)
|
||||||
|
chunk = self.HeapChunk(addr, total_chunk_size, size)
|
||||||
|
if self._debug_print:
|
||||||
|
print("Allocating 0x{0:x}-byte chunk @ 0x{1:016x}".format(chunk.data_size, chunk.data_addr))
|
||||||
|
break
|
||||||
|
except UcError as e:
|
||||||
|
continue
|
||||||
|
# Something went very wrong
|
||||||
|
if chunk == None:
|
||||||
|
return 0
|
||||||
|
self._chunks.append(chunk)
|
||||||
|
return chunk.data_addr
|
||||||
|
|
||||||
|
def calloc(self, size, count):
|
||||||
|
# Simple wrapper around malloc with calloc() args
|
||||||
|
return self.malloc(size*count)
|
||||||
|
|
||||||
|
def realloc(self, ptr, new_size):
|
||||||
|
# Wrapper around malloc(new_size) / memcpy(new, old, old_size) / free(old)
|
||||||
|
if self._debug_print:
|
||||||
|
print("Reallocating chunk @ 0x{0:016x} to be 0x{1:x} bytes".format(ptr, new_size))
|
||||||
|
old_chunk = None
|
||||||
|
for chunk in self._chunks:
|
||||||
|
if chunk.data_addr == ptr:
|
||||||
|
old_chunk = chunk
|
||||||
|
new_chunk_addr = self.malloc(new_size)
|
||||||
|
if old_chunk != None:
|
||||||
|
self._uc.mem_write(new_chunk_addr, str(self._uc.mem_read(old_chunk.data_addr, old_chunk.data_size)))
|
||||||
|
self.free(old_chunk.data_addr)
|
||||||
|
return new_chunk_addr
|
||||||
|
|
||||||
|
def free(self, addr):
|
||||||
|
for chunk in self._chunks:
|
||||||
|
if chunk.is_buffer_in_chunk(addr, 1):
|
||||||
|
if self._debug_print:
|
||||||
|
print("Freeing 0x{0:x}-byte chunk @ 0x{0:016x}".format(chunk.req_size, chunk.data_addr))
|
||||||
|
self._uc.mem_unmap(chunk.actual_addr, chunk.total_size)
|
||||||
|
self._chunks.remove(chunk)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Implements basic guard-page functionality
|
||||||
|
def __check_mem_access(self, uc, access, address, size, value, user_data):
|
||||||
|
for chunk in self._chunks:
|
||||||
|
if address >= chunk.actual_addr and ((address + size) <= (chunk.actual_addr + chunk.total_size)):
|
||||||
|
if chunk.is_buffer_in_chunk(address, size) == False:
|
||||||
|
if self._debug_print:
|
||||||
|
print("Heap over/underflow attempting to {0} 0x{1:x} bytes @ {2:016x}".format( \
|
||||||
|
"write" if access == UC_MEM_WRITE else "read", size, address))
|
||||||
|
# Force a memory-based crash
|
||||||
|
uc.force_crash(UcError(UC_ERR_READ_PROT))
|
||||||
|
|
||||||
|
#---------------------------
|
||||||
|
#---- Loading function
|
||||||
|
|
||||||
|
class AflUnicornEngine(Uc):
|
||||||
|
|
||||||
|
def __init__(self, context_directory, enable_trace=False, debug_print=False):
|
||||||
|
"""
|
||||||
|
Initializes an AflUnicornEngine instance, which extends standard the UnicornEngine
|
||||||
|
with a bunch of helper routines that are useful for creating afl-unicorn test harnesses.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- context_directory: Path to the directory generated by one of the context dumper scripts
|
||||||
|
- enable_trace: If True trace information will be printed to STDOUT
|
||||||
|
- debug_print: If True debugging information will be printed while loading the context
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Make sure the index file exists and load it
|
||||||
|
index_file_path = os.path.join(context_directory, INDEX_FILE_NAME)
|
||||||
|
if not os.path.isfile(index_file_path):
|
||||||
|
raise Exception("Index file not found. Expected it to be at {}".format(index_file_path))
|
||||||
|
|
||||||
|
# Load the process context from the index file
|
||||||
|
if debug_print:
|
||||||
|
print("Loading process context index from {}".format(index_file_path))
|
||||||
|
index_file = open(index_file_path, 'r')
|
||||||
|
context = json.load(index_file)
|
||||||
|
index_file.close()
|
||||||
|
|
||||||
|
# Check the context to make sure we have the basic essential components
|
||||||
|
if 'arch' not in context:
|
||||||
|
raise Exception("Couldn't find architecture information in index file")
|
||||||
|
if 'regs' not in context:
|
||||||
|
raise Exception("Couldn't find register information in index file")
|
||||||
|
if 'segments' not in context:
|
||||||
|
raise Exception("Couldn't find segment/memory information in index file")
|
||||||
|
|
||||||
|
# Set the UnicornEngine instance's architecture and mode
|
||||||
|
self._arch_str = context['arch']['arch']
|
||||||
|
arch, mode = self.__get_arch_and_mode(self._arch_str)
|
||||||
|
Uc.__init__(self, arch, mode)
|
||||||
|
|
||||||
|
# Load the registers
|
||||||
|
regs = context['regs']
|
||||||
|
reg_map = self.__get_register_map(self._arch_str)
|
||||||
|
for register, value in regs.iteritems():
|
||||||
|
if debug_print:
|
||||||
|
print("Reg {0} = {1}".format(register, value))
|
||||||
|
if not reg_map.has_key(register.lower()):
|
||||||
|
if debug_print:
|
||||||
|
print("Skipping Reg: {}".format(register))
|
||||||
|
else:
|
||||||
|
reg_write_retry = True
|
||||||
|
try:
|
||||||
|
self.reg_write(reg_map[register.lower()], value)
|
||||||
|
reg_write_retry = False
|
||||||
|
except Exception as e:
|
||||||
|
if debug_print:
|
||||||
|
print("ERROR writing register: {}, value: {} -- {}".format(register, value, repr(e)))
|
||||||
|
|
||||||
|
if reg_write_retry:
|
||||||
|
if debug_print:
|
||||||
|
print("Trying to parse value ({}) as hex string".format(value))
|
||||||
|
try:
|
||||||
|
self.reg_write(reg_map[register.lower()], int(value, 16))
|
||||||
|
except Exception as e:
|
||||||
|
if debug_print:
|
||||||
|
print("ERROR writing hex string register: {}, value: {} -- {}".format(register, value, repr(e)))
|
||||||
|
|
||||||
|
# Setup the memory map and load memory content
|
||||||
|
self.__map_segments(context['segments'], context_directory, debug_print)
|
||||||
|
|
||||||
|
if enable_trace:
|
||||||
|
self.hook_add(UC_HOOK_BLOCK, self.__trace_block)
|
||||||
|
self.hook_add(UC_HOOK_CODE, self.__trace_instruction)
|
||||||
|
self.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, self.__trace_mem_access)
|
||||||
|
self.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_READ_INVALID, self.__trace_mem_invalid_access)
|
||||||
|
|
||||||
|
if debug_print:
|
||||||
|
print("Done loading context.")
|
||||||
|
|
||||||
|
def get_arch(self):
|
||||||
|
return self._arch
|
||||||
|
|
||||||
|
def get_mode(self):
|
||||||
|
return self._mode
|
||||||
|
|
||||||
|
def get_arch_str(self):
|
||||||
|
return self._arch_str
|
||||||
|
|
||||||
|
def force_crash(self, uc_error):
|
||||||
|
""" This function should be called to indicate to AFL that a crash occurred during emulation.
|
||||||
|
You can pass the exception received from Uc.emu_start
|
||||||
|
"""
|
||||||
|
mem_errors = [
|
||||||
|
UC_ERR_READ_UNMAPPED, UC_ERR_READ_PROT, UC_ERR_READ_UNALIGNED,
|
||||||
|
UC_ERR_WRITE_UNMAPPED, UC_ERR_WRITE_PROT, UC_ERR_WRITE_UNALIGNED,
|
||||||
|
UC_ERR_FETCH_UNMAPPED, UC_ERR_FETCH_PROT, UC_ERR_FETCH_UNALIGNED,
|
||||||
|
]
|
||||||
|
if uc_error.errno in mem_errors:
|
||||||
|
# Memory error - throw SIGSEGV
|
||||||
|
os.kill(os.getpid(), signal.SIGSEGV)
|
||||||
|
elif uc_error.errno == UC_ERR_INSN_INVALID:
|
||||||
|
# Invalid instruction - throw SIGILL
|
||||||
|
os.kill(os.getpid(), signal.SIGILL)
|
||||||
|
else:
|
||||||
|
# Not sure what happened - throw SIGABRT
|
||||||
|
os.kill(os.getpid(), signal.SIGABRT)
|
||||||
|
|
||||||
|
def dump_regs(self):
|
||||||
|
""" Dumps the contents of all the registers to STDOUT """
|
||||||
|
for reg in sorted(self.__get_register_map(self._arch_str).items(), key=lambda reg: reg[0]):
|
||||||
|
print(">>> {0:>4}: 0x{1:016x}".format(reg[0], self.reg_read(reg[1])))
|
||||||
|
|
||||||
|
# TODO: Make this dynamically get the stack pointer register and pointer width for the current architecture
|
||||||
|
"""
|
||||||
|
def dump_stack(self, window=10):
|
||||||
|
print(">>> Stack:")
|
||||||
|
stack_ptr_addr = self.reg_read(UC_X86_REG_RSP)
|
||||||
|
for i in xrange(-window, window + 1):
|
||||||
|
addr = stack_ptr_addr + (i*8)
|
||||||
|
print("{0}0x{1:016x}: 0x{2:016x}".format( \
|
||||||
|
'SP->' if i == 0 else ' ', addr, \
|
||||||
|
struct.unpack('<Q', self.mem_read(addr, 8))[0]))
|
||||||
|
"""
|
||||||
|
|
||||||
|
#-----------------------------
|
||||||
|
#---- Loader Helper Functions
|
||||||
|
|
||||||
|
def __map_segment(self, name, address, size, perms, debug_print=False):
|
||||||
|
# - size is unsigned and must be != 0
|
||||||
|
# - starting address must be aligned to 4KB
|
||||||
|
# - map size must be multiple of the page size (4KB)
|
||||||
|
mem_start = address
|
||||||
|
mem_end = address + size
|
||||||
|
mem_start_aligned = ALIGN_PAGE_DOWN(mem_start)
|
||||||
|
mem_end_aligned = ALIGN_PAGE_UP(mem_end)
|
||||||
|
if debug_print:
|
||||||
|
if mem_start_aligned != mem_start or mem_end_aligned != mem_end:
|
||||||
|
print("Aligning segment to page boundary:")
|
||||||
|
print(" name: {}".format(name))
|
||||||
|
print(" start: {0:016x} -> {1:016x}".format(mem_start, mem_start_aligned))
|
||||||
|
print(" end: {0:016x} -> {1:016x}".format(mem_end, mem_end_aligned))
|
||||||
|
print("Mapping segment from {0:016x} - {1:016x} with perm={2}: {3}".format(mem_start_aligned, mem_end_aligned, perms, name))
|
||||||
|
if(mem_start_aligned < mem_end_aligned):
|
||||||
|
self.mem_map(mem_start_aligned, mem_end_aligned - mem_start_aligned, perms)
|
||||||
|
|
||||||
|
|
||||||
|
def __map_segments(self, segment_list, context_directory, debug_print=False):
|
||||||
|
for segment in segment_list:
|
||||||
|
|
||||||
|
# Get the segment information from the index
|
||||||
|
name = segment['name']
|
||||||
|
seg_start = segment['start']
|
||||||
|
seg_end = segment['end']
|
||||||
|
perms = \
|
||||||
|
(UC_PROT_READ if segment['permissions']['r'] == True else 0) | \
|
||||||
|
(UC_PROT_WRITE if segment['permissions']['w'] == True else 0) | \
|
||||||
|
(UC_PROT_EXEC if segment['permissions']['x'] == True else 0)
|
||||||
|
|
||||||
|
if debug_print:
|
||||||
|
print("Handling segment {}".format(name))
|
||||||
|
|
||||||
|
# Check for any overlap with existing segments. If there is, it must
|
||||||
|
# be consolidated and merged together before mapping since Unicorn
|
||||||
|
# doesn't allow overlapping segments.
|
||||||
|
found = False
|
||||||
|
overlap_start = False
|
||||||
|
overlap_end = False
|
||||||
|
tmp = 0
|
||||||
|
for (mem_start, mem_end, mem_perm) in self.mem_regions():
|
||||||
|
mem_end = mem_end + 1
|
||||||
|
if seg_start >= mem_start and seg_end < mem_end:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if seg_start >= mem_start and seg_start < mem_end:
|
||||||
|
overlap_start = True
|
||||||
|
tmp = mem_end
|
||||||
|
break
|
||||||
|
if seg_end >= mem_start and seg_end < mem_end:
|
||||||
|
overlap_end = True
|
||||||
|
tmp = mem_start
|
||||||
|
break
|
||||||
|
|
||||||
|
# Map memory into the address space if it is of an acceptable size.
|
||||||
|
if (seg_end - seg_start) > MAX_ALLOWABLE_SEG_SIZE:
|
||||||
|
if debug_print:
|
||||||
|
print("Skipping segment (LARGER THAN {0}) from {1:016x} - {2:016x} with perm={3}: {4}".format(MAX_ALLOWABLE_SEG_SIZE, seg_start, seg_end, perms, name))
|
||||||
|
continue
|
||||||
|
elif not found: # Make sure it's not already mapped
|
||||||
|
if overlap_start: # Partial overlap (start)
|
||||||
|
self.__map_segment(name, tmp, seg_end - tmp, perms, debug_print)
|
||||||
|
elif overlap_end: # Patrial overlap (end)
|
||||||
|
self.__map_segment(name, seg_start, tmp - seg_start, perms, debug_print)
|
||||||
|
else: # Not found
|
||||||
|
self.__map_segment(name, seg_start, seg_end - seg_start, perms, debug_print)
|
||||||
|
else:
|
||||||
|
if debug_print:
|
||||||
|
print("Segment {} already mapped. Moving on.".format(name))
|
||||||
|
|
||||||
|
# Load the content (if available)
|
||||||
|
if 'content_file' in segment and len(segment['content_file']) > 0:
|
||||||
|
content_file_path = os.path.join(context_directory, segment['content_file'])
|
||||||
|
if not os.path.isfile(content_file_path):
|
||||||
|
raise Exception("Unable to find segment content file. Expected it to be at {}".format(content_file_path))
|
||||||
|
#if debug_print:
|
||||||
|
# print("Loading content for segment {} from {}".format(name, segment['content_file']))
|
||||||
|
content_file = open(content_file_path, 'rb')
|
||||||
|
compressed_content = content_file.read()
|
||||||
|
content_file.close()
|
||||||
|
self.mem_write(seg_start, zlib.decompress(compressed_content))
|
||||||
|
|
||||||
|
else:
|
||||||
|
if debug_print:
|
||||||
|
print("No content found for segment {0} @ {1:016x}".format(name, seg_start))
|
||||||
|
self.mem_write(seg_start, '\x00' * (seg_end - seg_start))
|
||||||
|
|
||||||
|
def __get_arch_and_mode(self, arch_str):
|
||||||
|
arch_map = {
|
||||||
|
"x64" : [ UC_X86_REG_RIP, UC_ARCH_X86, UC_MODE_64 ],
|
||||||
|
"x86" : [ UC_X86_REG_EIP, UC_ARCH_X86, UC_MODE_32 ],
|
||||||
|
"arm64be" : [ UC_ARM64_REG_PC, UC_ARCH_ARM64, UC_MODE_ARM | UC_MODE_BIG_ENDIAN ],
|
||||||
|
"arm64le" : [ UC_ARM64_REG_PC, UC_ARCH_ARM64, UC_MODE_ARM | UC_MODE_LITTLE_ENDIAN ],
|
||||||
|
"armbe" : [ UC_ARM_REG_PC, UC_ARCH_ARM, UC_MODE_ARM | UC_MODE_BIG_ENDIAN ],
|
||||||
|
"armle" : [ UC_ARM_REG_PC, UC_ARCH_ARM, UC_MODE_ARM | UC_MODE_LITTLE_ENDIAN ],
|
||||||
|
"armbethumb": [ UC_ARM_REG_PC, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_BIG_ENDIAN ],
|
||||||
|
"armlethumb": [ UC_ARM_REG_PC, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_LITTLE_ENDIAN ],
|
||||||
|
"mips" : [ UC_MIPS_REG_PC, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_BIG_ENDIAN ],
|
||||||
|
"mipsel" : [ UC_MIPS_REG_PC, UC_ARCH_MIPS, UC_MODE_MIPS32 | UC_MODE_LITTLE_ENDIAN ],
|
||||||
|
}
|
||||||
|
return (arch_map[arch_str][1], arch_map[arch_str][2])
|
||||||
|
|
||||||
|
def __get_register_map(self, arch):
|
||||||
|
if arch == "arm64le" or arch == "arm64be":
|
||||||
|
arch = "arm64"
|
||||||
|
elif arch == "armle" or arch == "armbe" or "thumb" in arch:
|
||||||
|
arch = "arm"
|
||||||
|
elif arch == "mipsel":
|
||||||
|
arch = "mips"
|
||||||
|
|
||||||
|
registers = {
|
||||||
|
"x64" : {
|
||||||
|
"rax": UC_X86_REG_RAX,
|
||||||
|
"rbx": UC_X86_REG_RBX,
|
||||||
|
"rcx": UC_X86_REG_RCX,
|
||||||
|
"rdx": UC_X86_REG_RDX,
|
||||||
|
"rsi": UC_X86_REG_RSI,
|
||||||
|
"rdi": UC_X86_REG_RDI,
|
||||||
|
"rbp": UC_X86_REG_RBP,
|
||||||
|
"rsp": UC_X86_REG_RSP,
|
||||||
|
"r8": UC_X86_REG_R8,
|
||||||
|
"r9": UC_X86_REG_R9,
|
||||||
|
"r10": UC_X86_REG_R10,
|
||||||
|
"r11": UC_X86_REG_R11,
|
||||||
|
"r12": UC_X86_REG_R12,
|
||||||
|
"r13": UC_X86_REG_R13,
|
||||||
|
"r14": UC_X86_REG_R14,
|
||||||
|
"r15": UC_X86_REG_R15,
|
||||||
|
"rip": UC_X86_REG_RIP,
|
||||||
|
"rsp": UC_X86_REG_RSP,
|
||||||
|
"efl": UC_X86_REG_EFLAGS,
|
||||||
|
"cs": UC_X86_REG_CS,
|
||||||
|
"ds": UC_X86_REG_DS,
|
||||||
|
"es": UC_X86_REG_ES,
|
||||||
|
"fs": UC_X86_REG_FS,
|
||||||
|
"gs": UC_X86_REG_GS,
|
||||||
|
"ss": UC_X86_REG_SS,
|
||||||
|
},
|
||||||
|
"x86" : {
|
||||||
|
"eax": UC_X86_REG_EAX,
|
||||||
|
"ebx": UC_X86_REG_EBX,
|
||||||
|
"ecx": UC_X86_REG_ECX,
|
||||||
|
"edx": UC_X86_REG_EDX,
|
||||||
|
"esi": UC_X86_REG_ESI,
|
||||||
|
"edi": UC_X86_REG_EDI,
|
||||||
|
"ebp": UC_X86_REG_EBP,
|
||||||
|
"esp": UC_X86_REG_ESP,
|
||||||
|
"eip": UC_X86_REG_EIP,
|
||||||
|
"esp": UC_X86_REG_ESP,
|
||||||
|
"efl": UC_X86_REG_EFLAGS,
|
||||||
|
# Segment registers removed...
|
||||||
|
# They caused segfaults (from unicorn?) when they were here
|
||||||
|
},
|
||||||
|
"arm" : {
|
||||||
|
"r0": UC_ARM_REG_R0,
|
||||||
|
"r1": UC_ARM_REG_R1,
|
||||||
|
"r2": UC_ARM_REG_R2,
|
||||||
|
"r3": UC_ARM_REG_R3,
|
||||||
|
"r4": UC_ARM_REG_R4,
|
||||||
|
"r5": UC_ARM_REG_R5,
|
||||||
|
"r6": UC_ARM_REG_R6,
|
||||||
|
"r7": UC_ARM_REG_R7,
|
||||||
|
"r8": UC_ARM_REG_R8,
|
||||||
|
"r9": UC_ARM_REG_R9,
|
||||||
|
"r10": UC_ARM_REG_R10,
|
||||||
|
"r11": UC_ARM_REG_R11,
|
||||||
|
"r12": UC_ARM_REG_R12,
|
||||||
|
"pc": UC_ARM_REG_PC,
|
||||||
|
"sp": UC_ARM_REG_SP,
|
||||||
|
"lr": UC_ARM_REG_LR,
|
||||||
|
"cpsr": UC_ARM_REG_CPSR
|
||||||
|
},
|
||||||
|
"arm64" : {
|
||||||
|
"x0": UC_ARM64_REG_X0,
|
||||||
|
"x1": UC_ARM64_REG_X1,
|
||||||
|
"x2": UC_ARM64_REG_X2,
|
||||||
|
"x3": UC_ARM64_REG_X3,
|
||||||
|
"x4": UC_ARM64_REG_X4,
|
||||||
|
"x5": UC_ARM64_REG_X5,
|
||||||
|
"x6": UC_ARM64_REG_X6,
|
||||||
|
"x7": UC_ARM64_REG_X7,
|
||||||
|
"x8": UC_ARM64_REG_X8,
|
||||||
|
"x9": UC_ARM64_REG_X9,
|
||||||
|
"x10": UC_ARM64_REG_X10,
|
||||||
|
"x11": UC_ARM64_REG_X11,
|
||||||
|
"x12": UC_ARM64_REG_X12,
|
||||||
|
"x13": UC_ARM64_REG_X13,
|
||||||
|
"x14": UC_ARM64_REG_X14,
|
||||||
|
"x15": UC_ARM64_REG_X15,
|
||||||
|
"x16": UC_ARM64_REG_X16,
|
||||||
|
"x17": UC_ARM64_REG_X17,
|
||||||
|
"x18": UC_ARM64_REG_X18,
|
||||||
|
"x19": UC_ARM64_REG_X19,
|
||||||
|
"x20": UC_ARM64_REG_X20,
|
||||||
|
"x21": UC_ARM64_REG_X21,
|
||||||
|
"x22": UC_ARM64_REG_X22,
|
||||||
|
"x23": UC_ARM64_REG_X23,
|
||||||
|
"x24": UC_ARM64_REG_X24,
|
||||||
|
"x25": UC_ARM64_REG_X25,
|
||||||
|
"x26": UC_ARM64_REG_X26,
|
||||||
|
"x27": UC_ARM64_REG_X27,
|
||||||
|
"x28": UC_ARM64_REG_X28,
|
||||||
|
"pc": UC_ARM64_REG_PC,
|
||||||
|
"sp": UC_ARM64_REG_SP,
|
||||||
|
"fp": UC_ARM64_REG_FP,
|
||||||
|
"lr": UC_ARM64_REG_LR,
|
||||||
|
"nzcv": UC_ARM64_REG_NZCV,
|
||||||
|
"cpsr": UC_ARM_REG_CPSR,
|
||||||
|
},
|
||||||
|
"mips" : {
|
||||||
|
"0" : UC_MIPS_REG_ZERO,
|
||||||
|
"at": UC_MIPS_REG_AT,
|
||||||
|
"v0": UC_MIPS_REG_V0,
|
||||||
|
"v1": UC_MIPS_REG_V1,
|
||||||
|
"a0": UC_MIPS_REG_A0,
|
||||||
|
"a1": UC_MIPS_REG_A1,
|
||||||
|
"a2": UC_MIPS_REG_A2,
|
||||||
|
"a3": UC_MIPS_REG_A3,
|
||||||
|
"t0": UC_MIPS_REG_T0,
|
||||||
|
"t1": UC_MIPS_REG_T1,
|
||||||
|
"t2": UC_MIPS_REG_T2,
|
||||||
|
"t3": UC_MIPS_REG_T3,
|
||||||
|
"t4": UC_MIPS_REG_T4,
|
||||||
|
"t5": UC_MIPS_REG_T5,
|
||||||
|
"t6": UC_MIPS_REG_T6,
|
||||||
|
"t7": UC_MIPS_REG_T7,
|
||||||
|
"t8": UC_MIPS_REG_T8,
|
||||||
|
"t9": UC_MIPS_REG_T9,
|
||||||
|
"s0": UC_MIPS_REG_S0,
|
||||||
|
"s1": UC_MIPS_REG_S1,
|
||||||
|
"s2": UC_MIPS_REG_S2,
|
||||||
|
"s3": UC_MIPS_REG_S3,
|
||||||
|
"s4": UC_MIPS_REG_S4,
|
||||||
|
"s5": UC_MIPS_REG_S5,
|
||||||
|
"s6": UC_MIPS_REG_S6,
|
||||||
|
"s7": UC_MIPS_REG_S7,
|
||||||
|
"s8": UC_MIPS_REG_S8,
|
||||||
|
"k0": UC_MIPS_REG_K0,
|
||||||
|
"k1": UC_MIPS_REG_K1,
|
||||||
|
"gp": UC_MIPS_REG_GP,
|
||||||
|
"pc": UC_MIPS_REG_PC,
|
||||||
|
"sp": UC_MIPS_REG_SP,
|
||||||
|
"fp": UC_MIPS_REG_FP,
|
||||||
|
"ra": UC_MIPS_REG_RA,
|
||||||
|
"hi": UC_MIPS_REG_HI,
|
||||||
|
"lo": UC_MIPS_REG_LO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return registers[arch]
|
||||||
|
|
||||||
|
#---------------------------
|
||||||
|
# Callbacks for tracing
|
||||||
|
|
||||||
|
# TODO: Make integer-printing fixed widths dependent on bitness of architecture
|
||||||
|
# (i.e. only show 4 bytes for 32-bit, 8 bytes for 64-bit)
|
||||||
|
|
||||||
|
# TODO: Figure out how best to determine the capstone mode and architecture here
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# If Capstone is installed then we'll dump disassembly, otherwise just dump the binary.
|
||||||
|
from capstone import *
|
||||||
|
cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN)
|
||||||
|
def __trace_instruction(self, uc, address, size, user_data):
|
||||||
|
mem = uc.mem_read(address, size)
|
||||||
|
for (cs_address, cs_size, cs_mnemonic, cs_opstr) in cs.disasm_lite(bytes(mem), size):
|
||||||
|
print(" Instr: {:#016x}:\t{}\t{}".format(address, cs_mnemonic, cs_opstr))
|
||||||
|
except ImportError:
|
||||||
|
def __trace_instruction(self, uc, address, size, user_data):
|
||||||
|
print(" Instr: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __trace_instruction(self, uc, address, size, user_data):
|
||||||
|
print(" Instr: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))
|
||||||
|
|
||||||
|
def __trace_block(self, uc, address, size, user_data):
|
||||||
|
print("Basic Block: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))
|
||||||
|
|
||||||
|
def __trace_mem_access(self, uc, access, address, size, value, user_data):
|
||||||
|
if access == UC_MEM_WRITE:
|
||||||
|
print(" >>> Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value))
|
||||||
|
else:
|
||||||
|
print(" >>> Read: addr=0x{0:016x} size={1}".format(address, size))
|
||||||
|
|
||||||
|
def __trace_mem_invalid_access(self, uc, access, address, size, value, user_data):
|
||||||
|
if access == UC_MEM_WRITE_UNMAPPED:
|
||||||
|
print(" >>> INVALID Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value))
|
||||||
|
else:
|
||||||
|
print(" >>> INVALID Read: addr=0x{0:016x} size={1}".format(address, size))
|
||||||
|
|
290
unicorn_mode/patches/afl-unicorn-cpu-inl.h
Normal file
290
unicorn_mode/patches/afl-unicorn-cpu-inl.h
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
american fuzzy lop - high-performance binary-only instrumentation
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
Written by Andrew Griffiths <agriffiths@google.com> and
|
||||||
|
Michal Zalewski <lcamtuf@google.com>
|
||||||
|
|
||||||
|
TCG instrumentation and block chaining support by Andrea Biondo
|
||||||
|
<andrea.biondo965@gmail.com>
|
||||||
|
Adapted for afl-unicorn by Dominik Maier <mail@dmnk.co>
|
||||||
|
|
||||||
|
Idea & design very much by Andrew Griffiths.
|
||||||
|
|
||||||
|
Copyright 2015, 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
|
||||||
|
|
||||||
|
This code is a shim patched into the separately-distributed source
|
||||||
|
code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality
|
||||||
|
to implement AFL-style instrumentation and to take care of the remaining
|
||||||
|
parts of the AFL fork server logic.
|
||||||
|
|
||||||
|
The resulting QEMU binary is essentially a standalone instrumentation
|
||||||
|
tool; for an example of how to leverage it for other purposes, you can
|
||||||
|
have a look at afl-showmap.c.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include "../../config.h"
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
* VARIOUS AUXILIARY STUFF *
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
/* A snippet patched into tb_find_slow to inform the parent process that
|
||||||
|
we have hit a new block that hasn't been translated yet, and to tell
|
||||||
|
it to translate within its own context, too (this avoids translation
|
||||||
|
overhead in the next forked-off copy). */
|
||||||
|
|
||||||
|
#define AFL_UNICORN_CPU_SNIPPET1 do { \
|
||||||
|
afl_request_tsl(pc, cs_base, flags); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* This snippet kicks in when the instruction pointer is positioned at
|
||||||
|
_start and does the usual forkserver stuff, not very different from
|
||||||
|
regular instrumentation injected via afl-as.h. */
|
||||||
|
|
||||||
|
#define AFL_UNICORN_CPU_SNIPPET2 do { \
|
||||||
|
if(unlikely(afl_first_instr == 0)) { \
|
||||||
|
afl_setup(); \
|
||||||
|
afl_forkserver(env); \
|
||||||
|
afl_first_instr = 1; \
|
||||||
|
} \
|
||||||
|
afl_maybe_log(tb->pc); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* We use one additional file descriptor to relay "needs translation"
|
||||||
|
messages between the child and the fork server. */
|
||||||
|
|
||||||
|
#define TSL_FD (FORKSRV_FD - 1)
|
||||||
|
|
||||||
|
/* This is equivalent to afl-as.h: */
|
||||||
|
|
||||||
|
static unsigned char *afl_area_ptr;
|
||||||
|
|
||||||
|
/* Set in the child process in forkserver mode: */
|
||||||
|
|
||||||
|
static unsigned char afl_fork_child;
|
||||||
|
static unsigned int afl_forksrv_pid;
|
||||||
|
|
||||||
|
/* Instrumentation ratio: */
|
||||||
|
|
||||||
|
static unsigned int afl_inst_rms = MAP_SIZE;
|
||||||
|
|
||||||
|
/* Function declarations. */
|
||||||
|
|
||||||
|
static void afl_setup(void);
|
||||||
|
static void afl_forkserver(CPUArchState*);
|
||||||
|
static inline void afl_maybe_log(unsigned long);
|
||||||
|
|
||||||
|
static void afl_wait_tsl(CPUArchState*, int);
|
||||||
|
static void afl_request_tsl(target_ulong, target_ulong, uint64_t);
|
||||||
|
|
||||||
|
static TranslationBlock *tb_find_slow(CPUArchState*, target_ulong,
|
||||||
|
target_ulong, uint64_t);
|
||||||
|
|
||||||
|
/* Data structure passed around by the translate handlers: */
|
||||||
|
|
||||||
|
struct afl_tsl {
|
||||||
|
target_ulong pc;
|
||||||
|
target_ulong cs_base;
|
||||||
|
uint64_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
* ACTUAL IMPLEMENTATION *
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
/* Set up SHM region and initialize other stuff. */
|
||||||
|
|
||||||
|
static void afl_setup(void) {
|
||||||
|
|
||||||
|
char *id_str = getenv(SHM_ENV_VAR),
|
||||||
|
*inst_r = getenv("AFL_INST_RATIO");
|
||||||
|
|
||||||
|
int shm_id;
|
||||||
|
|
||||||
|
if (inst_r) {
|
||||||
|
|
||||||
|
unsigned int r;
|
||||||
|
|
||||||
|
r = atoi(inst_r);
|
||||||
|
|
||||||
|
if (r > 100) r = 100;
|
||||||
|
if (!r) r = 1;
|
||||||
|
|
||||||
|
afl_inst_rms = MAP_SIZE * r / 100;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id_str) {
|
||||||
|
|
||||||
|
shm_id = atoi(id_str);
|
||||||
|
afl_area_ptr = shmat(shm_id, NULL, 0);
|
||||||
|
|
||||||
|
if (afl_area_ptr == (void*)-1) exit(1);
|
||||||
|
|
||||||
|
/* With AFL_INST_RATIO set to a low value, we want to touch the bitmap
|
||||||
|
so that the parent doesn't give up on us. */
|
||||||
|
|
||||||
|
if (inst_r) afl_area_ptr[0] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fork server logic, invoked once we hit first emulated instruction. */
|
||||||
|
|
||||||
|
static void afl_forkserver(CPUArchState *env) {
|
||||||
|
|
||||||
|
static unsigned char tmp[4];
|
||||||
|
|
||||||
|
if (!afl_area_ptr) return;
|
||||||
|
|
||||||
|
/* Tell the parent that we're alive. If the parent doesn't want
|
||||||
|
to talk, assume that we're not running in forkserver mode. */
|
||||||
|
|
||||||
|
if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
|
||||||
|
|
||||||
|
afl_forksrv_pid = getpid();
|
||||||
|
|
||||||
|
/* All right, let's await orders... */
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
pid_t child_pid;
|
||||||
|
int status, t_fd[2];
|
||||||
|
|
||||||
|
/* Whoops, parent dead? */
|
||||||
|
|
||||||
|
if (read(FORKSRV_FD, tmp, 4) != 4) exit(2);
|
||||||
|
|
||||||
|
/* Establish a channel with child to grab translation commands. We'll
|
||||||
|
read from t_fd[0], child will write to TSL_FD. */
|
||||||
|
|
||||||
|
if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3);
|
||||||
|
close(t_fd[1]);
|
||||||
|
|
||||||
|
child_pid = fork();
|
||||||
|
if (child_pid < 0) exit(4);
|
||||||
|
|
||||||
|
if (!child_pid) {
|
||||||
|
|
||||||
|
/* Child process. Close descriptors and run free. */
|
||||||
|
|
||||||
|
afl_fork_child = 1;
|
||||||
|
close(FORKSRV_FD);
|
||||||
|
close(FORKSRV_FD + 1);
|
||||||
|
close(t_fd[0]);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parent. */
|
||||||
|
|
||||||
|
close(TSL_FD);
|
||||||
|
|
||||||
|
if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5);
|
||||||
|
|
||||||
|
/* Collect translation requests until child dies and closes the pipe. */
|
||||||
|
|
||||||
|
afl_wait_tsl(env, t_fd[0]);
|
||||||
|
|
||||||
|
/* Get and relay exit status to parent. */
|
||||||
|
|
||||||
|
if (waitpid(child_pid, &status, 0) < 0) exit(6);
|
||||||
|
if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The equivalent of the tuple logging routine from afl-as.h. */
|
||||||
|
|
||||||
|
static inline void afl_maybe_log(unsigned long cur_loc) {
|
||||||
|
|
||||||
|
static __thread unsigned long prev_loc;
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
//printf("IN AFL_MAYBE_LOG 0x%lx\n", cur_loc);
|
||||||
|
|
||||||
|
// MODIFIED FOR UNICORN MODE -> We want to log all addresses,
|
||||||
|
// so the checks for 'start < addr < end' are removed
|
||||||
|
if(!afl_area_ptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
//printf("afl_area_ptr = %p\n", afl_area_ptr);
|
||||||
|
|
||||||
|
/* Looks like QEMU always maps to fixed locations, so ASAN is not a
|
||||||
|
concern. Phew. But instruction addresses may be aligned. Let's mangle
|
||||||
|
the value to get something quasi-uniform. */
|
||||||
|
|
||||||
|
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
|
||||||
|
cur_loc &= MAP_SIZE - 1;
|
||||||
|
|
||||||
|
/* Implement probabilistic instrumentation by looking at scrambled block
|
||||||
|
address. This keeps the instrumented locations stable across runs. */
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
//printf("afl_inst_rms = 0x%lx\n", afl_inst_rms);
|
||||||
|
|
||||||
|
if (cur_loc >= afl_inst_rms) return;
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
//printf("cur_loc = 0x%lx\n", cur_loc);
|
||||||
|
|
||||||
|
afl_area_ptr[cur_loc ^ prev_loc]++;
|
||||||
|
prev_loc = cur_loc >> 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This code is invoked whenever QEMU decides that it doesn't have a
|
||||||
|
translation of a particular block and needs to compute it. When this happens,
|
||||||
|
we tell the parent to mirror the operation, so that the next fork() has a
|
||||||
|
cached copy. */
|
||||||
|
|
||||||
|
static void afl_request_tsl(target_ulong pc, target_ulong cb, uint64_t flags) {
|
||||||
|
|
||||||
|
struct afl_tsl t;
|
||||||
|
|
||||||
|
if (!afl_fork_child) return;
|
||||||
|
|
||||||
|
t.pc = pc;
|
||||||
|
t.cs_base = cb;
|
||||||
|
t.flags = flags;
|
||||||
|
|
||||||
|
if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This is the other side of the same channel. Since timeouts are handled by
|
||||||
|
afl-fuzz simply killing the child, we can just wait until the pipe breaks. */
|
||||||
|
|
||||||
|
static void afl_wait_tsl(CPUArchState *env, int fd) {
|
||||||
|
|
||||||
|
struct afl_tsl t;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/* Broken pipe means it's time to return to the fork server routine. */
|
||||||
|
|
||||||
|
if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
|
||||||
|
break;
|
||||||
|
|
||||||
|
tb_find_slow(env, t.pc, t.cs_base, t.flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
107
unicorn_mode/patches/patches.diff
Normal file
107
unicorn_mode/patches/patches.diff
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
diff --git a/Makefile b/Makefile
|
||||||
|
index 7d73782..fb3ccfd 100644
|
||||||
|
--- a/Makefile
|
||||||
|
+++ b/Makefile
|
||||||
|
@@ -88,6 +88,10 @@ AR = llvm-ar
|
||||||
|
LDFLAGS := -fsanitize=address ${LDFLAGS}
|
||||||
|
endif
|
||||||
|
|
||||||
|
+ifeq ($(UNICORN_AFL),yes)
|
||||||
|
+UNICORN_CFLAGS += -DUNICORN_AFL
|
||||||
|
+endif
|
||||||
|
+
|
||||||
|
ifeq ($(CROSS),)
|
||||||
|
CC ?= cc
|
||||||
|
AR ?= ar
|
||||||
|
diff --git a/config.mk b/config.mk
|
||||||
|
index c3621fb..c7b4f7e 100644
|
||||||
|
--- a/config.mk
|
||||||
|
+++ b/config.mk
|
||||||
|
@@ -8,7 +8,7 @@
|
||||||
|
# Compile with debug info when you want to debug code.
|
||||||
|
# Change this to 'no' for release edition.
|
||||||
|
|
||||||
|
-UNICORN_DEBUG ?= yes
|
||||||
|
+UNICORN_DEBUG ?= no
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Specify which archs you want to compile in. By default, we build all archs.
|
||||||
|
@@ -28,3 +28,9 @@ UNICORN_STATIC ?= yes
|
||||||
|
# a shared library.
|
||||||
|
|
||||||
|
UNICORN_SHARED ?= yes
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+################################################################################
|
||||||
|
+# Changing 'UNICORN_AFLL = yes' to 'UNICORN_AFL = no' disables AFL instrumentation
|
||||||
|
+
|
||||||
|
+UNICORN_AFL ?= yes
|
||||||
|
diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c
|
||||||
|
index 7755adf..8114b70 100644
|
||||||
|
--- a/qemu/cpu-exec.c
|
||||||
|
+++ b/qemu/cpu-exec.c
|
||||||
|
@@ -24,6 +24,11 @@
|
||||||
|
|
||||||
|
#include "uc_priv.h"
|
||||||
|
|
||||||
|
+#if defined(UNICORN_AFL)
|
||||||
|
+#include "../afl-unicorn-cpu-inl.h"
|
||||||
|
+static int afl_first_instr = 0;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
static tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr);
|
||||||
|
static TranslationBlock *tb_find_slow(CPUArchState *env, target_ulong pc,
|
||||||
|
target_ulong cs_base, uint64_t flags);
|
||||||
|
@@ -231,6 +236,10 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env) // qq
|
||||||
|
next_tb & TB_EXIT_MASK, tb);
|
||||||
|
}
|
||||||
|
|
||||||
|
+#if defined(UNICORN_AFL)
|
||||||
|
+ AFL_UNICORN_CPU_SNIPPET2;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/* cpu_interrupt might be called while translating the
|
||||||
|
TB, but before it is linked into a potentially
|
||||||
|
infinite loop and becomes env->current_tb. Avoid
|
||||||
|
@@ -369,6 +378,11 @@ static TranslationBlock *tb_find_slow(CPUArchState *env, target_ulong pc,
|
||||||
|
not_found:
|
||||||
|
/* if no translated code available, then translate it now */
|
||||||
|
tb = tb_gen_code(cpu, pc, cs_base, (int)flags, 0); // qq
|
||||||
|
+
|
||||||
|
+#if defined(UNICORN_AFL)
|
||||||
|
+ /* There seems to be no chaining in unicorn ever? :( */
|
||||||
|
+ AFL_UNICORN_CPU_SNIPPET1;
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
found:
|
||||||
|
/* Move the last found TB to the head of the list */
|
||||||
|
diff --git a/qemu/translate-all.c b/qemu/translate-all.c
|
||||||
|
index 1a96c34..7ef4878 100644
|
||||||
|
--- a/qemu/translate-all.c
|
||||||
|
+++ b/qemu/translate-all.c
|
||||||
|
@@ -403,11 +403,25 @@ static PageDesc *page_find_alloc(struct uc_struct *uc, tb_page_addr_t index, int
|
||||||
|
|
||||||
|
#if defined(CONFIG_USER_ONLY)
|
||||||
|
/* We can't use g_malloc because it may recurse into a locked mutex. */
|
||||||
|
+#if defined(UNICORN_AFL)
|
||||||
|
+ /* This was added by unicorn-afl to bail out semi-gracefully if out of memory. */
|
||||||
|
+# define ALLOC(P, SIZE) \
|
||||||
|
+ do { \
|
||||||
|
+ void* _tmp = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, \
|
||||||
|
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \
|
||||||
|
+ if (_tmp == (void*)-1) { \
|
||||||
|
+ qemu_log(">>> Out of memory for stack, bailing out. <<<\n"); \
|
||||||
|
+ exit(1); \
|
||||||
|
+ } \
|
||||||
|
+ (P) = _tmp; \
|
||||||
|
+ } while (0)
|
||||||
|
+#else /* !UNICORN_AFL */
|
||||||
|
# define ALLOC(P, SIZE) \
|
||||||
|
do { \
|
||||||
|
P = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, \
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \
|
||||||
|
} while (0)
|
||||||
|
+#endif /* UNICORN_AFL */
|
||||||
|
#else
|
||||||
|
# define ALLOC(P, SIZE) \
|
||||||
|
do { P = g_malloc0(SIZE); } while (0)
|
41
unicorn_mode/samples/simple/COMPILE.md
Normal file
41
unicorn_mode/samples/simple/COMPILE.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
Compiling simple_target.c
|
||||||
|
==========================
|
||||||
|
|
||||||
|
You shouldn't need to compile simple_target.c since a MIPS binary version is
|
||||||
|
pre-built and shipped with afl-unicorn. This file documents how the binary
|
||||||
|
was built in case you want to rebuild it or recompile it for any reason.
|
||||||
|
|
||||||
|
The pre-built binary (simple_target.bin) was built by cross-compiling
|
||||||
|
simple_target.c for MIPS using the mips-linux-gnu-gcc package on an Ubuntu
|
||||||
|
16.04 LTS system. This cross compiler (and associated binutils) was installed
|
||||||
|
from apt-get packages:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt-get install gcc-mips-linux-gnu
|
||||||
|
```
|
||||||
|
|
||||||
|
simple_target.c was compiled without optimization, position-independent,
|
||||||
|
and without standard libraries using the following command line:
|
||||||
|
|
||||||
|
```
|
||||||
|
mips-linux-gnu-gcc -o simple_target.elf simple_target.c -fPIC -O0 -nostdlib
|
||||||
|
```
|
||||||
|
|
||||||
|
The .text section from the resulting ELF binary was then extracted to create
|
||||||
|
the raw binary blob that is loaded and emulated by simple_test_harness.py:
|
||||||
|
|
||||||
|
```
|
||||||
|
mips-linux-gnu-objcopy -O binary --only-section=.text simple_target.elf simple_target.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
In summary, to recreate simple_taget.bin execute the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
mips-linux-gnu-gcc -o simple_target.elf simple_target.c -fPIC -O0 -nostdlib
|
||||||
|
&& mips-linux-gnu-objcopy -O binary --only-section=.text simple_target.elf simple_target.bin
|
||||||
|
&& rm simple_target.elf
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the output of this is padded with nulls for 16-byte alignment. This is
|
||||||
|
important when emulating it, as NOPs will be added after the return of main()
|
||||||
|
as necessary.
|
1
unicorn_mode/samples/simple/sample_inputs/sample1.bin
Normal file
1
unicorn_mode/samples/simple/sample_inputs/sample1.bin
Normal file
@ -0,0 +1 @@
|
|||||||
|
abcd
|
BIN
unicorn_mode/samples/simple/sample_inputs/sample2.bin
Normal file
BIN
unicorn_mode/samples/simple/sample_inputs/sample2.bin
Normal file
Binary file not shown.
1
unicorn_mode/samples/simple/sample_inputs/sample3.bin
Normal file
1
unicorn_mode/samples/simple/sample_inputs/sample3.bin
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
1
unicorn_mode/samples/simple/sample_inputs/sample4.bin
Normal file
1
unicorn_mode/samples/simple/sample_inputs/sample4.bin
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
1
unicorn_mode/samples/simple/sample_inputs/sample5.bin
Normal file
1
unicorn_mode/samples/simple/sample_inputs/sample5.bin
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
BIN
unicorn_mode/samples/simple/simple_target.bin
Normal file
BIN
unicorn_mode/samples/simple/simple_target.bin
Normal file
Binary file not shown.
31
unicorn_mode/samples/simple/simple_target.c
Normal file
31
unicorn_mode/samples/simple/simple_target.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Sample target file to test afl-unicorn fuzzing capabilities.
|
||||||
|
* This is a very trivial example that will crash pretty easily
|
||||||
|
* in several different exciting ways.
|
||||||
|
*
|
||||||
|
* Input is assumed to come from a buffer located at DATA_ADDRESS
|
||||||
|
* (0x00300000), so make sure that your Unicorn emulation of this
|
||||||
|
* puts user data there.
|
||||||
|
*
|
||||||
|
* Written by Nathan Voss <njvoss99@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Magic address where mutated data will be placed
|
||||||
|
#define DATA_ADDRESS 0x00300000
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
unsigned char *data_buf = (unsigned char *) DATA_ADDRESS;
|
||||||
|
|
||||||
|
if (data_buf[20] != 0) {
|
||||||
|
// Cause an 'invalid read' crash if data[0..3] == '\x01\x02\x03\x04'
|
||||||
|
unsigned char invalid_read = *(unsigned char *) 0x00000000;
|
||||||
|
} else if (data_buf[0] > 0x10 && data_buf[0] < 0x20 && data_buf[1] > data_buf[2]) {
|
||||||
|
// Cause an 'invalid read' crash if (0x10 < data[0] < 0x20) and data[1] > data[2]
|
||||||
|
unsigned char invalid_read = *(unsigned char *) 0x00000000;
|
||||||
|
} else if (data_buf[9] == 0x00 && data_buf[10] != 0x00 && data_buf[11] == 0x00) {
|
||||||
|
// Cause a crash if data[10] is not zero, but [9] and [11] are zero
|
||||||
|
unsigned char invalid_read = *(unsigned char *) 0x00000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
170
unicorn_mode/samples/simple/simple_test_harness.py
Normal file
170
unicorn_mode/samples/simple/simple_test_harness.py
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
"""
|
||||||
|
Simple test harness for AFL's Unicorn Mode.
|
||||||
|
|
||||||
|
This loads the simple_target.bin binary (precompiled as MIPS code) into
|
||||||
|
Unicorn's memory map for emulation, places the specified input into
|
||||||
|
simple_target's buffer (hardcoded to be at 0x300000), and executes 'main()'.
|
||||||
|
If any crashes occur during emulation, this script throws a matching signal
|
||||||
|
to tell AFL that a crash occurred.
|
||||||
|
|
||||||
|
Run under AFL as follows:
|
||||||
|
|
||||||
|
$ cd <afl_path>/unicorn_mode/samples/simple/
|
||||||
|
$ ../../../afl-fuzz -U -m none -i ./sample_inputs -o ./output -- python simple_test_harness.py @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
|
||||||
|
from unicorn import *
|
||||||
|
from unicorn.mips_const import *
|
||||||
|
|
||||||
|
# Path to the file containing the binary to emulate
|
||||||
|
BINARY_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'simple_target.bin')
|
||||||
|
|
||||||
|
# Memory map for the code to be tested
|
||||||
|
CODE_ADDRESS = 0x00100000 # Arbitrary address where code to test will be loaded
|
||||||
|
CODE_SIZE_MAX = 0x00010000 # Max size for the code (64kb)
|
||||||
|
STACK_ADDRESS = 0x00200000 # Address of the stack (arbitrarily chosen)
|
||||||
|
STACK_SIZE = 0x00010000 # Size of the stack (arbitrarily chosen)
|
||||||
|
DATA_ADDRESS = 0x00300000 # Address where mutated data will be placed
|
||||||
|
DATA_SIZE_MAX = 0x00010000 # Maximum allowable size of mutated data
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If Capstone is installed then we'll dump disassembly, otherwise just dump the binary.
|
||||||
|
from capstone import *
|
||||||
|
cs = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN)
|
||||||
|
def unicorn_debug_instruction(uc, address, size, user_data):
|
||||||
|
mem = uc.mem_read(address, size)
|
||||||
|
for (cs_address, cs_size, cs_mnemonic, cs_opstr) in cs.disasm_lite(bytes(mem), size):
|
||||||
|
print(" Instr: {:#016x}:\t{}\t{}".format(address, cs_mnemonic, cs_opstr))
|
||||||
|
except ImportError:
|
||||||
|
def unicorn_debug_instruction(uc, address, size, user_data):
|
||||||
|
print(" Instr: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))
|
||||||
|
|
||||||
|
def unicorn_debug_block(uc, address, size, user_data):
|
||||||
|
print("Basic Block: addr=0x{0:016x}, size=0x{1:016x}".format(address, size))
|
||||||
|
|
||||||
|
def unicorn_debug_mem_access(uc, access, address, size, value, user_data):
|
||||||
|
if access == UC_MEM_WRITE:
|
||||||
|
print(" >>> Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value))
|
||||||
|
else:
|
||||||
|
print(" >>> Read: addr=0x{0:016x} size={1}".format(address, size))
|
||||||
|
|
||||||
|
def unicorn_debug_mem_invalid_access(uc, access, address, size, value, user_data):
|
||||||
|
if access == UC_MEM_WRITE_UNMAPPED:
|
||||||
|
print(" >>> INVALID Write: addr=0x{0:016x} size={1} data=0x{2:016x}".format(address, size, value))
|
||||||
|
else:
|
||||||
|
print(" >>> INVALID Read: addr=0x{0:016x} size={1}".format(address, size))
|
||||||
|
|
||||||
|
def force_crash(uc_error):
|
||||||
|
# This function should be called to indicate to AFL that a crash occurred during emulation.
|
||||||
|
# Pass in the exception received from Uc.emu_start()
|
||||||
|
mem_errors = [
|
||||||
|
UC_ERR_READ_UNMAPPED, UC_ERR_READ_PROT, UC_ERR_READ_UNALIGNED,
|
||||||
|
UC_ERR_WRITE_UNMAPPED, UC_ERR_WRITE_PROT, UC_ERR_WRITE_UNALIGNED,
|
||||||
|
UC_ERR_FETCH_UNMAPPED, UC_ERR_FETCH_PROT, UC_ERR_FETCH_UNALIGNED,
|
||||||
|
]
|
||||||
|
if uc_error.errno in mem_errors:
|
||||||
|
# Memory error - throw SIGSEGV
|
||||||
|
os.kill(os.getpid(), signal.SIGSEGV)
|
||||||
|
elif uc_error.errno == UC_ERR_INSN_INVALID:
|
||||||
|
# Invalid instruction - throw SIGILL
|
||||||
|
os.kill(os.getpid(), signal.SIGILL)
|
||||||
|
else:
|
||||||
|
# Not sure what happened - throw SIGABRT
|
||||||
|
os.kill(os.getpid(), signal.SIGABRT)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Test harness for simple_target.bin")
|
||||||
|
parser.add_argument('input_file', type=str, help="Path to the file containing the mutated input to load")
|
||||||
|
parser.add_argument('-d', '--debug', default=False, action="store_true", help="Enables debug tracing")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Instantiate a MIPS32 big endian Unicorn Engine instance
|
||||||
|
uc = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_BIG_ENDIAN)
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
uc.hook_add(UC_HOOK_BLOCK, unicorn_debug_block)
|
||||||
|
uc.hook_add(UC_HOOK_CODE, unicorn_debug_instruction)
|
||||||
|
uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, unicorn_debug_mem_access)
|
||||||
|
uc.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_READ_INVALID, unicorn_debug_mem_invalid_access)
|
||||||
|
|
||||||
|
#---------------------------------------------------
|
||||||
|
# Load the binary to emulate and map it into memory
|
||||||
|
|
||||||
|
print("Loading data input from {}".format(args.input_file))
|
||||||
|
binary_file = open(BINARY_FILE, 'rb')
|
||||||
|
binary_code = binary_file.read()
|
||||||
|
binary_file.close()
|
||||||
|
|
||||||
|
# Apply constraints to the mutated input
|
||||||
|
if len(binary_code) > CODE_SIZE_MAX:
|
||||||
|
print("Binary code is too large (> {} bytes)".format(CODE_SIZE_MAX))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Write the mutated command into the data buffer
|
||||||
|
uc.mem_map(CODE_ADDRESS, CODE_SIZE_MAX)
|
||||||
|
uc.mem_write(CODE_ADDRESS, binary_code)
|
||||||
|
|
||||||
|
# Set the program counter to the start of the code
|
||||||
|
start_address = CODE_ADDRESS # Address of entry point of main()
|
||||||
|
end_address = CODE_ADDRESS + 0xf4 # Address of last instruction in main()
|
||||||
|
uc.reg_write(UC_MIPS_REG_PC, start_address)
|
||||||
|
|
||||||
|
#-----------------
|
||||||
|
# Setup the stack
|
||||||
|
|
||||||
|
uc.mem_map(STACK_ADDRESS, STACK_SIZE)
|
||||||
|
uc.reg_write(UC_MIPS_REG_SP, STACK_ADDRESS + STACK_SIZE)
|
||||||
|
|
||||||
|
#-----------------------------------------------------
|
||||||
|
# Emulate 1 instruction to kick off AFL's fork server
|
||||||
|
# THIS MUST BE DONE BEFORE LOADING USER DATA!
|
||||||
|
# If this isn't done every single run, the AFL fork server
|
||||||
|
# will not be started appropriately and you'll get erratic results!
|
||||||
|
# It doesn't matter what this returns with, it just has to execute at
|
||||||
|
# least one instruction in order to get the fork server started.
|
||||||
|
|
||||||
|
# Execute 1 instruction just to startup the forkserver
|
||||||
|
print("Starting the AFL forkserver by executing 1 instruction")
|
||||||
|
try:
|
||||||
|
uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), 0, 0, count=1)
|
||||||
|
except UcError as e:
|
||||||
|
print("ERROR: Failed to execute a single instruction (error: {})!".format(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
#-----------------------------------------------
|
||||||
|
# Load the mutated input and map it into memory
|
||||||
|
|
||||||
|
# Load the mutated input from disk
|
||||||
|
print("Loading data input from {}".format(args.input_file))
|
||||||
|
input_file = open(args.input_file, 'rb')
|
||||||
|
input = input_file.read()
|
||||||
|
input_file.close()
|
||||||
|
|
||||||
|
# Apply constraints to the mutated input
|
||||||
|
if len(input) > DATA_SIZE_MAX:
|
||||||
|
print("Test input is too long (> {} bytes)".format(DATA_SIZE_MAX))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Write the mutated command into the data buffer
|
||||||
|
uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX)
|
||||||
|
uc.mem_write(DATA_ADDRESS, input)
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# Emulate the code, allowing it to process the mutated input
|
||||||
|
|
||||||
|
print("Executing until a crash or execution reaches 0x{0:016x}".format(end_address))
|
||||||
|
try:
|
||||||
|
result = uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=0, count=0)
|
||||||
|
except UcError as e:
|
||||||
|
print("Execution failed with error: {}".format(e))
|
||||||
|
force_crash(e)
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Reference in New Issue
Block a user