diff --git a/.travis.yml b/.travis.yml index b8f27d3c..dc0ff94a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,7 @@ language: c sudo: false before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install cmocka; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install cmocka crosstool-ng; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./install-cmocka-linux.sh; fi script: - make && make -C bindings/go && make -C bindings/go test && make test diff --git a/install-cmocka-linux.sh b/install-cmocka-linux.sh index d3eb6959..85b2a12a 100755 --- a/install-cmocka-linux.sh +++ b/install-cmocka-linux.sh @@ -1,13 +1,12 @@ -#!/bin/sh -set -ex +#!/bin/sh -ex mkdir -p cmocka #wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz -O /tmp/cmocka-1.1.0.tar.xz -wget --no-check-certificate http://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz -O /tmp/cmocka-1.1.0.tar.xz -tar -xf /tmp/cmocka-1.1.0.tar.xz -C /tmp -cd cmocka && cmake -DUNIT_TESTING=On /tmp/cmocka-1.1.0 && make && make test +wget --no-check-certificate https://cmocka.org/files/1.1/cmocka-1.1.1.tar.xz -P /tmp/ +tar -xf /tmp/cmocka-1.1.1.tar.xz -C /tmp +cd cmocka && cmake -DUNIT_TESTING=On /tmp/cmocka-1.1.1 && make && make test # cmake builds an so instead of a dll in mingw/msys if [[ ! -z $MSYSTEM ]]; then cp src/cmocka.so src/cmocka.dll fi # cmocka does not include headers in build -cp -R /tmp/cmocka-1.1.0/include/ . +cp -R /tmp/cmocka-1.1.1/include/ . diff --git a/tests/unit/.gitignore b/tests/unit/.gitignore index 5b0bc668..d40da5f7 100644 --- a/tests/unit/.gitignore +++ b/tests/unit/.gitignore @@ -1,2 +1,3 @@ !*.c test_* +*.bin diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 1aa6a9ae..fe33aedd 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -2,10 +2,15 @@ CFLAGS += -Wall -Werror -Wno-unused-function -g CFLAGS += -D__USE_MINGW_ANSI_STDIO=1 CFLAGS += -L ../../ -I ../../include CFLAGS += -L ../../cmocka/src -I ../../cmocka/include +ASFLAGS += --32 +OBJCOPY = objcopy UNAME_S := $(shell uname -s) ifeq ($(UNAME_S), Linux) LDLIBS += -lrt -pthread +else ifeq ($(UNAME_S), Darwin) +OBJCOPY = gobjcopy +ASFLAGS = -arch i386 endif LDLIBS += -lcmocka -lunicorn @@ -20,17 +25,24 @@ LDFLAGS := -fsanitize=address ${LDFLAGS} endif ALL_TESTS_SOURCES = $(wildcard *.c) +TEST_ASSEMBLY = $(wildcard *.s) +TEST_PROGS = $(TEST_ASSEMBLY:%.s=%.o) +TEST_BINS = $(TEST_PROGS:%.o=%.bin) ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%) .PHONY: all -all: ${ALL_TESTS} +all: ${TEST_BINS} ${ALL_TESTS} .PHONY: clean clean: - rm -rf ${ALL_TESTS} + rm -rf ${TEST_BINS} ${ALL_TESTS} + +%.bin: %.o + ${OBJCOPY} -O binary $^ $@ + hexdump -C $@ .PHONY: test -test: ${ALL_TESTS} +test: all ${EXECUTE_VARS} ./test_sanity ${EXECUTE_VARS} ./test_x86 ${EXECUTE_VARS} ./test_mem_map diff --git a/tests/unit/gdt_idx.s b/tests/unit/gdt_idx.s new file mode 100644 index 00000000..42e24622 --- /dev/null +++ b/tests/unit/gdt_idx.s @@ -0,0 +1,3 @@ +.text +sidt (esp) +sgdt (esp+6) diff --git a/tests/unit/high_address.s b/tests/unit/high_address.s new file mode 100644 index 00000000..de921447 --- /dev/null +++ b/tests/unit/high_address.s @@ -0,0 +1,6 @@ +dec %eax +mov (%eax), %eax +nop +nop +nop +nop diff --git a/tests/unit/pc_change.s b/tests/unit/pc_change.s new file mode 100644 index 00000000..415e3cf8 --- /dev/null +++ b/tests/unit/pc_change.s @@ -0,0 +1,9 @@ +.text +inc %ecx +inc %ecx +inc %ecx +inc %ecx +inc %ecx +inc %ecx +inc %edx +inc %edx diff --git a/tests/unit/tb_x86.s b/tests/unit/tb_x86.s new file mode 100644 index 00000000..8ef54c00 --- /dev/null +++ b/tests/unit/tb_x86.s @@ -0,0 +1,90 @@ +mov %esp,%ecx +fxch %st(5) +fnstenv -0xc(%ecx) +pop %ebp +push %ebp +pop %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +dec %ecx +inc %ebx +inc %ebx +inc %ebx +inc %ebx +inc %ebx +inc %ebx +aaa +push %ecx +pop %edx +push $0x41 +pop %eax +push %eax +xor %al,0x30(%ecx) +inc %ecx +imul $0x51,0x41(%ecx),%eax +xor 0x42(%ecx),%al +xor 0x42(%edx),%al +xor %al,0x42(%edx) +inc %ecx +inc %edx +pop %eax +push %eax +cmp %al,0x42(%ecx) +jne .+0x4c +dec %ecx +push %ecx +push %ecx +push %ecx +push %edx +inc %edi +xor 0x34(%edi),%eax +push %ecx +push %ebp +push %ecx +push %esi +push %eax +inc %edi +inc %edi +cmp %al,0x39(%edi) +push %eax +dec %edx +push %eax +dec %ebx +push %eax +dec %esp +push %eax +dec %ebp +push %eax +dec %esi +push %eax +dec %edi +push %eax +push %eax +push %eax +xor %eax, 0x42(%edi) +inc %edi +inc %edx +push %eax +xor $0x50,%al +pop %edx +push %eax +inc %ebp +push %ecx +push %edx +inc %esi +xor 0x31(%edi),%al +push %eax +dec %ebp +push %ecx +push %ecx +push %eax +dec %esi +inc %ecx +inc %ecx diff --git a/tests/unit/test_gdt_idt_x86.c b/tests/unit/test_gdt_idt_x86.c index b95a4e4f..fe18e0d1 100644 --- a/tests/unit/test_gdt_idt_x86.c +++ b/tests/unit/test_gdt_idt_x86.c @@ -1,45 +1,9 @@ +#include "unicorn_test.h" #include #include #include #include -/** - * Assert that err matches expect - */ -#define uc_assert_err(expect, err) \ -do { \ - uc_err __err = err; \ - if (__err != expect) { \ - fprintf(stderr, "%s", uc_strerror(__err)); \ - exit(1); \ - } \ -} while (0) - -/** - * Assert that err is UC_ERR_OK - */ -#define uc_assert_success(err) uc_assert_err(UC_ERR_OK, err) - -/** - * Assert that err is anything but UC_ERR_OK - * - * Note: Better to use uc_assert_err(, err), - * as this serves to document which errors a function will return - * in various scenarios. - */ -#define uc_assert_fail(err) \ -do { \ - uc_err __err = err; \ - if (__err == UC_ERR_OK) { \ - fprintf(stderr, "%s", uc_strerror(__err)); \ - exit(1); \ - } \ -} while (0) - -#define OK(x) uc_assert_success(x) - -/******************************************************************************/ - static void test_idt_gdt_i386(/*void **state*/) { uc_engine *uc; @@ -50,7 +14,9 @@ static void test_idt_gdt_i386(/*void **state*/) uc_x86_mmr ldt; uc_x86_mmr tr; - const uint8_t code[] = "\x0f\x01\x0c\x24\x0f\x01\x44\x24\x06"; // sidt [esp]; sgdt [esp+6] + struct stat info; + char * code = read_file("gdt_idx.bin", &info); + const uint64_t address = 0x1000000; int r_esp = address + 0x1000 - 0x100; // initial esp @@ -79,7 +45,7 @@ static void test_idt_gdt_i386(/*void **state*/) uc_assert_success(err); // write machine code to be emulated to memory - err = uc_mem_write(uc, address, code, sizeof(code)-1); + err = uc_mem_write(uc, address, code, info.st_size); uc_assert_success(err); // initialize machine registers @@ -141,7 +107,7 @@ static void test_idt_gdt_i386(/*void **state*/) assert(memcmp(buf, "\xba\xdc\x21\x43\x65\x87", 6) == 0); uc_close(uc); - + free(code); } /******************************************************************************/ diff --git a/tests/unit/test_mem_high.c b/tests/unit/test_mem_high.c index c57f6121..7e410497 100644 --- a/tests/unit/test_mem_high.c +++ b/tests/unit/test_mem_high.c @@ -90,6 +90,8 @@ static void test_high_address_reads(void **state) static void test_high_address_read_values(void **state) { uc_engine *uc = *state; + struct stat info; + char * code = read_file("high_address.bin", &info); uint64_t addr = 0x0010000000000001; //addr = 0x000ffffffffffff8; // uncomment to fix wrong behaviour @@ -100,15 +102,16 @@ static void test_high_address_read_values(void **state) uc_assert_success(uc_mem_write(uc, addr, content, 8)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr)); const uint64_t base_addr = 0x40000; - uint8_t code[] = {0x48,0x8b,0x00,0x90,0x90,0x90,0x90}; // mov rax, [rax], nops uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL)); - uc_assert_success(uc_mem_write(uc, base_addr, code, 7)); + uc_assert_success(uc_mem_write(uc, base_addr, code, info.st_size)); uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0)); uint64_t rax = 0; uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); if(rax != 0x4242424242424242) { fail_msg("wrong memory read from code %"PRIx64, rax); } + + free(code); } diff --git a/tests/unit/test_pc_change.c b/tests/unit/test_pc_change.c index 85656319..6c844aa4 100644 --- a/tests/unit/test_pc_change.c +++ b/tests/unit/test_pc_change.c @@ -1,6 +1,7 @@ // Test PC change during the callback. by Nguyen Anh Quynh, 2016 #include "unicorn_test.h" #include "unicorn/unicorn.h" +#include "sys/stat.h" #define OK(x) uc_assert_success(x) @@ -54,21 +55,12 @@ static void test_pc_change(void **state) uc_engine *uc = *state; uc_hook trace1; int32_t r_ecx = 3, r_edx = 15; + struct stat info; + char *code = read_file("pc_change.bin", &info); #define BASEADDR 0x1000000 uint64_t address = BASEADDR; - const uint8_t code[] = { - 0x41, // inc ECX @0x1000000 - 0x41, // inc ECX - 0x41, // inc ECX - 0x41, // inc ECX @0x1000003 - 0x41, // inc ECX - 0x41, // inc ECX - - 0x42, // inc EDX @0x1000006 - 0x42, // inc EDX - }; #undef BASEADDR @@ -76,7 +68,7 @@ static void test_pc_change(void **state) OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL)); // write machine code to be emulated to memory - OK(uc_mem_write(uc, address, code, sizeof(code))); + OK(uc_mem_write(uc, address, code, info.st_size)); uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx); uc_reg_write(uc, UC_X86_REG_EDX, &r_edx); @@ -85,7 +77,7 @@ static void test_pc_change(void **state) // trace all instructions OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0)); - OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0)); + OK(uc_emu_start(uc, address, address+info.st_size, 0, 0)); uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx); uc_reg_read(uc, UC_X86_REG_EDX, &r_edx); @@ -93,6 +85,7 @@ static void test_pc_change(void **state) printf("ECX = %u, EDX = %u\n", r_ecx, r_edx); assert_int_equal(r_ecx, 6); assert_int_equal(r_edx, 17); + free(code); } int main(void) diff --git a/tests/unit/test_tb_x86.c b/tests/unit/test_tb_x86.c index 5bf0173b..04d07636 100644 --- a/tests/unit/test_tb_x86.c +++ b/tests/unit/test_tb_x86.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "unicorn/unicorn.h" #define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1) @@ -21,17 +22,6 @@ #define CODE_SPACE (2 * 1024 * 1024) #define PHY_STACK_REGION (0x60000000) -#define X86_CODE32_ALPHA_MIXED \ - "\x89\xe1\xd9\xcd\xd9\x71\xf4\x5d\x55\x59\x49\x49\x49\x49\x49\x49" \ - "\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58" \ - "\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" \ - "\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x51\x51\x51\x52" \ - "\x47\x33\x47\x34\x51\x55\x51\x56\x50\x47\x47\x38\x47\x39\x50\x4a" \ - "\x50\x4b\x50\x4c\x50\x4d\x50\x4e\x50\x4f\x50\x50\x50\x31\x47\x42" \ - "\x47\x42\x50\x34\x50\x5a\x50\x45\x51\x52\x46\x32\x47\x31\x50\x4d" \ - "\x51\x51\x50\x4e\x41\x41" - - /* Called before every test to set up a new instance */ static int setup(void **state) { @@ -57,12 +47,12 @@ static int teardown(void **state) -static void dump_stack_mem(uc_engine *uc) +static void dump_stack_mem(uc_engine *uc, const struct stat info) { uint8_t tmp[256]; uint32_t size; - size = sizeof(X86_CODE32_ALPHA_MIXED); + size = sizeof(info.st_size); if (size > 255) size = 255; if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size)) { @@ -106,7 +96,8 @@ static void print_registers(uc_engine *uc) static void hook_code32(uc_engine *uc, uint64_t address, uint32_t size, - void *user_data) + void *user_data, + const struct stat info) { //uint8_t opcode[256]; uint8_t tmp[16]; @@ -126,7 +117,7 @@ static void hook_code32(uc_engine *uc, } printf("\n"); } - dump_stack_mem(uc); + dump_stack_mem(uc, info); if (address == 0x60000025) @@ -222,6 +213,8 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state) { uc_engine *uc = *state; uc_hook trace1, trace2; + struct stat info; + char * code = read_file("tb_x86.bin", &info); //void *mem; #ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE // These values assumes just before PC = 0x60000021 @@ -258,8 +251,8 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state) UC_PROT_ALL)); uc_assert_success(uc_mem_write(uc, PHY_STACK_REGION, - X86_CODE32_ALPHA_MIXED, - sizeof(X86_CODE32_ALPHA_MIXED) - 1)); + code, + info.st_size)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx)); uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx)); @@ -275,7 +268,8 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state) hook_code32, NULL, 1, - 0)); + 0, + info)); uc_assert_success(uc_hook_add(uc, &trace2, @@ -294,7 +288,7 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state) #else PHY_STACK_REGION+0x0000, #endif - PHY_STACK_REGION+sizeof(X86_CODE32_ALPHA_MIXED) - 1, + PHY_STACK_REGION+info.st_size, 0, 0)); uc_assert_success(uc_close(uc)); diff --git a/tests/unit/unicorn_test.h b/tests/unit/unicorn_test.h index 9342566d..8899bc9f 100644 --- a/tests/unit/unicorn_test.h +++ b/tests/unit/unicorn_test.h @@ -6,6 +6,7 @@ #include #include #include +#include /** * Assert that err matches expect @@ -38,5 +39,12 @@ do { \ } \ } while (0) +char * read_file(const char *filename, struct stat *info) { + stat(filename, info); + char *code = malloc(info->st_size); + FILE *fp = fopen(filename, "r"); + fread(code, info->st_size, 1, fp); + return code; +} #endif /* UNICORN_TEST_H */ diff --git a/tests/unit/x86_soft_paging_low.s b/tests/unit/x86_soft_paging_low.s new file mode 100644 index 00000000..7b25745d --- /dev/null +++ b/tests/unit/x86_soft_paging_low.s @@ -0,0 +1,49 @@ +// Zero memory for page directories and page tables +mov $0x1000,%edi +mov $0x1000,%ecx +xor %eax,%eax +rep stos %eax,(%edi) + +// Load DWORD [0x4000] with 0xDEADBEEF to retrieve later +mov $0x4000,%edi +mov $0xBEEF,%eax +mov %eax, (%edi) + +// Identify map the first 4MiB of memory +mov $0x400,%ecx +mov $0x2000,%edi +mov $3, %eax +loop: +stos %eax,(%edi) +add $0x1000,%eax +loop loop + +// Map phyiscal address 0x4000 to cirtual address 0x7FF000 +mov $0x3ffc,%edi +mov $0x4003,%eax +mov %eax, (%edi) + +// Add page tables into page directory +mov $0x1000, %edi +mov $0x2003, %eax +mov %eax, (%edi) +mov $0x1004, %edi +mov $0x3003, %eax +mov %eax, (%edi) + +// Load the page directory register +mov $0x1000, %eax +mov %eax, %cr3 + +// Enable paging +mov %cr0, %eax +or $0x80000000, %eax + +// Clear EAX +mov %eax, %cr0 + +//Load using virtual memory address; EAX = 0xBEEF +xor %eax,%eax +mov $0x7FF000, %esi +mov (%esi), %eax +hlt