diff --git a/CMakeLists.txt b/CMakeLists.txt index e573c15..f891f50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.16) option(BDD_INCLUDE_TOOL "Include the disasmtool executable" ON) option(BDD_INCLUDE_ISAGENERATOR "Include the isagenerator target (if a python interpreter is found)" ON) +option(BDD_INCLUDE_FUZZERS "Include the bdshemu fuzzer" OFF) option(BDD_USE_EXTERNAL_VSNPRINTF "Expect nd_vsnprintf_s implementation from the integrator" OFF) option(BDD_USE_EXTERNAL_MEMSET "Expect nd_memset implementation from the integrator" OFF) @@ -54,9 +55,7 @@ else () -Wno-unused-function -Wno-multichar -Wno-incompatible-pointer-types - -Wno-discarded-qualifiers -Wnull-dereference - -Wduplicated-cond -Werror=format-security -Werror=implicit-function-declaration -pipe @@ -70,6 +69,12 @@ else () -gdwarf-4 -grecord-gcc-switches -march=westmere) + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + list(APPEND BDDISASM_COMMON_COMPILE_OPTIONS + -Wduplicated-cond + -Wno-discarded-qualifiers) + endif () endif () set(BDDISASM_PUBLIC_HEADERS @@ -200,6 +205,11 @@ if ((${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) AND BDD_INCLUDE_ISAGENERATO add_subdirectory(isagenerator) endif () +# If this is the master project (and if the user requested it) add the fuzzer. +if ((${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) AND BDD_INCLUDE_FUZZERS) + add_subdirectory(bdshemu_fuzz) +endif () + # If this is the master project add install and package targets. if (${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) set(BDDISASM_INSTALL_CMAKEDIR diff --git a/bdshemu_fuzz/CMakeLists.txt b/bdshemu_fuzz/CMakeLists.txt new file mode 100644 index 0000000..46a4885 --- /dev/null +++ b/bdshemu_fuzz/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) + +option(BDD_FUZZ_WITH_LOGS "Enable logging for the fuzzer" OFF) + +project(bdshemu_fuzzer LANGUAGES C) + +add_executable(shfuzzx86 bdshemu_fuzzer.c) +target_link_libraries(shfuzzx86 PRIVATE bddisasm::bdshemu) +target_compile_definitions(shfuzzx86 PRIVATE FUZZ_X86) + +add_executable(shfuzzx64 bdshemu_fuzzer.c) +target_link_libraries(shfuzzx64 PRIVATE bddisasm::bdshemu) +target_compile_definitions(shfuzzx64 PRIVATE FUZZ_X64) + +if (BDD_FUZZ_WITH_LOGS) + target_compile_definitions(shfuzzx86 PRIVATE ENABLE_LOGGING) + target_compile_definitions(shfuzzx64 PRIVATE ENABLE_LOGGING) +endif (BDD_FUZZ_WITH_LOGS) + +add_custom_target(shfuzz DEPENDS shfuzzx86 shfuzzx64) diff --git a/bdshemu_fuzz/Makefile b/bdshemu_fuzz/Makefile deleted file mode 100644 index 78f88fc..0000000 --- a/bdshemu_fuzz/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -# Look for afl-fuzz. If it is in path we assume that afl-gcc/afl-clang/afl-clang-fast is present as well. -ifeq (, $(shell which afl-fuzz)) - $(error "It looks like AFL is not in your path. AFL is available on GitHub: https://github.com/google/AFL") -endif - -# Try to figure out which compiler we should use, afl-clang-fast being the preferred choice. -ifeq (, $(AFL_COMPILER)) - # If AFL_COMPILER is not set, check if afl-clang-fast is available. - ifeq (, $(shell which afl-clang-fast)) - # If it is not, try to figure out if we should use afl-gcc or afl-clang. - ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" - AFL_COMPILER = afl-gcc - else - AFL_COMPILER = afl-clang - endif - else - # If it is available, use it. - AFL_COMPILER = afl-clang-fast - endif -endif - -ROOT_DIR := $(realpath ..) -DISASM_SRC := $(ROOT_DIR)/bddisasm -DISASM_INC := $(DISASM_SRC)/include -SHEMU_SRC := $(ROOT_DIR)/bdshemu -PUBLIC_INC := $(ROOT_DIR)/inc -SHEMU_INC := $(PUBLIC_INC)/bdshemu -FUZZER_SRC := $(ROOT_DIR)/bdshemu_fuzz - -ALL_SRC := $(shell find $(DISASM_SRC)/*.c -type f) -ALL_SRC += $(shell find $(SHEMU_SRC)/*.c -type f) -ALL_SRC += $(shell find $(FUZZER_SRC)/*.c -type f) - -ALL_INC := $(shell find $(DISASM_INC)/*.h -type f) -ALL_INC += $(shell find $(PUBLIC_INC)/*.h -type f) -ALL_INC += $(shell find $(SHEMU_INC)/*.h -type f) - -# Enable qui ck and dirty mode, if needed. -ifeq ($(DIRTY),y) - AFL_ARGS := -d -endif - -ifdef AFL_MEMORY - AFL_ARGS := -m $(AFL_MEMORY) -endif - -CFLAGS := -std=c11 -fpic -fno-strict-aliasing -maes \ - -D_REENTRANT -fstack-protector -ffunction-sections -fdata-sections \ - -DNDEBUG -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Ofast -g3 - -ifeq ($(LOG),y) - PREDEFS := -DENABLE_LOGGING -endif - -# Build shfuzz with AFL instrumentation. -shfuzz: $(ALL_SRC) $(ALL_INC) - @echo "Using $(AFL_COMPILER) as a compiler" - @$(AFL_COMPILER) $(PREDEFS) $(CFLAGS) -I$(DISASM_INC) -I$(PUBLIC_INC) -I$(SHEMU_INC) $(ALL_SRC) -o shfuzz - @echo "Done!" - -# Run the tests in 32-bit mode. -.PHONY: fuzz32 -fuzz32: shfuzz - @afl-fuzz -i ./in-32 -o ./out-32 $(AFL_ARGS) ./shfuzz @@ 32 - -# Run the tests in 64-bit mode. -.PHONY: fuzz64 -fuzz64: shfuzz - @afl-fuzz -i ./in-64 -o ./out-64 $(AFL_ARGS) ./shfuzz @@ 64 - -.PHONY: clean -clean: - @rm -f ./shfuzz diff --git a/bdshemu_fuzz/bdshemu_fuzzer.c b/bdshemu_fuzz/bdshemu_fuzzer.c index 93f5351..a29fde9 100644 --- a/bdshemu_fuzz/bdshemu_fuzzer.c +++ b/bdshemu_fuzz/bdshemu_fuzzer.c @@ -2,82 +2,43 @@ * Copyright (c) 2020 Bitdefender * SPDX-License-Identifier: Apache-2.0 */ +#ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS +#endif +#include #include +#include #include +#include #include #include -#include -#include #include "bddisasm.h" #include "bdshemu.h" +#pragma clang optimize off +#pragma GCC optimize("O0") + #ifdef ENABLE_LOGGING #define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define LOG(fmt, ...) #endif // ENABLE_LOGGING -#ifndef __AFL_LOOP -#warning "Persistent mode will not work (you are probably not using afl-clang-fast)" -#endif - -int nd_vsnprintf_s( - char *buffer, - size_t sizeOfBuffer, - size_t count, - const char *format, - va_list argptr -) -{ - return vsnprintf(buffer, sizeOfBuffer, format, argptr); -} - -void *nd_memset(void *s, int c, size_t n) -{ - return memset(s, c, n); -} - -uint8_t *read_file(const char *fpath, size_t *size) -{ - uint8_t *buffer; - FILE *fd = fopen(fpath, "rb"); - if (fd == NULL) - { - LOG("[-] Failed to open \"%s\"\n", fpath); - return NULL; - } - - fseek(fd, 0ull, SEEK_END); - *size = ftell(fd); - rewind(fd); - - buffer = malloc(*size); - if (buffer == NULL) - { - LOG("[-] Failed to allocate %zu bytes\n", *size); - } - else - { - fread(buffer, *size, 1, fd); - } - - fclose(fd); - return buffer; -} - -static bool gUseLog; - void ShemuLog(char *data) { - if (gUseLog) - { - LOG("%s", data); - } + LOG("%s", data); } -void run_shemu(uint8_t *Data, size_t Size, uint8_t Def) +#ifdef FUZZ_X86 +#define DEF_CODE ND_CODE_32 +#define FUZZER_TYPE "x86" +#else +#define DEF_CODE ND_CODE_64 +#define FUZZER_TYPE "x64" +#endif // FUZZ_X86 + +void run_shemu(uint8_t *Data, size_t Size) { // More or less copy pasted from disasmtool @@ -119,7 +80,7 @@ void run_shemu(uint8_t *Data, size_t Size, uint8_t Def) ctx.Segments.Gs.Selector = 0x30; ctx.Segments.Gs.Base = 0x7FFF0000; - ctx.Mode = Def; + ctx.Mode = DEF_CODE; ctx.Ring = 3; ctx.TibBase = ctx.Mode == ND_CODE_32 ? ctx.Segments.Fs.Base : ctx.Segments.Gs.Base; ctx.MaxInstructionsCount = 4096; @@ -134,85 +95,87 @@ void run_shemu(uint8_t *Data, size_t Size, uint8_t Def) free(ctx.Stack); } -void run_test(uint8_t *Data, size_t Size, uint8_t Def) -{ - run_shemu(Data, Size, Def); -} +#if defined(__AFL_FUZZ_TESTCASE_LEN) +#include + +// See https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md +__AFL_FUZZ_INIT(); -int8_t arch_to_def_code(const char *arch) +int main() { - uint32_t a = strtoul(arch, NULL, 0); + __AFL_INIT(); - switch (a) + uint8_t *buffer = __AFL_FUZZ_TESTCASE_BUF; + while (__AFL_LOOP(UINT_MAX)) { - case 16: - return ND_CODE_16; + size_t size = __AFL_FUZZ_TESTCASE_LEN; + run_shemu(buffer, size); + } - case 32: - return ND_CODE_32; + return 0; +} +#elif defined(__clang__) +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + // Shemu will write into the shellcode buffer, so copy it to a new buffer first. - case 64: - return ND_CODE_64; + uint8_t *buffer = malloc(Size); + if (!buffer) return 1; - default: - LOG("[-] Unknown arch: \"%s\"\n", arch); - return -1; - } -} + memcpy(buffer, Data, Size); + run_shemu(buffer, Size); -int main(int argc, char **argv) + free(buffer); + return 0; +} +#else +uint8_t *read_file(const char *fpath, size_t *size) { - size_t size = 0; uint8_t *buffer; - int8_t defCode; - - if (argc < 3) + FILE *fd = fopen(fpath, "rb"); + if (fd == NULL) { - LOG("[-] Missing arguments: <16|32|64> [activate shemu print]\n"); - abort(); + LOG("[-] Failed to open \"%s\"\n", fpath); + return NULL; } - defCode = arch_to_def_code(argv[2]); - if (defCode == -1) - { - abort(); - } + fseek(fd, 0ull, SEEK_END); + *size = ftell(fd); + rewind(fd); - if (argc >= 4) + buffer = malloc(*size); + if (buffer == NULL) { - LOG("[+] Will activate ShemuLog\n"); - gUseLog = true; + LOG("[-] Failed to allocate %zu bytes\n", *size); } else { - gUseLog = false; + fread(buffer, *size, 1, fd); } - LOG("[+] Using def code/data = %d\n", defCode); + fclose(fd); + return buffer; +} -// __AFL_LOOP is defined only when compiling with afl-clang-fast and allows us to use -// AFL's persistent mode. We still want to be able to compile and run even if we are -// using afl-gcc or afl-clang, but there is no reason to actually have a loop here -// if we are not using persistent mode. -#ifdef __AFL_LOOP - while (__AFL_LOOP(1000)) -#endif // __AFL_LOOP +int main(int argc, char **argv) +{ + if (argc < 2) { - LOG("[+] Reading data from \"%s\"\n", argv[1]); - buffer = read_file(argv[1], &size); - if (buffer == NULL) - { - abort(); - } - - LOG("[+] Read %zu bytes at %p (range: [%p, %p))\n", size, buffer, buffer, buffer + size); - - run_test(buffer, size, (uint8_t)defCode); + LOG("Missing file argument\n"); + abort(); + } - free(buffer); + size_t size; + uint8_t *buffer = read_file(argv[1], &size); + if (!buffer) + { + LOG("[-] read_file failed for \"%s\"\n", argv[1]); + abort(); } - LOG("[+] Done!\n"); + run_shemu(buffer, size); + free(buffer); return 0; } +#endif // defined(__AFL_FUZZ_TESTCASE_LEN)