From dbdc2b371571f46a8983960d1c1e6222f7f55223 Mon Sep 17 00:00:00 2001 From: Egbert Date: Sat, 3 Oct 2015 22:46:25 -0700 Subject: [PATCH] Issue #465 hook_count_cb doesn't stop at n instructions; unit test file --- tests/unit/Makefile | 5 +- tests/unit/test_hookcounts.c | 197 +++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_hookcounts.c diff --git a/tests/unit/Makefile b/tests/unit/Makefile index d08671d1..b99570da 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -5,7 +5,8 @@ CFLAGS += -lcmocka -lunicorn CFLAGS += -I ../../include ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \ - test_tb_x86 test_multihook test_pc_change test_x86_soft_paging + test_tb_x86 test_multihook test_pc_change test_x86_soft_paging \ + test_hookcounts .PHONY: all all: ${ALL_TESTS} @@ -26,6 +27,7 @@ test: ${ALL_TESTS} ./test_multihook ./test_pc_change ./test_x86_soft_paging + ./test_hookcounts test_sanity: test_sanity.c test_x86: test_x86.c @@ -36,6 +38,7 @@ test_tb_x86: test_tb_x86.c test_multihook: test_multihook.c test_pc_change: test_pc_change.c test_x86_soft_paging: test_x86_soft_paging.c +test_hookcounts: test_hookcounts.c ${ALL_TESTS}: ${CC} ${CFLAGS} -o $@ $^ diff --git a/tests/unit/test_hookcounts.c b/tests/unit/test_hookcounts.c new file mode 100644 index 00000000..c0a5ca4a --- /dev/null +++ b/tests/unit/test_hookcounts.c @@ -0,0 +1,197 @@ +// Test hook evocation count +// +// Objective is to demonstrate finer duration control of +// emulation by counts of instruction code +// +#include "unicorn_test.h" +#include + +#define OK(x) uc_assert_success(x) + +volatile int expected_instructions = 0; +volatile int total_instructions = 0; + + +// NOTE: It would appear that this UC_HOOK_CODE is being done before the +// uc_count_fb hook. +// So, termination by uc->emu_count has not been done yet here... +static void test_code_hook(uc_engine *uc, + uint64_t address, + uint32_t size, + void *user_data) +{ + + ++total_instructions; + if (total_instructions > expected_instructions) + { + uc_emu_stop(uc); + } + +#ifdef DEBUG + printf("instruction at 0x%"PRIx64": ", address); + if (!uc_mem_read(uc, address, tmp, size)) { + uint8_t tmp[256]; + uint32_t i; + + for (i = 0; i < size; i++) { + printf("0x%x ", tmp[i]); + } + printf("\n"); + } +#endif // DEBUG +} + + +/* Called before every test to set up a new instance */ +static int setup32(void **state) +{ + uc_hook trace1; + uc_engine *uc; + + OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); + + *state = uc; + + // trace all instructions + OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0)); + + return 0; +} + +/* Called after every test to clean up */ +static int teardown(void **state) +{ + uc_engine *uc = *state; + + OK(uc_close(uc)); + + *state = NULL; + return 0; +} + +/******************************************************************************/ + +static void +test_hook_count(uc_engine *uc, + const uint8_t *code, + int start_offset, + int expected_instructions) +{ + +#define BASEADDR 0x1000000 +#define MEMSIZE (2 * 1024 * 1024) + + uint64_t address = BASEADDR + (expected_instructions * MEMSIZE); + total_instructions = 0; + +#undef BASEADDR + + // map a new 2MB memory for this emulation + OK(uc_mem_map(uc, address, MEMSIZE, UC_PROT_ALL)); + + // write machine code to be emulated to memory + OK(uc_mem_write(uc, address, code, expected_instructions)); + + OK(uc_emu_start(uc, + address, + address+start_offset, + 0, + expected_instructions)); + + assert_int_equal(expected_instructions, total_instructions); + + // map 2MB memory for this emulation + OK(uc_mem_unmap(uc, address, MEMSIZE)); +} + + +/* Perform fine-grain emulation control of exactly 1 instruction */ +static void test_hook_count_1(void **state) +{ + uc_engine *uc = *state; + 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 + }; + test_hook_count(uc, code, 0, 1); +} + + +/* Perform fine-grain emulation control over a range of */ +/* varied instruction steps. */ +static void test_hook_count_range(void **state) +{ + int i; + uc_engine *uc = *state; + 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 + }; + for (i = 2; i < 7; i++) + { + test_hook_count(uc, code, 1, i); + } +} + + +static void test_hook_count_end(void **state) +{ + uc_engine *uc = *state; + 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 + }; + test_hook_count(uc, code, sizeof(code)-1, 1); +} + + +static void test_hook_count_midpoint(void **state) +{ + uc_engine *uc = *state; + 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 + }; + test_hook_count(uc, code, sizeof(code)/2, 2); + test_hook_count(uc, code, 2, sizeof(code)-2); +} + + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_hook_count_1, setup32, teardown), + cmocka_unit_test_setup_teardown(test_hook_count_range, setup32, teardown), + cmocka_unit_test_setup_teardown(test_hook_count_midpoint, setup32, teardown), + cmocka_unit_test_setup_teardown(test_hook_count_end, setup32, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} +