From ac169c30874251e3dcd74f9ce4b43a7d26435cdd Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 22 Jan 2022 17:22:00 +0100 Subject: fix makefiles for uc2 c examples --- custom_mutators/grammar_mutator/grammar_mutator | 2 +- unicorn_mode/samples/c/Makefile | 39 ++-- unicorn_mode/samples/c/harness.c | 5 +- unicorn_mode/samples/persistent/Makefile | 38 ++-- unicorn_mode/samples/persistent/harness.c | 1 + unicorn_mode/samples/python_simple/COMPILE.md | 40 ++++ .../python_simple/sample_inputs/sample1.bin | 1 + .../python_simple/sample_inputs/sample2.bin | Bin 0 -> 1 bytes .../python_simple/sample_inputs/sample3.bin | 1 + .../python_simple/sample_inputs/sample4.bin | 1 + .../python_simple/sample_inputs/sample5.bin | 1 + .../samples/python_simple/simple_target.bin | Bin 0 -> 256 bytes unicorn_mode/samples/python_simple/simple_target.c | 31 +++ .../samples/python_simple/simple_test_harness.py | 163 +++++++++++++++ .../python_simple/simple_test_harness_alt.py | 223 +++++++++++++++++++++ unicorn_mode/samples/simple/COMPILE.md | 40 ---- .../samples/simple/sample_inputs/sample1.bin | 1 - .../samples/simple/sample_inputs/sample2.bin | Bin 1 -> 0 bytes .../samples/simple/sample_inputs/sample3.bin | 1 - .../samples/simple/sample_inputs/sample4.bin | 1 - .../samples/simple/sample_inputs/sample5.bin | 1 - unicorn_mode/samples/simple/simple_target.bin | Bin 256 -> 0 bytes unicorn_mode/samples/simple/simple_target.c | 31 --- unicorn_mode/samples/simple/simple_test_harness.py | 163 --------------- .../samples/simple/simple_test_harness_alt.py | 223 --------------------- unicorn_mode/samples/speedtest/c/Makefile | 36 ++-- unicorn_mode/samples/speedtest/c/harness.c | 5 +- 27 files changed, 530 insertions(+), 518 deletions(-) create mode 100644 unicorn_mode/samples/python_simple/COMPILE.md create mode 100644 unicorn_mode/samples/python_simple/sample_inputs/sample1.bin create mode 100644 unicorn_mode/samples/python_simple/sample_inputs/sample2.bin create mode 100644 unicorn_mode/samples/python_simple/sample_inputs/sample3.bin create mode 100644 unicorn_mode/samples/python_simple/sample_inputs/sample4.bin create mode 100644 unicorn_mode/samples/python_simple/sample_inputs/sample5.bin create mode 100644 unicorn_mode/samples/python_simple/simple_target.bin create mode 100644 unicorn_mode/samples/python_simple/simple_target.c create mode 100644 unicorn_mode/samples/python_simple/simple_test_harness.py create mode 100644 unicorn_mode/samples/python_simple/simple_test_harness_alt.py delete mode 100644 unicorn_mode/samples/simple/COMPILE.md delete mode 100644 unicorn_mode/samples/simple/sample_inputs/sample1.bin delete mode 100644 unicorn_mode/samples/simple/sample_inputs/sample2.bin delete mode 100644 unicorn_mode/samples/simple/sample_inputs/sample3.bin delete mode 100644 unicorn_mode/samples/simple/sample_inputs/sample4.bin delete mode 100644 unicorn_mode/samples/simple/sample_inputs/sample5.bin delete mode 100644 unicorn_mode/samples/simple/simple_target.bin delete mode 100644 unicorn_mode/samples/simple/simple_target.c delete mode 100644 unicorn_mode/samples/simple/simple_test_harness.py delete mode 100644 unicorn_mode/samples/simple/simple_test_harness_alt.py diff --git a/custom_mutators/grammar_mutator/grammar_mutator b/custom_mutators/grammar_mutator/grammar_mutator index cbe5e327..6ca490c6 160000 --- a/custom_mutators/grammar_mutator/grammar_mutator +++ b/custom_mutators/grammar_mutator/grammar_mutator @@ -1 +1 @@ -Subproject commit cbe5e32752773945e0142fac9f1b7a0ccb5dcdff +Subproject commit 6ca490c66b949db20d8c861ebc8fb2e6ca725ead diff --git a/unicorn_mode/samples/c/Makefile b/unicorn_mode/samples/c/Makefile index cb491e10..fd6dbe77 100644 --- a/unicorn_mode/samples/c/Makefile +++ b/unicorn_mode/samples/c/Makefile @@ -4,29 +4,28 @@ .POSIX: UNAME_S =$(shell uname -s)# GNU make UNAME_S:sh=uname -s # BSD make -_UNIQ=_QINU_ -LIBDIR = ../../unicornafl +UNICORNAFL_LIB = ../../unicornafl/build +UNICORN_LIB = ../../unicornafl/unicorn/build BIN_EXT = AR_EXT = a # Verbose output? V ?= 0 -CFLAGS += -Wall -Werror -I../../unicornafl/include +CFLAGS += -Wall -Werror -I../../unicornafl/unicorn/include -I../../unicornafl/include -LDFLAGS += -L$(LIBDIR) -lpthread -lm +LDFLAGS += -L$(UNICORNAFL_LIB) -L$(UNICORN_LIB) -lpthread -lm -lunicornafl -lunicorn -lc++ -_LRT = $(_UNIQ)$(UNAME_S:Linux=) -__LRT = $(_LRT:$(_UNIQ)=-lrt) -LRT = $(__LRT:$(_UNIQ)=) +ifeq ($(UNAME), Linux) +# do something Linux-y +LRT = -lrt +else +LRT = +endif LDFLAGS += $(LRT) -_CC = $(_UNIQ)$(CROSS) -__CC = $(_CC:$(_UNIQ)=$(CC)) -MYCC = $(__CC:$(_UNIQ)$(CROSS)=$(CROSS)gcc) - .PHONY: all clean all: harness @@ -34,14 +33,20 @@ all: harness clean: rm -rf *.o harness harness-debug -harness.o: harness.c ../../unicornafl/include/unicorn/*.h - ${MYCC} ${CFLAGS} -O3 -c harness.c +harness.o: harness.c ../../unicornafl/unicorn/include/unicorn/*.h + ${CC} ${CFLAGS} -O3 -c harness.c + +harness-debug.o: harness.c ../../unicornafl/unicorn/include/unicorn/*.h + ${CC} ${CFLAGS} -g -c harness.c -o $@ -harness-debug.o: harness.c ../../unicornafl/include/unicorn/*.h - ${MYCC} ${CFLAGS} -g -c harness.c -o $@ +../../unicornafl/build/libunicornafl.a: + cd ../.. && ./build_unicorn_support.sh harness: harness.o - ${MYCC} -L${LIBDIR} harness.o ../../unicornafl/libunicornafl.a $(LDFLAGS) -o $@ + ${CC} harness.o ../../unicornafl/build/libunicornafl.a $(LDFLAGS) -o $@ debug: harness-debug.o - ${MYCC} -L${LIBDIR} harness.o ../../unicornafl/libunicornafl.a $(LDFLAGS) -o harness-debug + ${CC} harness.o ../../unicornafl/build/libunicornafl.a $(LDFLAGS) -o harness-debug + +fuzz: harness + DYLD_FALLBACK_LIBRARY_PATH="../../unicornafl/unicorn/build" LD_LIBRARY_PATH="../../unicornafl/unicorn/build" ../../../afl-fuzz -m none -i sample_inputs -o out -- ./harness @@ diff --git a/unicorn_mode/samples/c/harness.c b/unicorn_mode/samples/c/harness.c index 4bda6e2d..3a93aeaa 100644 --- a/unicorn_mode/samples/c/harness.c +++ b/unicorn_mode/samples/c/harness.c @@ -26,6 +26,7 @@ #include #include +#include // Path to the file containing the binary to emulate #define BINARY_FILE ("persistent_target_x86_64") @@ -141,7 +142,7 @@ static void mem_map_checked(uc_engine *uc, uint64_t addr, size_t size, uint32_t //printf("SIZE %llx, align: %llx\n", size, ALIGNMENT); uc_err err = uc_mem_map(uc, addr, size, mode); if (err != UC_ERR_OK) { - printf("Error mapping %ld bytes at 0x%lx: %s (mode: %d)\n", size, addr, uc_strerror(err), mode); + printf("Error mapping %ld bytes at 0x%llx: %s (mode: %d)\n", size, (unsigned long long) addr, uc_strerror(err), (int) mode); exit(1); } } @@ -184,7 +185,7 @@ int main(int argc, char **argv, char **envp) { // Map memory. mem_map_checked(uc, BASE_ADDRESS, len, UC_PROT_ALL); - printf("Len: %lx\n", len); + printf("Len: %lx\n", (unsigned long) len); fflush(stdout); // write machine code to be emulated to memory diff --git a/unicorn_mode/samples/persistent/Makefile b/unicorn_mode/samples/persistent/Makefile index 80a47550..35584416 100644 --- a/unicorn_mode/samples/persistent/Makefile +++ b/unicorn_mode/samples/persistent/Makefile @@ -4,29 +4,28 @@ .POSIX: UNAME_S =$(shell uname -s)# GNU make UNAME_S:sh=uname -s # BSD make -_UNIQ=_QINU_ -LIBDIR = ../../unicornafl +UNICORNAFL_LIB = ../../unicornafl/build +UNICORN_LIB = ../../unicornafl/unicorn/build BIN_EXT = AR_EXT = a # Verbose output? V ?= 0 -CFLAGS += -Wall -Werror -I../../unicornafl/include +CFLAGS += -Wall -Werror -I../../unicornafl/unicorn/include -I../../unicornafl/include -LDFLAGS += -L$(LIBDIR) -lpthread -lm +LDFLAGS += -L$(UNICORNAFL_LIB) -L$(UNICORN_LIB) -lpthread -lm -lunicornafl -lunicorn -lc++ -_LRT = $(_UNIQ)$(UNAME_S) -__LRT = $(_LRT:$(_UNIQ)Linux=-lrt) -LRT = $(__LRT:$(_UNIQ)$(UNAME_S)=) +ifeq ($(UNAME), Linux) +# do something Linux-y +LRT = -lrt +else +LRT = +endif LDFLAGS += $(LRT) -_CC = $(_UNIQ)$(CROSS) -__CC = $(_CC:$(_UNIQ)=$(CC)) -MYCC = $(__CC:$(_UNIQ)$(CROSS)=$(CROSS)gcc) - .PHONY: all clean all: harness @@ -34,20 +33,23 @@ all: harness clean: rm -rf *.o harness harness-debug -harness.o: harness.c ../../unicornafl/include/unicorn/*.h - ${MYCC} ${CFLAGS} -O3 -c harness.c +harness.o: harness.c ../../unicornafl/unicorn/include/unicorn/*.h + ${CC} ${CFLAGS} -O3 -c harness.c -harness-debug.o: harness.c ../../unicornafl/include/unicorn/*.h - ${MYCC} ${CFLAGS} -DAFL_DEBUG=1 -g -c harness.c -o $@ +harness-debug.o: harness.c ../../unicornafl/unicorn/include/unicorn/*.h + ${CC} ${CFLAGS} -DAFL_DEBUG=1 -g -c harness.c -o $@ harness: harness.o - ${MYCC} -L${LIBDIR} harness.o ../../unicornafl/libunicornafl.a $(LDFLAGS) -o $@ + ${CC} harness.o ../../unicornafl/build/libunicornafl.a $(LDFLAGS) -o $@ debug: harness-debug.o - ${MYCC} -L${LIBDIR} harness-debug.o ../../unicornafl/libunicornafl.a $(LDFLAGS) -o harness-debug + ${CC} harness-debug.o ../../unicornafl/build/libunicornafl.a $(LDFLAGS) -o harness-debug + +../../unicornafl/build/libunicornafl.a: + cd ../.. && ./build_unicorn_support.sh fuzz: harness - ../../../afl-fuzz -m none -i sample_inputs -o out -- ./harness @@ + DYLD_FALLBACK_LIBRARY_PATH="../../unicornafl/unicorn/build" LD_LIBRARY_PATH="../../unicornafl/unicorn/build" ../../../afl-fuzz -m none -i sample_inputs -o out -- ./harness @@ debugmake: @echo UNAME_S=$(UNAME_S), _LRT=$(_LRT), __LRT=$(__LRT), LRT=$(LRT) diff --git a/unicorn_mode/samples/persistent/harness.c b/unicorn_mode/samples/persistent/harness.c index eae3f1fc..37b652e0 100644 --- a/unicorn_mode/samples/persistent/harness.c +++ b/unicorn_mode/samples/persistent/harness.c @@ -30,6 +30,7 @@ #include #include +#include // Path to the file containing the binary to emulate #define BINARY_FILE ("persistent_target_x86_64") diff --git a/unicorn_mode/samples/python_simple/COMPILE.md b/unicorn_mode/samples/python_simple/COMPILE.md new file mode 100644 index 00000000..f7bf5b50 --- /dev/null +++ b/unicorn_mode/samples/python_simple/COMPILE.md @@ -0,0 +1,40 @@ +# 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. diff --git a/unicorn_mode/samples/python_simple/sample_inputs/sample1.bin b/unicorn_mode/samples/python_simple/sample_inputs/sample1.bin new file mode 100644 index 00000000..85df5078 --- /dev/null +++ b/unicorn_mode/samples/python_simple/sample_inputs/sample1.bin @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/unicorn_mode/samples/python_simple/sample_inputs/sample2.bin b/unicorn_mode/samples/python_simple/sample_inputs/sample2.bin new file mode 100644 index 00000000..f76dd238 Binary files /dev/null and b/unicorn_mode/samples/python_simple/sample_inputs/sample2.bin differ diff --git a/unicorn_mode/samples/python_simple/sample_inputs/sample3.bin b/unicorn_mode/samples/python_simple/sample_inputs/sample3.bin new file mode 100644 index 00000000..6b2aaa76 --- /dev/null +++ b/unicorn_mode/samples/python_simple/sample_inputs/sample3.bin @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unicorn_mode/samples/python_simple/sample_inputs/sample4.bin b/unicorn_mode/samples/python_simple/sample_inputs/sample4.bin new file mode 100644 index 00000000..71bd63e6 --- /dev/null +++ b/unicorn_mode/samples/python_simple/sample_inputs/sample4.bin @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unicorn_mode/samples/python_simple/sample_inputs/sample5.bin b/unicorn_mode/samples/python_simple/sample_inputs/sample5.bin new file mode 100644 index 00000000..aed2973e --- /dev/null +++ b/unicorn_mode/samples/python_simple/sample_inputs/sample5.bin @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unicorn_mode/samples/python_simple/simple_target.bin b/unicorn_mode/samples/python_simple/simple_target.bin new file mode 100644 index 00000000..0095eb6c Binary files /dev/null and b/unicorn_mode/samples/python_simple/simple_target.bin differ diff --git a/unicorn_mode/samples/python_simple/simple_target.c b/unicorn_mode/samples/python_simple/simple_target.c new file mode 100644 index 00000000..49e1b4d8 --- /dev/null +++ b/unicorn_mode/samples/python_simple/simple_target.c @@ -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 + */ + +// 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; +} diff --git a/unicorn_mode/samples/python_simple/simple_test_harness.py b/unicorn_mode/samples/python_simple/simple_test_harness.py new file mode 100644 index 00000000..cd04ad3a --- /dev/null +++ b/unicorn_mode/samples/python_simple/simple_test_harness.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +""" + 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, unicornafl will + tell AFL that a crash occurred. + + Run under AFL as follows: + + $ cd /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 unicornafl import * +from unicornafl.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 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( + "-t", + "--trace", + 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.trace: + 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) + + # reserve some space for data + uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX) + + # ----------------------------------------------------- + # Set up a callback to place input data (do little work here, it's called for every single iteration) + # We did not pass in any data and don't use persistent mode, so we can ignore these params. + # Be sure to check out the docstrings for the uc.afl_* functions. + def place_input_callback(uc, input, persistent_round, data): + # Apply constraints to the mutated input + if len(input) > DATA_SIZE_MAX: + # print("Test input is too long (> {} bytes)") + return False + + # Write the mutated command into the data buffer + uc.mem_write(DATA_ADDRESS, input) + + # Start the fuzzer. + uc.afl_fuzz(args.input_file, place_input_callback, [end_address]) + + +if __name__ == "__main__": + main() diff --git a/unicorn_mode/samples/python_simple/simple_test_harness_alt.py b/unicorn_mode/samples/python_simple/simple_test_harness_alt.py new file mode 100644 index 00000000..3249b13d --- /dev/null +++ b/unicorn_mode/samples/python_simple/simple_test_harness_alt.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +""" + Alternative simple test harness for Unicornafl. + It is slower but compatible with anything that uses unicorn. + + Have a look at `unicornafl.monkeypatch()` for an easy way to fuzz unicorn projects. + + 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 /unicorn_mode/samples/simple/ + $ ../../../afl-fuzz -U -m none -i ./sample_inputs -o ./output -- python simple_test_harness_alt.py @@ +""" + +import argparse +import os +import signal + +from unicornafl import * +from unicornafl.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) + + # reserve some space for data + uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX) + + # ----------------------------------------------------- + # 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! + + print("Starting the AFL forkserver") + + afl_mode = uc.afl_forkserver_start([end_address]) + if afl_mode != UC_AFL_RET_NO_AFL: + # Disable prints for speed + out = lambda x, y: None + else: + out = lambda x, y: print(x.format(y)) + + # ----------------------------------------------- + # Load the mutated input and map it into memory + + # Load the mutated input from disk + out("Loading data input from {}", 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: + out("Test input is too long (> {} bytes)", DATA_SIZE_MAX) + return + + # Write the mutated command into the data buffer + uc.mem_write(DATA_ADDRESS, input) + + # ------------------------------------------------------------ + # Emulate the code, allowing it to process the mutated input + + out("Executing until a crash or execution reaches 0x{0:016x}", end_address) + try: + uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=0, count=0) + except UcError as e: + out("Execution failed with error: {}", e) + force_crash(e) + + # UC_AFL_RET_ERROR = 0 + # UC_AFL_RET_CHILD = 1 + # UC_AFL_RET_NO_AFL = 2 + # UC_AFL_RET_FINISHED = 3 + out("Done. AFL Mode is {}", afl_mode) + + +if __name__ == "__main__": + main() diff --git a/unicorn_mode/samples/simple/COMPILE.md b/unicorn_mode/samples/simple/COMPILE.md deleted file mode 100644 index f7bf5b50..00000000 --- a/unicorn_mode/samples/simple/COMPILE.md +++ /dev/null @@ -1,40 +0,0 @@ -# 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. diff --git a/unicorn_mode/samples/simple/sample_inputs/sample1.bin b/unicorn_mode/samples/simple/sample_inputs/sample1.bin deleted file mode 100644 index 85df5078..00000000 --- a/unicorn_mode/samples/simple/sample_inputs/sample1.bin +++ /dev/null @@ -1 +0,0 @@ -abcd \ No newline at end of file diff --git a/unicorn_mode/samples/simple/sample_inputs/sample2.bin b/unicorn_mode/samples/simple/sample_inputs/sample2.bin deleted file mode 100644 index f76dd238..00000000 Binary files a/unicorn_mode/samples/simple/sample_inputs/sample2.bin and /dev/null differ diff --git a/unicorn_mode/samples/simple/sample_inputs/sample3.bin b/unicorn_mode/samples/simple/sample_inputs/sample3.bin deleted file mode 100644 index 6b2aaa76..00000000 --- a/unicorn_mode/samples/simple/sample_inputs/sample3.bin +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/unicorn_mode/samples/simple/sample_inputs/sample4.bin b/unicorn_mode/samples/simple/sample_inputs/sample4.bin deleted file mode 100644 index 71bd63e6..00000000 --- a/unicorn_mode/samples/simple/sample_inputs/sample4.bin +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/unicorn_mode/samples/simple/sample_inputs/sample5.bin b/unicorn_mode/samples/simple/sample_inputs/sample5.bin deleted file mode 100644 index aed2973e..00000000 --- a/unicorn_mode/samples/simple/sample_inputs/sample5.bin +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/unicorn_mode/samples/simple/simple_target.bin b/unicorn_mode/samples/simple/simple_target.bin deleted file mode 100644 index 0095eb6c..00000000 Binary files a/unicorn_mode/samples/simple/simple_target.bin and /dev/null differ diff --git a/unicorn_mode/samples/simple/simple_target.c b/unicorn_mode/samples/simple/simple_target.c deleted file mode 100644 index 49e1b4d8..00000000 --- a/unicorn_mode/samples/simple/simple_target.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 - */ - -// 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; -} diff --git a/unicorn_mode/samples/simple/simple_test_harness.py b/unicorn_mode/samples/simple/simple_test_harness.py deleted file mode 100644 index cd04ad3a..00000000 --- a/unicorn_mode/samples/simple/simple_test_harness.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -""" - 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, unicornafl will - tell AFL that a crash occurred. - - Run under AFL as follows: - - $ cd /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 unicornafl import * -from unicornafl.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 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( - "-t", - "--trace", - 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.trace: - 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) - - # reserve some space for data - uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX) - - # ----------------------------------------------------- - # Set up a callback to place input data (do little work here, it's called for every single iteration) - # We did not pass in any data and don't use persistent mode, so we can ignore these params. - # Be sure to check out the docstrings for the uc.afl_* functions. - def place_input_callback(uc, input, persistent_round, data): - # Apply constraints to the mutated input - if len(input) > DATA_SIZE_MAX: - # print("Test input is too long (> {} bytes)") - return False - - # Write the mutated command into the data buffer - uc.mem_write(DATA_ADDRESS, input) - - # Start the fuzzer. - uc.afl_fuzz(args.input_file, place_input_callback, [end_address]) - - -if __name__ == "__main__": - main() diff --git a/unicorn_mode/samples/simple/simple_test_harness_alt.py b/unicorn_mode/samples/simple/simple_test_harness_alt.py deleted file mode 100644 index 3249b13d..00000000 --- a/unicorn_mode/samples/simple/simple_test_harness_alt.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python -""" - Alternative simple test harness for Unicornafl. - It is slower but compatible with anything that uses unicorn. - - Have a look at `unicornafl.monkeypatch()` for an easy way to fuzz unicorn projects. - - 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 /unicorn_mode/samples/simple/ - $ ../../../afl-fuzz -U -m none -i ./sample_inputs -o ./output -- python simple_test_harness_alt.py @@ -""" - -import argparse -import os -import signal - -from unicornafl import * -from unicornafl.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) - - # reserve some space for data - uc.mem_map(DATA_ADDRESS, DATA_SIZE_MAX) - - # ----------------------------------------------------- - # 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! - - print("Starting the AFL forkserver") - - afl_mode = uc.afl_forkserver_start([end_address]) - if afl_mode != UC_AFL_RET_NO_AFL: - # Disable prints for speed - out = lambda x, y: None - else: - out = lambda x, y: print(x.format(y)) - - # ----------------------------------------------- - # Load the mutated input and map it into memory - - # Load the mutated input from disk - out("Loading data input from {}", 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: - out("Test input is too long (> {} bytes)", DATA_SIZE_MAX) - return - - # Write the mutated command into the data buffer - uc.mem_write(DATA_ADDRESS, input) - - # ------------------------------------------------------------ - # Emulate the code, allowing it to process the mutated input - - out("Executing until a crash or execution reaches 0x{0:016x}", end_address) - try: - uc.emu_start(uc.reg_read(UC_MIPS_REG_PC), end_address, timeout=0, count=0) - except UcError as e: - out("Execution failed with error: {}", e) - force_crash(e) - - # UC_AFL_RET_ERROR = 0 - # UC_AFL_RET_CHILD = 1 - # UC_AFL_RET_NO_AFL = 2 - # UC_AFL_RET_FINISHED = 3 - out("Done. AFL Mode is {}", afl_mode) - - -if __name__ == "__main__": - main() diff --git a/unicorn_mode/samples/speedtest/c/Makefile b/unicorn_mode/samples/speedtest/c/Makefile index 46789954..d34f49fa 100644 --- a/unicorn_mode/samples/speedtest/c/Makefile +++ b/unicorn_mode/samples/speedtest/c/Makefile @@ -4,29 +4,28 @@ .POSIX: UNAME_S =$(shell uname -s)# GNU make UNAME_S:sh=uname -s # BSD make -_UNIQ=_QINU_ -LIBDIR = ../../../unicornafl +UNICORNAFL_LIB = ../../../unicornafl/build +UNICORN_LIB = ../../../unicornafl/unicorn/build BIN_EXT = AR_EXT = a # Verbose output? V ?= 0 -CFLAGS += -Wall -Werror -Wextra -Wno-unused-parameter -I../../../unicornafl/include +CFLAGS += -Wall -I../../../unicornafl/unicorn/include -I../../../unicornafl/include -LDFLAGS += -L$(LIBDIR) -lpthread -lm +LDFLAGS += -L$(UNICORNAFL_LIB) -L$(UNICORN_LIB) -lpthread -lm -lunicornafl -lunicorn -lc++ -_LRT = $(_UNIQ)$(UNAME_S:Linux=) -__LRT = $(_LRT:$(_UNIQ)=-lrt) -LRT = $(__LRT:$(_UNIQ)=) +ifeq ($(UNAME), Linux) +# do something Linux-y +LRT = -lrt +else +LRT = +endif LDFLAGS += $(LRT) -_CC = $(_UNIQ)$(CROSS) -__CC = $(_CC:$(_UNIQ)=$(CC)) -MYCC = $(__CC:$(_UNIQ)$(CROSS)=$(CROSS)gcc) - .PHONY: all clean all: ../target harness @@ -38,17 +37,20 @@ afl-fuzz: ../../../../afl-fuzz clean: rm -rf *.o harness harness-debug -harness.o: harness.c ../../../unicornafl/include/unicorn/*.h - ${MYCC} ${CFLAGS} -O3 -c harness.c -o $@ +../../../unicornafl/build/libunicornafl.a: + cd ../../.. && ./build_unicorn_support.sh + +harness.o: harness.c ../../../unicornafl/unicorn/include/unicorn/*.h + ${CC} ${CFLAGS} -O3 -c harness.c -o $@ -harness-debug.o: harness.c ../../../unicornafl/include/unicorn/*.h - ${MYCC} ${CFLAGS} -fsanitize=address -g -Og -c harness.c -o $@ +harness-debug.o: harness.c ../../../unicornafl/unicorn/include/unicorn/*.h + ${CC} ${CFLAGS} -fsanitize=address -g -Og -c harness.c -o $@ harness: harness.o - ${MYCC} -L${LIBDIR} harness.o ../../../unicornafl/libunicornafl.a $(LDFLAGS) -o $@ + ${CC} harness.o $(LDFLAGS) -o $@ harness-debug: harness-debug.o - ${MYCC} -fsanitize=address -g -Og -L${LIBDIR} harness-debug.o ../../../unicornafl/libunicornafl.a $(LDFLAGS) -o harness-debug + ${CC} -fsanitize=address -g -Og harness-debug.o ../../../unicornafl/libunicornafl.a $(LDFLAGS) -o harness-debug ../target: $(MAKE) -C .. diff --git a/unicorn_mode/samples/speedtest/c/harness.c b/unicorn_mode/samples/speedtest/c/harness.c index 9eb05257..184934b9 100644 --- a/unicorn_mode/samples/speedtest/c/harness.c +++ b/unicorn_mode/samples/speedtest/c/harness.c @@ -28,6 +28,7 @@ #include #include +#include // Path to the file containing the binary to emulate #define BINARY_FILE ("../target") @@ -124,7 +125,7 @@ static void mem_map_checked(uc_engine *uc, uint64_t addr, size_t size, uint32_t //printf("SIZE %llx, align: %llx\n", size, ALIGNMENT); uc_err err = uc_mem_map(uc, addr, size, mode); if (err != UC_ERR_OK) { - printf("Error mapping %ld bytes at 0x%lx: %s (mode: %d)\n", size, addr, uc_strerror(err), mode); + printf("Error mapping %ld bytes at 0x%llx: %s (mode: %d)\n", (unsigned long) size, (unsigned long long) addr, uc_strerror(err), (int) mode); exit(1); } } @@ -306,7 +307,7 @@ int main(int argc, char **argv, char **envp) { exit(-1); } uint64_t start_address; - if(fscanf(f, "%lx", &start_address) == EOF) { + if(fscanf(f, "%llx", (unsigned long long) &start_address) == EOF) { puts("Start address not found in target.offests.main"); exit(-1); } -- cgit 1.4.1