#include "unicorn_test.h" const uint64_t code_start = 0x1000; const uint64_t code_len = 0x4000; #define MEM_BASE 0x40000000 #define MEM_SIZE 1024 * 1024 #define MEM_STACK MEM_BASE + (MEM_SIZE / 2) #define MEM_TEXT MEM_STACK + 4096 static void uc_common_setup(uc_engine **uc, uc_arch arch, uc_mode mode, const char *code, uint64_t size) { OK(uc_open(arch, mode, uc)); OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL)); OK(uc_mem_write(*uc, code_start, code, size)); } typedef struct RegInfo_t { const char *file; int line; const char *name; uc_x86_reg reg; uint64_t value; } RegInfo; typedef struct QuickTest_t { uc_mode mode; uint8_t *code_data; size_t code_size; size_t in_count; RegInfo in_regs[32]; size_t out_count; RegInfo out_regs[32]; } QuickTest; static void QuickTest_run(QuickTest *test) { uc_engine *uc; // initialize emulator in X86-64bit mode OK(uc_open(UC_ARCH_X86, test->mode, &uc)); // map 1MB of memory for this emulation OK(uc_mem_map(uc, MEM_BASE, MEM_SIZE, UC_PROT_ALL)); OK(uc_mem_write(uc, MEM_TEXT, test->code_data, test->code_size)); if (test->mode == UC_MODE_64) { uint64_t stack_top = MEM_STACK; OK(uc_reg_write(uc, UC_X86_REG_RSP, &stack_top)); } else { uint32_t stack_top = MEM_STACK; OK(uc_reg_write(uc, UC_X86_REG_ESP, &stack_top)); } for (size_t i = 0; i < test->in_count; i++) { if (test->mode == UC_MODE_64) { OK(uc_reg_write(uc, test->in_regs[i].reg, &test->in_regs[i].value)); } else { uint32_t reg = test->in_regs[i].value & 0xFFFFFFFF; OK(uc_reg_write(uc, test->in_regs[i].reg, ®)); } } OK(uc_emu_start(uc, MEM_TEXT, MEM_TEXT + test->code_size, 0, 0)); for (size_t i = 0; i < test->out_count; i++) { RegInfo *out = &test->out_regs[i]; if (test->mode == UC_MODE_64) { uint64_t value = 0; OK(uc_reg_read(uc, out->reg, &value)); acutest_check_(value == out->value, out->file, out->line, "OUT_REG(%s, 0x%" PRIx64 ") = 0x%" PRIx64 "", out->name, out->value, value); } else { uint32_t value = 0; OK(uc_reg_read(uc, out->reg, &value)); acutest_check_(value == (uint32_t)out->value, out->file, out->line, "OUT_REG(%s, 0x%X) = 0x%X", out->name, (uint32_t)out->value, value); } } OK(uc_mem_unmap(uc, MEM_BASE, MEM_SIZE)); OK(uc_close(uc)); } #define TEST_CODE(MODE, CODE) \ QuickTest t; \ memset(&t, 0, sizeof(t)); \ t.mode = MODE; \ t.code_data = CODE; \ t.code_size = sizeof(CODE) #define TEST_IN_REG(NAME, VALUE) \ t.in_regs[t.in_count].file = __FILE__; \ t.in_regs[t.in_count].line = __LINE__; \ t.in_regs[t.in_count].name = #NAME; \ t.in_regs[t.in_count].reg = UC_X86_REG_##NAME; \ t.in_regs[t.in_count].value = VALUE; \ t.in_count++ #define TEST_OUT_REG(NAME, VALUE) \ t.out_regs[t.out_count].file = __FILE__; \ t.out_regs[t.out_count].line = __LINE__; \ t.out_regs[t.out_count].name = #NAME; \ t.out_regs[t.out_count].reg = UC_X86_REG_##NAME; \ t.out_regs[t.out_count].value = VALUE; \ t.out_count++ #define TEST_RUN() QuickTest_run(&t) typedef struct _INSN_IN_RESULT { uint32_t port; int size; } INSN_IN_RESULT; static void test_x86_in_callback(uc_engine *uc, uint32_t port, int size, void *user_data) { INSN_IN_RESULT *result = (INSN_IN_RESULT *)user_data; uint32_t eip; result->port = port; result->size = size; OK(uc_reg_read(uc, UC_X86_REG_EIP, (void*)&eip)); TEST_CHECK(eip == code_start); } static void test_x86_in(void) { uc_engine *uc; uc_hook hook; char code[] = "\xe5\x10"; // IN eax, 0x10 INSN_IN_RESULT result; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_in_callback, &result, 1, 0, UC_X86_INS_IN)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(result.port == 0x10); TEST_CHECK(result.size == 4); OK(uc_hook_del(uc, hook)); OK(uc_close(uc)); } typedef struct _INSN_OUT_RESULT { uint32_t port; int size; uint32_t value; } INSN_OUT_RESULT; static void test_x86_out_callback(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data) { INSN_OUT_RESULT *result = (INSN_OUT_RESULT *)user_data; result->port = port; result->size = size; result->value = value; } static void test_x86_out(void) { uc_engine *uc; uc_hook hook; char code[] = "\xb0\x32\xe6\x46"; // MOV al, 0x32; OUT 0x46, al; INSN_OUT_RESULT result; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_out_callback, &result, 1, 0, UC_X86_INS_OUT)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(result.port == 0x46); TEST_CHECK(result.size == 1); TEST_CHECK(result.value == 0x32); OK(uc_hook_del(uc, hook)); OK(uc_close(uc)); } typedef struct _MEM_HOOK_RESULT { uc_mem_type type; uint64_t address; int size; uint64_t value; } MEM_HOOK_RESULT; typedef struct _MEM_HOOK_RESULTS { uint64_t count; MEM_HOOK_RESULT results[16]; } MEM_HOOK_RESULTS; static bool test_x86_mem_hook_all_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, uint64_t value, void *user_data) { MEM_HOOK_RESULTS *r = (MEM_HOOK_RESULTS *)user_data; uint64_t count = r->count; if (count >= 16) { TEST_ASSERT(false); } r->results[count].type = type; r->results[count].address = address; r->results[count].size = size; r->results[count].value = value; r->count++; if (type == UC_MEM_READ_UNMAPPED) { uc_mem_map(uc, address, 0x1000, UC_PROT_ALL); } return true; } static void test_x86_mem_hook_all(void) { uc_engine *uc; uc_hook hook; // mov eax, 0xdeadbeef; // mov [0x8000], eax; // mov eax, [0x10000]; char code[] = "\xb8\xef\xbe\xad\xde\xa3\x00\x80\x00\x00\xa1\x00\x00\x01\x00"; MEM_HOOK_RESULTS r = {0}; MEM_HOOK_RESULT expects[3] = {{UC_MEM_WRITE, 0x8000, 4, 0xdeadbeef}, {UC_MEM_READ_UNMAPPED, 0x10000, 4, 0}, {UC_MEM_READ, 0x10000, 4, 0}}; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_mem_map(uc, 0x8000, 0x1000, UC_PROT_ALL)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_VALID | UC_HOOK_MEM_INVALID, test_x86_mem_hook_all_callback, &r, 1, 0)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(r.count == 3); for (int i = 0; i < r.count; i++) { TEST_CHECK(expects[i].type == r.results[i].type); TEST_CHECK(expects[i].address == r.results[i].address); TEST_CHECK(expects[i].size == r.results[i].size); TEST_CHECK(expects[i].value == r.results[i].value); } OK(uc_hook_del(uc, hook)); OK(uc_close(uc)); } static void test_x86_inc_dec_pxor(void) { uc_engine *uc; char code[] = "\x41\x4a\x66\x0f\xef\xc1"; // INC ecx; DEC edx; PXOR xmm0, xmm1 int r_ecx = 0x1234; int r_edx = 0x7890; uint64_t r_xmm0[2] = {0x08090a0b0c0d0e0f, 0x0001020304050607}; uint64_t r_xmm1[2] = {0x8090a0b0c0d0e0f0, 0x0010203040506070}; OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL)); OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL)); OK(uc_mem_write(uc, code_start, code, sizeof(code) - 1)); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); OK(uc_reg_write(uc, UC_X86_REG_XMM0, &r_xmm0)); OK(uc_reg_write(uc, UC_X86_REG_XMM1, &r_xmm1)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); OK(uc_reg_read(uc, UC_X86_REG_XMM0, &r_xmm0)); TEST_CHECK(r_ecx == 0x1235); TEST_CHECK(r_edx == 0x788f); TEST_CHECK(r_xmm0[0] == 0x8899aabbccddeeff); TEST_CHECK(r_xmm0[1] == 0x0011223344556677); OK(uc_close(uc)); } static void test_x86_relative_jump(void) { uc_engine *uc; char code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop; // nop; nop; nop int r_eip; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_emu_start(uc, code_start, code_start + 4, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EIP, &r_eip)); TEST_CHECK(r_eip == code_start + 4); OK(uc_close(uc)); } static void test_x86_loop(void) { uc_engine *uc; char code[] = "\x41\x4a\xeb\xfe"; // inc ecx; dec edx; jmp $; int r_ecx = 0x1234; int r_edx = 0x7890; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 1 * 1000000, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_ecx == 0x1235); TEST_CHECK(r_edx == 0x788f); OK(uc_close(uc)); } static void test_x86_invalid_mem_read(void) { uc_engine *uc; char code[] = "\x8b\x0d\xaa\xaa\xaa\xaa"; // mov ecx, [0xAAAAAAAA] uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); uc_assert_err( UC_ERR_READ_UNMAPPED, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } static void test_x86_invalid_mem_write(void) { uc_engine *uc; char code[] = "\x89\x0d\xaa\xaa\xaa\xaa"; // mov ecx, [0xAAAAAAAA] uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); uc_assert_err( UC_ERR_WRITE_UNMAPPED, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } static void test_x86_invalid_jump(void) { uc_engine *uc; char code[] = "\xe9\xe9\xee\xee\xee"; // jmp 0xEEEEEEEE uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); uc_assert_err( UC_ERR_FETCH_UNMAPPED, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } static void test_x86_64_syscall_callback(uc_engine *uc, void *user_data) { uint64_t rax; OK(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); TEST_CHECK(rax == 0x100); } static void test_x86_64_syscall(void) { uc_engine *uc; uc_hook hook; char code[] = "\x0f\x05"; // syscall uint64_t r_rax = 0x100; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_RAX, &r_rax)); OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_64_syscall_callback, NULL, 1, 0, UC_X86_INS_SYSCALL)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_hook_del(uc, hook)); OK(uc_close(uc)); } static void test_x86_16_add(void) { uc_engine *uc; char code[] = "\x00\x00"; // add byte ptr [bx + si], al uint16_t r_ax = 7; uint16_t r_bx = 5; uint16_t r_si = 6; uint8_t result; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_16, code, sizeof(code) - 1); OK(uc_mem_map(uc, 0, 0x1000, UC_PROT_ALL)); OK(uc_reg_write(uc, UC_X86_REG_AX, &r_ax)); OK(uc_reg_write(uc, UC_X86_REG_BX, &r_bx)); OK(uc_reg_write(uc, UC_X86_REG_SI, &r_si)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_mem_read(uc, r_bx + r_si, &result, 1)); TEST_CHECK(result == 7); OK(uc_close(uc)); } static void test_x86_reg_save(void) { uc_engine *uc; uc_context *ctx; char code[] = "\x40"; // inc eax int r_eax = 1; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_eax)); OK(uc_context_alloc(uc, &ctx)); OK(uc_context_save(uc, ctx)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EAX, &r_eax)); TEST_CHECK(r_eax == 2); OK(uc_context_restore(uc, ctx)); OK(uc_reg_read(uc, UC_X86_REG_EAX, &r_eax)); TEST_CHECK(r_eax == 1); OK(uc_context_free(ctx)); OK(uc_close(uc)); } static bool test_x86_invalid_mem_read_stop_in_cb_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, uint64_t value, void *user_data) { // False indicates that we fail to handle this ERROR and let the emulation // stop. // // Note that the memory must be mapped properly if we return true! Check // test_x86_mem_hook_all for example. return false; } static void test_x86_invalid_mem_read_stop_in_cb(void) { uc_engine *uc; uc_hook hook; char code[] = "\x40\x8b\x1d\x00\x00\x10\x00\x42"; // inc eax; mov ebx, // [0x100000]; inc edx int r_eax = 0x1234; int r_edx = 0x5678; int r_eip = 0; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_READ, test_x86_invalid_mem_read_stop_in_cb_callback, NULL, 1, 0)); OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_eax)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); uc_assert_err( UC_ERR_READ_UNMAPPED, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); // The state of Unicorn should be correct at this time. OK(uc_reg_read(uc, UC_X86_REG_EIP, &r_eip)); OK(uc_reg_read(uc, UC_X86_REG_EAX, &r_eax)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_eip == code_start + 1); TEST_CHECK(r_eax == 0x1235); TEST_CHECK(r_edx == 0x5678); OK(uc_close(uc)); } static void test_x86_x87_fnstenv_callback(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { uint32_t r_eip; uint32_t r_eax; uint32_t fnstenv[7]; if (address == code_start + 4) { // The first fnstenv executed // Save the address of the fld. OK(uc_reg_read(uc, UC_X86_REG_EIP, &r_eip)); *((uint32_t *)user_data) = r_eip; OK(uc_reg_read(uc, UC_X86_REG_EAX, &r_eax)); OK(uc_mem_read(uc, r_eax, fnstenv, sizeof(fnstenv))); // Don't update FCS:FIP for fnop. TEST_CHECK(fnstenv[3] == 0); } } static void test_x86_x87_fnstenv(void) { uc_engine *uc; uc_hook hook; char code[] = "\xd9\xd0\xd9\x30\xd9\x00\xd9\x30"; // fnop;fnstenv [eax];fld dword ptr // [eax];fnstenv [eax] uint32_t base = code_start + 3 * code_len; uint32_t last_eip; uint32_t fnstenv[7]; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_mem_map(uc, base, code_len, UC_PROT_ALL)); OK(uc_reg_write(uc, UC_X86_REG_EAX, &base)); OK(uc_hook_add(uc, &hook, UC_HOOK_CODE, test_x86_x87_fnstenv_callback, &last_eip, 1, 0)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_mem_read(uc, base, fnstenv, sizeof(fnstenv))); // But update FCS:FIP for fld. TEST_CHECK(LEINT32(fnstenv[3]) == last_eip); OK(uc_close(uc)); } static uint64_t test_x86_mmio_read_callback(uc_engine *uc, uint64_t offset, unsigned size, void *user_data) { TEST_CHECK(offset == 4); TEST_CHECK(size == 4); return 0x19260817; } static void test_x86_mmio_write_callback(uc_engine *uc, uint64_t offset, unsigned size, uint64_t value, void *user_data) { TEST_CHECK(offset == 4); TEST_CHECK(size == 4); TEST_CHECK(value == 0xdeadbeef); return; } static void test_x86_mmio(void) { uc_engine *uc; int r_ecx = 0xdeadbeef; char code[] = "\x89\x0d\x04\x00\x02\x00\x8b\x0d\x04\x00\x02\x00"; // mov [0x20004], // ecx; mov ecx, // [0x20004] uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_mmio_map(uc, 0x20000, 0x1000, test_x86_mmio_read_callback, NULL, test_x86_mmio_write_callback, NULL)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); TEST_CHECK(r_ecx == 0x19260817); OK(uc_close(uc)); } static bool test_x86_missing_code_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, uint64_t value, void *user_data) { char code[] = "\x41\x4a"; // inc ecx; dec edx; uint64_t algined_address = address & 0xFFFFFFFFFFFFF000ULL; int aligned_size = ((int)(size / 0x1000) + 1) * 0x1000; OK(uc_mem_map(uc, algined_address, aligned_size, UC_PROT_ALL)); OK(uc_mem_write(uc, algined_address, code, sizeof(code) - 1)); return true; } static void test_x86_missing_code(void) { uc_engine *uc; uc_hook hook; int r_ecx = 0x1234; int r_edx = 0x7890; // Don't write any code by design. OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_UNMAPPED, test_x86_missing_code_callback, NULL, 1, 0)); OK(uc_emu_start(uc, code_start, code_start + 2, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_ecx == 0x1235); TEST_CHECK(r_edx == 0x788f); OK(uc_close(uc)); } static void test_x86_smc_xor(void) { uc_engine *uc; /* * 0x1000 xor dword ptr [edi+0x3], eax ; edi=0x1000, eax=0xbc4177e6 * 0x1003 dw 0x3ea98b13 */ char code[] = "\x31\x47\x03\x13\x8b\xa9\x3e"; int r_edi = code_start; int r_eax = 0xbc4177e6; uint32_t result; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); uc_reg_write(uc, UC_X86_REG_EDI, &r_edi); uc_reg_write(uc, UC_X86_REG_EAX, &r_eax); OK(uc_emu_start(uc, code_start, code_start + 3, 0, 0)); OK(uc_mem_read(uc, code_start + 3, (void *)&result, 4)); TEST_CHECK(LEINT32(result) == (0x3ea98b13 ^ 0xbc4177e6)); OK(uc_close(uc)); } static void test_x86_smc_add(void) { uc_engine *uc; uint64_t stack_base = 0x20000; uint64_t r_rsp; /* * mov qword ptr [rip+0x10], rax * mov word ptr [rip], 0x0548 * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 * add rax, 0x12345678 hlt */ char code[] = "\x48\x89\x05\x10\x00\x00\x00\x66\xc7\x05\x00\x00\x00\x00\x48" "\x05\x8b\x80\x78\x56\x34\x12\xf4"; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_mem_map(uc, stack_base, 0x2000, UC_PROT_ALL)); r_rsp = stack_base + 0x1800; OK(uc_reg_write(uc, UC_X86_REG_RSP, &r_rsp)); OK(uc_emu_start(uc, code_start, -1, 0, 0)); OK(uc_close(uc)); } static void test_x86_smc_mem_hook_callback(uc_engine *uc, uc_mem_type t, uint64_t addr, int size, uint64_t value, void *user_data) { uint64_t write_addresses[] = {0x1030, 0x1010, 0x1010, 0x1018, 0x1018, 0x1029, 0x1029}; unsigned int *i = user_data; TEST_CHECK(*i < (sizeof(write_addresses) / sizeof(write_addresses[0]))); TEST_CHECK(write_addresses[*i] == addr); (*i)++; } static void test_x86_smc_mem_hook(void) { uc_engine *uc; uc_hook hook; uint64_t stack_base = 0x20000; uint64_t r_rsp; unsigned int i = 0; /* * mov qword ptr [rip+0x29], rax * mov word ptr [rip], 0x0548 * [orig] mov eax, dword ptr [rax + 0x12345678]; [after SMC] 480578563412 * add rax, 0x12345678 nop nop nop mov qword ptr [rip-0x08], rax mov word * ptr [rip], 0x0548 [orig] mov eax, dword ptr [rax + 0x12345678]; [after * SMC] 480578563412 add rax, 0x12345678 hlt */ char code[] = "\x48\x89\x05\x29\x00\x00\x00\x66\xC7\x05\x00\x00\x00\x00\x48\x05\x8B" "\x80\x78\x56\x34\x12\x90\x90\x90\x48\x89\x05\xF8\xFF\xFF\xFF\x66\xC7" "\x05\x00\x00\x00\x00\x48\x05\x8B\x80\x78\x56\x34\x12\xF4"; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, test_x86_smc_mem_hook_callback, &i, 1, 0)); OK(uc_mem_map(uc, stack_base, 0x2000, UC_PROT_ALL)); r_rsp = stack_base + 0x1800; OK(uc_reg_write(uc, UC_X86_REG_RSP, &r_rsp)); OK(uc_emu_start(uc, code_start, -1, 0, 0)); OK(uc_close(uc)); } static uint64_t test_x86_mmio_uc_mem_rw_read_callback(uc_engine *uc, uint64_t offset, unsigned size, void *user_data) { TEST_CHECK(offset == 8); TEST_CHECK(size == 4); return 0x19260817; } static void test_x86_mmio_uc_mem_rw_write_callback(uc_engine *uc, uint64_t offset, unsigned size, uint64_t value, void *user_data) { TEST_CHECK(offset == 4); TEST_CHECK(size == 4); TEST_CHECK(value == 0xdeadbeef); return; } static void test_x86_mmio_uc_mem_rw(void) { uc_engine *uc; int data = LEINT32(0xdeadbeef); OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_mmio_map(uc, 0x20000, 0x1000, test_x86_mmio_uc_mem_rw_read_callback, NULL, test_x86_mmio_uc_mem_rw_write_callback, NULL)); OK(uc_mem_write(uc, 0x20004, (void *)&data, 4)); OK(uc_mem_read(uc, 0x20008, (void *)&data, 4)); TEST_CHECK(LEINT32(data) == 0x19260817); OK(uc_close(uc)); } static void test_x86_sysenter_hook(uc_engine *uc, void *user) { *(int *)user = 1; } static void test_x86_sysenter(void) { uc_engine *uc; char code[] = "\x0F\x34"; // sysenter uc_hook h; int called = 0; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &h, UC_HOOK_INSN, test_x86_sysenter_hook, &called, 1, 0, UC_X86_INS_SYSENTER)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(called == 1); OK(uc_close(uc)); } static int test_x86_hook_cpuid_callback(uc_engine *uc, void *data) { uint32_t reg = 7; uint32_t eip; OK(uc_reg_read(uc, UC_X86_REG_EIP, (void*)&eip)); OK(uc_reg_write(uc, UC_X86_REG_EAX, ®)); TEST_CHECK(eip == code_start + 1); // Overwrite the cpuid instruction. return 1; } static void test_x86_hook_cpuid(void) { uc_engine *uc; char code[] = "\x40\x0F\xA2"; // INC EAX; CPUID uc_hook h; int reg; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &h, UC_HOOK_INSN, test_x86_hook_cpuid_callback, NULL, 1, 0, UC_X86_INS_CPUID)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EAX, ®)); TEST_CHECK(reg == 7); OK(uc_close(uc)); } static void test_x86_486_cpuid(void) { uc_engine *uc; uint32_t eax; uint32_t ebx; char code[] = {0x31, 0xC0, 0x0F, 0xA2}; // XOR EAX EAX; CPUID OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_486)); OK(uc_mem_map(uc, 0, 4 * 1024, UC_PROT_ALL)); OK(uc_mem_write(uc, 0, code, sizeof(code) / sizeof(code[0]))); OK(uc_emu_start(uc, 0, sizeof(code) / sizeof(code[0]), 0, 0)); /* Read eax after emulation */ OK(uc_reg_read(uc, UC_X86_REG_EAX, &eax)); OK(uc_reg_read(uc, UC_X86_REG_EBX, &ebx)); TEST_CHECK(eax != 0); TEST_CHECK(ebx == 0x756e6547); // magic string "Genu" for intel cpu OK(uc_close(uc)); } // This is a regression bug. static void test_x86_clear_tb_cache(void) { uc_engine *uc; char code[] = "\x83\xc1\x01\x4a"; // ADD ecx, 1; DEC edx; int r_ecx = 0x1234; int r_edx = 0x7890; uint64_t code_start = 0x1240; // Choose this address by design uint64_t code_len = 0x1000; OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_mem_map(uc, code_start & (1 << 12), code_len, UC_PROT_ALL)); OK(uc_mem_write(uc, code_start, code, sizeof(code))); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); // This emulation should take no effect at all. OK(uc_emu_start(uc, code_start, code_start, 0, 0)); // Emulate ADD ecx, 1. OK(uc_emu_start(uc, code_start, code_start + 3, 0, 0)); // If tb cache is not cleared, edx would be still 0x7890 OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_ecx == 0x1236); TEST_CHECK(r_edx == 0x788f); OK(uc_close(uc)); } static void test_x86_clear_count_cache(void) { uc_engine *uc; // uc_emu_start will clear last TB when exiting so generating a tb at last // by design char code[] = "\x83\xc1\x01\x4a\xeb\x00\x83\xc3\x01"; // ADD ecx, 1; DEC edx; // jmp t; // t: // ADD ebx, 1 int r_ecx = 0x1234; int r_edx = 0x7890; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 2)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_ecx == 0x1236); TEST_CHECK(r_edx == 0x788e); OK(uc_close(uc)); } // This is a regression bug. static void test_x86_clear_empty_tb(void) { uc_engine *uc; // lb: // add ecx, 1; // cmp ecx, 0; // jz lb; // dec edx; char code[] = "\x83\xc1\x01\x83\xf9\x00\x74\xf8\x4a"; int r_edx = 0x7890; uint64_t code_start = 0x1240; // Choose this address by design uint64_t code_len = 0x1000; OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_mem_map(uc, code_start & (1 << 12), code_len, UC_PROT_ALL)); OK(uc_mem_write(uc, code_start, code, sizeof(code))); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); // Make sure we generate an empty tb at the exit address by stopping at dec // edx. OK(uc_emu_start(uc, code_start, code_start + 8, 0, 0)); // If tb cache is not cleared, edx would be still 0x7890 OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_edx == 0x788f); OK(uc_close(uc)); } typedef struct _HOOK_TCG_OP_RESULT { uint64_t address; uint64_t arg1; uint64_t arg2; } HOOK_TCG_OP_RESULT; typedef struct _HOOK_TCG_OP_RESULTS { HOOK_TCG_OP_RESULT results[128]; uint64_t len; } HOOK_TCG_OP_RESULTS; static void test_x86_hook_tcg_op_cb(uc_engine *uc, uint64_t address, uint64_t arg1, uint64_t arg2, uint32_t size, void *data) { HOOK_TCG_OP_RESULTS *results = (HOOK_TCG_OP_RESULTS *)data; HOOK_TCG_OP_RESULT *result = &results->results[results->len++]; result->address = address; result->arg1 = arg1; result->arg2 = arg2; } static void test_x86_hook_tcg_op(void) { uc_engine *uc; uc_hook h; int flag; HOOK_TCG_OP_RESULTS results; // sub esi, [0x1000]; // sub eax, ebx; // sub eax, 1; // cmp eax, 0; // cmp ebx, edx; // cmp esi, [0x1000]; char code[] = "\x2b\x35\x00\x10\x00\x00\x29\xd8\x83\xe8\x01\x83\xf8\x00\x39" "\xd3\x3b\x35\x00\x10\x00\x00"; int r_eax = 0x1234; int r_ebx = 2; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_eax)); OK(uc_reg_write(uc, UC_X86_REG_EBX, &r_ebx)); memset(&results, 0, sizeof(HOOK_TCG_OP_RESULTS)); flag = 0; OK(uc_hook_add(uc, &h, UC_HOOK_TCG_OPCODE, test_x86_hook_tcg_op_cb, &results, 0, -1, UC_TCG_OP_SUB, flag)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_hook_del(uc, h)); TEST_CHECK(results.len == 6); memset(&results, 0, sizeof(HOOK_TCG_OP_RESULTS)); flag = UC_TCG_OP_FLAG_DIRECT; OK(uc_hook_add(uc, &h, UC_HOOK_TCG_OPCODE, test_x86_hook_tcg_op_cb, &results, 0, -1, UC_TCG_OP_SUB, flag)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_hook_del(uc, h)); TEST_CHECK(results.len == 3); memset(&results, 0, sizeof(HOOK_TCG_OP_RESULTS)); flag = UC_TCG_OP_FLAG_CMP; OK(uc_hook_add(uc, &h, UC_HOOK_TCG_OPCODE, test_x86_hook_tcg_op_cb, &results, 0, -1, UC_TCG_OP_SUB, flag)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_hook_del(uc, h)); TEST_CHECK(results.len == 3); OK(uc_close(uc)); } static bool test_x86_cmpxchg_mem_hook(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t val, void *data) { if (type == UC_MEM_READ) { *((int *)data) |= 1; } else { *((int *)data) |= 2; } return true; } static void test_x86_cmpxchg(void) { uc_engine *uc; char code[] = "\x0F\xC7\x0D\xE0\xBE\xAD\xDE"; // cmpxchg8b [0xdeadbee0] int r_zero = 0; int r_aaaa = 0x41414141; uint64_t mem; uc_hook h; int result = 0; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_mem_map(uc, 0xdeadb000, 0x1000, UC_PROT_ALL)); OK(uc_hook_add(uc, &h, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, test_x86_cmpxchg_mem_hook, &result, 1, 0)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_zero)); OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_zero)); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_aaaa)); OK(uc_reg_write(uc, UC_X86_REG_EBX, &r_aaaa)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_mem_read(uc, 0xdeadbee0, &mem, 8)); TEST_CHECK(mem == 0x4141414141414141); // Both read and write happened. TEST_CHECK(result == 3); OK(uc_close(uc)); } static void test_x86_nested_emu_start_cb(uc_engine *uc, uint64_t addr, size_t size, void *data) { OK(uc_emu_start(uc, code_start + 1, code_start + 2, 0, 0)); } static void test_x86_nested_emu_start(void) { uc_engine *uc; char code[] = "\x41\x4a"; // INC ecx; DEC edx; int r_ecx = 0x1234; int r_edx = 0x7890; uc_hook h; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); // Emulate DEC in the nested hook. OK(uc_hook_add(uc, &h, UC_HOOK_CODE, test_x86_nested_emu_start_cb, NULL, code_start, code_start)); // Emulate INC OK(uc_emu_start(uc, code_start, code_start + 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_ecx == 0x1235); TEST_CHECK(r_edx == 0x788f); OK(uc_close(uc)); } static void test_x86_nested_emu_stop_cb(uc_engine *uc, uint64_t addr, size_t size, void *data) { OK(uc_emu_start(uc, code_start + 1, code_start + 2, 0, 0)); // ecx shouldn't be changed! OK(uc_emu_stop(uc)); } static void test_x86_nested_emu_stop(void) { uc_engine *uc; // INC ecx; DEC edx; DEC edx; char code[] = "\x41\x4a\x4a"; int r_ecx = 0x1234; int r_edx = 0x7890; uc_hook h; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_write(uc, UC_X86_REG_EDX, &r_edx)); // Emulate DEC in the nested hook. OK(uc_hook_add(uc, &h, UC_HOOK_CODE, test_x86_nested_emu_stop_cb, NULL, code_start, code_start)); OK(uc_emu_start(uc, code_start, code_start + 3, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx)); OK(uc_reg_read(uc, UC_X86_REG_EDX, &r_edx)); TEST_CHECK(r_ecx == 0x1234); TEST_CHECK(r_edx == 0x788f); OK(uc_close(uc)); } static void test_x86_nested_emu_start_error_cb(uc_engine *uc, uint64_t addr, size_t size, void *data) { uc_assert_err(UC_ERR_READ_UNMAPPED, uc_emu_start(uc, code_start + 2, 0, 0, 0)); } static void test_x86_64_nested_emu_start_error(void) { uc_engine *uc; // "nop;nop;mov rax, [0x10000]" char code[] = "\x90\x90\x48\xa1\x00\x00\x01\x00\x00\x00\x00\x00"; uc_hook hk; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hk, UC_HOOK_CODE, test_x86_nested_emu_start_error_cb, NULL, code_start, code_start)); // This call shouldn't fail! OK(uc_emu_start(uc, code_start, code_start + 2, 0, 0)); OK(uc_close(uc)); } static void test_x86_eflags_reserved_bit(void) { uc_engine *uc; uint32_t r_eflags; OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &r_eflags)); TEST_CHECK((r_eflags & 2) != 0); OK(uc_reg_write(uc, UC_X86_REG_EFLAGS, &r_eflags)); OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &r_eflags)); TEST_CHECK((r_eflags & 2) != 0); OK(uc_close(uc)); } static void test_x86_nested_uc_emu_start_exits_cb(uc_engine *uc, uint64_t addr, size_t size, void *data) { OK(uc_emu_start(uc, code_start + 5, code_start + 6, 0, 0)); } static void test_x86_nested_uc_emu_start_exits(void) { uc_engine *uc; // cmp eax, 0 // jnz t // nop <-- nested emu_start // t:mov dword ptr [eax], 0 char code[] = "\x83\xf8\x00\x75\x01\x90\xc7\x00\x00\x00\x00\x00"; uc_hook hk; uint32_t r_pc; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hk, UC_HOOK_CODE, test_x86_nested_uc_emu_start_exits_cb, NULL, code_start, code_start)); OK(uc_emu_start(uc, code_start, code_start + 5, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EIP, &r_pc)); TEST_CHECK(r_pc == code_start + 5); OK(uc_close(uc)); } static bool test_x86_correct_address_in_small_jump_hook_callback( uc_engine *uc, int type, uint64_t address, int size, int64_t value, void *user_data) { // Check registers uint64_t r_rax = 0x0; uint64_t r_rip = 0x0; OK(uc_reg_read(uc, UC_X86_REG_RAX, &r_rax)); OK(uc_reg_read(uc, UC_X86_REG_RIP, &r_rip)); TEST_CHECK(r_rax == 0x7F00); TEST_CHECK(r_rip == 0x7F00); // Check address // printf("%lx\n", address); TEST_CHECK(address == 0x7F00); return false; } static void test_x86_correct_address_in_small_jump_hook(void) { uc_engine *uc; // movabs $0x7F00, %rax // jmp *%rax char code[] = "\x48\xb8\x00\x7F\x00\x00\x00\x00\x00\x00\xff\xe0"; uint64_t r_rax = 0x0; uint64_t r_rip = 0x0; uc_hook hook; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_UNMAPPED, test_x86_correct_address_in_small_jump_hook_callback, NULL, 1, 0)); uc_assert_err( UC_ERR_FETCH_UNMAPPED, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_RAX, &r_rax)); OK(uc_reg_read(uc, UC_X86_REG_RIP, &r_rip)); TEST_CHECK(r_rax == 0x7F00); TEST_CHECK(r_rip == 0x7F00); OK(uc_close(uc)); } static bool test_x86_correct_address_in_long_jump_hook_callback( uc_engine *uc, int type, uint64_t address, int size, int64_t value, void *user_data) { // Check registers uint64_t r_rax = 0x0; uint64_t r_rip = 0x0; OK(uc_reg_read(uc, UC_X86_REG_RAX, &r_rax)); OK(uc_reg_read(uc, UC_X86_REG_RIP, &r_rip)); TEST_CHECK(r_rax == 0x7FFFFFFFFFFFFF00); TEST_CHECK(r_rip == 0x7FFFFFFFFFFFFF00); // Check address // printf("%lx\n", address); TEST_CHECK(address == 0x7FFFFFFFFFFFFF00); return false; } static void test_x86_correct_address_in_long_jump_hook(void) { uc_engine *uc; // movabs $0x7FFFFFFFFFFFFF00, %rax // jmp *%rax char code[] = "\x48\xb8\x00\xff\xff\xff\xff\xff\xff\x7f\xff\xe0"; uint64_t r_rax = 0x0; uint64_t r_rip = 0x0; uc_hook hook; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_ctl_tlb_mode(uc, UC_TLB_VIRTUAL)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_UNMAPPED, test_x86_correct_address_in_long_jump_hook_callback, NULL, 1, 0)); uc_assert_err( UC_ERR_FETCH_UNMAPPED, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_RAX, &r_rax)); OK(uc_reg_read(uc, UC_X86_REG_RIP, &r_rip)); TEST_CHECK(r_rax == 0x7FFFFFFFFFFFFF00); TEST_CHECK(r_rip == 0x7FFFFFFFFFFFFF00); OK(uc_close(uc)); } static void test_x86_invalid_vex_l(void) { uc_engine *uc; /* vmovdqu ymm1, [rcx] */ char code[] = {'\xC5', '\xFE', '\x6F', '\x09'}; /* initialize memory and run emulation */ OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); OK(uc_mem_map(uc, 0, 2 * 1024 * 1024, UC_PROT_ALL)); OK(uc_mem_write(uc, 0, code, sizeof(code) / sizeof(code[0]))); uc_assert_err(UC_ERR_INSN_INVALID, uc_emu_start(uc, 0, sizeof(code) / sizeof(code[0]), 0, 0)); OK(uc_close(uc)); } // AARCH64 inline the read while s390x won't split the access. Though not tested // on other hosts but we restrict a bit more. #if !defined(TARGET_READ_INLINED) && defined(BOOST_LITTLE_ENDIAN) struct writelog_t { uint32_t addr, size; }; static void test_x86_unaligned_access_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) { TEST_CHECK(size != 0); struct writelog_t *write_log = (struct writelog_t *)user_data; for (int i = 0; i < 10; i++) { if (write_log[i].size == 0) { write_log[i].addr = (uint32_t)address; write_log[i].size = (uint32_t)size; return; } } TEST_ASSERT(false); } static void test_x86_unaligned_access(void) { uc_engine *uc; uc_hook hook; // mov dword ptr [0x200001], eax; mov eax, dword ptr [0x200001] char code[] = "\xa3\x01\x00\x20\x00\xa1\x01\x00\x20\x00"; uint32_t r_eax = LEINT32(0x41424344); struct writelog_t write_log[10]; struct writelog_t read_log[10]; memset(write_log, 0, sizeof(write_log)); memset(read_log, 0, sizeof(read_log)); uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_mem_map(uc, 0x200000, 0x1000, UC_PROT_ALL)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, test_x86_unaligned_access_callback, write_log, 1, 0)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_READ, test_x86_unaligned_access_callback, read_log, 1, 0)); OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_eax)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(write_log[0].addr == 0x200001); TEST_CHECK(write_log[0].size == 4); TEST_CHECK(write_log[1].size == 0); TEST_CHECK(read_log[0].addr == 0x200001); TEST_CHECK(read_log[0].size == 4); TEST_CHECK(read_log[1].size == 0); char b; OK(uc_mem_read(uc, 0x200001, &b, 1)); TEST_CHECK(b == 0x44); OK(uc_mem_read(uc, 0x200002, &b, 1)); TEST_CHECK(b == 0x43); OK(uc_mem_read(uc, 0x200003, &b, 1)); TEST_CHECK(b == 0x42); OK(uc_mem_read(uc, 0x200004, &b, 1)); TEST_CHECK(b == 0x41); OK(uc_close(uc)); } static void test_x86_64_unaligned_access(void) { uc_engine *uc; uc_hook hook; char code[] = {"\x48\x89\x01" // mov qword ptr [rcx],rax "\x48\x8b\x00" // mov rax,qword ptr [rax] "\xcc"}; uint64_t r_rax = LEINT64(0x2fffff); uint64_t r_rcx = LEINT64(0x2fffff); struct writelog_t write_log[10]; struct writelog_t read_log[10]; memset(write_log, 0, sizeof(write_log)); memset(read_log, 0, sizeof(read_log)); uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_mem_map(uc, 0x200000, 0x200000, UC_PROT_ALL)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_WRITE, test_x86_unaligned_access_callback, write_log, 1, 0)); OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_READ, test_x86_unaligned_access_callback, read_log, 1, 0)); OK(uc_reg_write(uc, UC_X86_REG_RAX, &r_rax)); OK(uc_reg_write(uc, UC_X86_REG_RCX, &r_rcx)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 2)); TEST_CHECK(write_log[0].addr == 0x2fffff); TEST_CHECK(write_log[0].size == 8); TEST_CHECK(write_log[1].size == 0); TEST_CHECK(read_log[0].addr == 0x2fffff); TEST_CHECK(read_log[0].size == 8); TEST_CHECK(read_log[1].size == 0); uint64_t b; OK(uc_mem_read(uc, 0x2fffff, &b, 8)); TEST_CHECK(b == 0x2fffff); OK(uc_close(uc)); } #endif static bool test_x86_lazy_mapping_mem_callback(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data) { OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); OK(uc_mem_write(uc, 0x1000, "\x90\x90", 2)); // nop; nop // Handled! return true; } static void test_x86_lazy_mapping_block_callback(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { int *block_count = (int *)user_data; (*block_count)++; } static void test_x86_lazy_mapping(void) { uc_engine *uc; uc_hook mem_hook, block_hook; int block_count = 0; OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_hook_add(uc, &mem_hook, UC_HOOK_MEM_FETCH_UNMAPPED, test_x86_lazy_mapping_mem_callback, NULL, 1, 0)); OK(uc_hook_add(uc, &block_hook, UC_HOOK_BLOCK, test_x86_lazy_mapping_block_callback, &block_count, 1, 0)); OK(uc_emu_start(uc, 0x1000, 0x1002, 0, 0)); TEST_CHECK(block_count == 1); OK(uc_close(uc)); } static void test_x86_16_incorrect_ip_cb(uc_engine *uc, uint64_t address, uint32_t size, void *data) { uint16_t cs, ip; OK(uc_reg_read(uc, UC_X86_REG_CS, &cs)); OK(uc_reg_read(uc, UC_X86_REG_IP, &ip)); TEST_CHECK(cs == 0x20); TEST_CHECK(address == ((cs << 4) + ip)); } static void test_x86_16_incorrect_ip(void) { uc_engine *uc; uc_hook hk1, hk2; uint16_t cs = 0x20; char code[] = "\x41"; // INC cx; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_16, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hk1, UC_HOOK_BLOCK, test_x86_16_incorrect_ip_cb, NULL, 1, 0)); OK(uc_hook_add(uc, &hk2, UC_HOOK_CODE, test_x86_16_incorrect_ip_cb, NULL, 1, 0)); OK(uc_reg_write(uc, UC_X86_REG_CS, &cs)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } // Porting to BE: Only uc_mem_read/write needs endian fixing static void test_x86_mmu_prepare_tlb(uc_engine *uc, uint64_t vaddr, uint64_t tlb_base) { uint64_t cr0; uint64_t cr4; uc_x86_msr msr = {.rid = 0x0c0000080, .value = 0}; uint64_t pml4o = ((vaddr & 0x00ff8000000000) >> 39) * 8; uint64_t pdpo = ((vaddr & 0x00007fc0000000) >> 30) * 8; uint64_t pdo = ((vaddr & 0x0000003fe00000) >> 21) * 8; uint64_t pml4e = (tlb_base + 0x1000) | 1 | (1 << 2); uint64_t pdpe = (tlb_base + 0x2000) | 1 | (1 << 2); uint64_t pde = (tlb_base + 0x3000) | 1 | (1 << 2); uint64_t pml4e_mem = LEINT64(pml4e); uint64_t pde_mem = LEINT64(pde); uint64_t pdpe_mem = LEINT64(pdpe); OK(uc_mem_write(uc, tlb_base + pml4o, &pml4e_mem, sizeof(pml4o))); OK(uc_mem_write(uc, tlb_base + 0x1000 + pdpo, &pdpe_mem, sizeof(pdpe))); OK(uc_mem_write(uc, tlb_base + 0x2000 + pdo, &pde_mem, sizeof(pde))); OK(uc_reg_write(uc, UC_X86_REG_CR3, &tlb_base)); OK(uc_reg_read(uc, UC_X86_REG_CR0, &cr0)); OK(uc_reg_read(uc, UC_X86_REG_CR4, &cr4)); OK(uc_reg_read(uc, UC_X86_REG_MSR, &msr)); cr0 |= 1; cr0 |= 1l << 31; cr4 |= 1l << 5; msr.value |= 1l << 8; OK(uc_reg_write(uc, UC_X86_REG_CR0, &cr0)); OK(uc_reg_write(uc, UC_X86_REG_CR4, &cr4)); OK(uc_reg_write(uc, UC_X86_REG_MSR, &msr)); } static void test_x86_mmu_pt_set(uc_engine *uc, uint64_t vaddr, uint64_t paddr, uint64_t tlb_base) { uint64_t pto = ((vaddr & 0x000000001ff000) >> 12) * 8; uint32_t pte = (paddr) | 1 | (1 << 2); pte = LEINT32(pte); uc_mem_write(uc, tlb_base + 0x3000 + pto, &pte, sizeof(pte)); } static void test_x86_mmu_callback(uc_engine *uc, void *userdata) { bool *parrent_done = userdata; uint64_t rax; OK(uc_reg_read(uc, UC_X86_REG_RAX, &rax)); switch (rax) { case 57: /* fork */ break; case 60: /* exit */ uc_emu_stop(uc); return; default: TEST_CHECK(false); } if (!(*parrent_done)) { *parrent_done = true; rax = 27; OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); uc_emu_stop(uc); } } static void test_x86_mmu(void) { bool parrent_done = false; uint64_t tlb_base = 0x3000; uint64_t parrent, child; uint64_t rax, rip; uc_context *context; uc_engine *uc; uc_hook h1; /* * mov rax, 57 * syscall * test rax, rax * jz child * xor rax, rax * mov rax, 60 * mov [0x4000], rax * syscall * * child: * xor rcx, rcx * mov rcx, 42 * mov [0x4000], rcx * mov rax, 60 * syscall */ char code[] = "\xB8\x39\x00\x00\x00\x0F\x05\x48\x85\xC0\x74\x0F\xB8\x3C\x00\x00\x00" "\x48\x89\x04\x25\x00\x40\x00\x00\x0F\x05\xB9\x2A\x00\x00\x00\x48\x89" "\x0C\x25\x00\x40\x00\x00\xB8\x3C\x00\x00\x00\x0F\x05"; OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); OK(uc_ctl_tlb_mode(uc, UC_TLB_CPU)); OK(uc_hook_add(uc, &h1, UC_HOOK_INSN, &test_x86_mmu_callback, &parrent_done, 1, 0, UC_X86_INS_SYSCALL)); OK(uc_context_alloc(uc, &context)); OK(uc_mem_map(uc, 0x0, 0x1000, UC_PROT_ALL)); // Code OK(uc_mem_write(uc, 0x0, code, sizeof(code) - 1)); OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL)); // Parrent OK(uc_mem_map(uc, 0x2000, 0x1000, UC_PROT_ALL)); // Child OK(uc_mem_map(uc, tlb_base, 0x4000, UC_PROT_ALL)); // TLB test_x86_mmu_prepare_tlb(uc, 0x0, tlb_base); test_x86_mmu_pt_set(uc, 0x2000, 0x0, tlb_base); test_x86_mmu_pt_set(uc, 0x4000, 0x1000, tlb_base); OK(uc_ctl_flush_tlb(uc)); OK(uc_emu_start(uc, 0x2000, 0x0, 0, 0)); OK(uc_context_save(uc, context)); OK(uc_reg_read(uc, UC_X86_REG_RIP, &rip)); OK(uc_emu_start(uc, rip, 0x0, 0, 0)); /* restore for child */ OK(uc_context_restore(uc, context)); test_x86_mmu_prepare_tlb(uc, 0x0, tlb_base); test_x86_mmu_pt_set(uc, 0x4000, 0x2000, tlb_base); rax = 0; OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); OK(uc_ctl_flush_tlb(uc)); OK(uc_emu_start(uc, rip, 0x0, 0, 0)); OK(uc_mem_read(uc, 0x1000, &parrent, sizeof(parrent))); OK(uc_mem_read(uc, 0x2000, &child, sizeof(child))); TEST_CHECK(LEINT64(parrent) == 60); TEST_CHECK(LEINT64(child) == 42); OK(uc_context_free(context)); OK(uc_close(uc)); } static bool test_x86_vtlb_callback(uc_engine *uc, uint64_t addr, uc_mem_type type, uc_tlb_entry *result, void *user_data) { result->paddr = addr; result->perms = UC_PROT_ALL; return true; } static void test_x86_vtlb(void) { uc_engine *uc; uc_hook hook; char code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop; // nop; nop; nop uint32_t r_eip = 0; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_ctl_tlb_mode(uc, UC_TLB_VIRTUAL)); OK(uc_hook_add(uc, &hook, UC_HOOK_TLB_FILL, test_x86_vtlb_callback, NULL, 1, 0)); OK(uc_emu_start(uc, code_start, code_start + 4, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EIP, &r_eip)); TEST_CHECK(r_eip == code_start + 4); OK(uc_close(uc)); } static void test_x86_segmentation(void) { uc_engine *uc; uint16_t fs = 0x53; uc_x86_mmr gdtr = {0, 0xfffff8076d962000, 0x57, 0}; OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); OK(uc_reg_write(uc, UC_X86_REG_GDTR, &gdtr)); uc_assert_err(UC_ERR_EXCEPTION, uc_reg_write(uc, UC_X86_REG_FS, &fs)); OK(uc_close(uc)); } static void test_x86_0xff_lcall_callback(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { // do nothing return; } // This aborts prior to a7a5d187e77f7853755eff4768658daf8095c3b7 static void test_x86_0xff_lcall(void) { uc_engine *uc; uc_hook hk; const char code[] = "\xB8\x01\x00\x00\x00\xBB\x01\x00\x00\x00\xB9\x01\x00\x00\x00\xFF\xDD" "\xBA\x01\x00\x00\x00\xB8\x02\x00\x00\x00\xBB\x02\x00\x00\x00"; // Taken from #1842 // 0: b8 01 00 00 00 mov eax,0x1 // 5: bb 01 00 00 00 mov ebx,0x1 // a: b9 01 00 00 00 mov ecx,0x1 // f: ff (bad) // 10: dd ba 01 00 00 00 fnstsw WORD PTR [edx+0x1] // 16: b8 02 00 00 00 mov eax,0x2 // 1b: bb 02 00 00 00 mov ebx,0x2 uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hk, UC_HOOK_CODE, test_x86_0xff_lcall_callback, NULL, 1, 0)); uc_assert_err( UC_ERR_INSN_INVALID, uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } static bool test_x86_64_not_overwriting_tmp0_for_pc_update_cb( uc_engine *uc, uc_mem_type type, uint64_t address, int size, uint64_t value, void *user_data) { return true; } // https://github.com/unicorn-engine/unicorn/issues/1717 // https://github.com/unicorn-engine/unicorn/issues/1862 static void test_x86_64_not_overwriting_tmp0_for_pc_update(void) { uc_engine *uc; uc_hook hk; const char code[] = "\x48\xb9\xff\xff\xff\xff\xff\xff\xff\xff\x48\x89\x0c" "\x24\x48\xd3\x24\x24\x73\x0a"; uint64_t rsp, pc; uint32_t eflags; // 0x1000: movabs rcx, 0xffffffffffffffff // 0x100a: mov qword ptr [rsp], rcx // 0x100e: shl qword ptr [rsp], cl ; (Shift to CF=1) // 0x1012: jae 0xd ; this jump should not be taken! (CF=1 but jae // expects CF=0) uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hk, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, test_x86_64_not_overwriting_tmp0_for_pc_update_cb, NULL, 1, 0)); rsp = 0x2000; OK(uc_reg_write(uc, UC_X86_REG_RSP, (void *)&rsp)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 4)); OK(uc_reg_read(uc, UC_X86_REG_RIP, &pc)); OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags)); TEST_CHECK(pc == 0x1014); TEST_CHECK((eflags & 0x1) == 1); OK(uc_close(uc)); } static void test_fxsave_fpip_x86(void) { // note: fxsave was introduced in Pentium II uint8_t code_x86[] = { // help testing through NOP offset [disassembly in at&t syntax] 0x90, 0x90, 0x90, 0x90, // nop nop nop nop // run a floating point instruction 0xdb, 0xc9, // fcmovne %st(1), %st // fxsave needs 512 bytes of storage space 0x81, 0xec, 0x00, 0x02, 0x00, 0x00, // subl $512, %esp // fxsave needs a 16-byte aligned address for storage 0x83, 0xe4, 0xf0, // andl $0xfffffff0, %esp // store fxsave data on the stack 0x0f, 0xae, 0x04, 0x24, // fxsave (%esp) // fxsave stores FPIP at an 8-byte offset, move FPIP to eax register 0x8b, 0x44, 0x24, 0x08 // movl 0x8(%esp), %eax }; uint32_t X86_NOP_OFFSET = 4; uint32_t stack_top = (uint32_t)MEM_STACK; uint32_t value; uc_engine *uc; // initialize emulator in X86-32bit mode OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); // map 1MB of memory for this emulation OK(uc_mem_map(uc, MEM_BASE, MEM_SIZE, UC_PROT_ALL)); OK(uc_mem_write(uc, MEM_TEXT, code_x86, sizeof(code_x86))); OK(uc_reg_write(uc, UC_X86_REG_ESP, &stack_top)); OK(uc_emu_start(uc, MEM_TEXT, MEM_TEXT + sizeof(code_x86), 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EAX, &value)); TEST_CHECK(value == ((uint32_t)MEM_TEXT + X86_NOP_OFFSET)); OK(uc_mem_unmap(uc, MEM_BASE, MEM_SIZE)); OK(uc_close(uc)); } static void test_fxsave_fpip_x64(void) { uint8_t code_x64[] = { // help testing through NOP offset [disassembly in at&t] 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops // run a floating point instruction 0xdb, 0xc9, // fcmovne %st(1), %st // fxsave64 needs 512 bytes of storage space 0x48, 0x81, 0xec, 0x00, 0x02, 0x00, 0x00, // subq $512, %rsp // fxsave needs a 16-byte aligned address for storage 0x48, 0x83, 0xe4, 0xf0, // andq 0xfffffffffffffff0, %rsp // store fxsave64 data on the stack 0x48, 0x0f, 0xae, 0x04, 0x24, // fxsave64 (%rsp) // fxsave64 stores FPIP at an 8-byte offset, move FPIP to rax register 0x48, 0x8b, 0x44, 0x24, 0x08, // movq 0x8(%rsp), %rax }; uint64_t stack_top = (uint64_t)MEM_STACK; uint64_t X64_NOP_OFFSET = 8; uint64_t value; uc_engine *uc; // initialize emulator in X86-32bit mode OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); // map 1MB of memory for this emulation OK(uc_mem_map(uc, MEM_BASE, MEM_SIZE, UC_PROT_ALL)); OK(uc_mem_write(uc, MEM_TEXT, code_x64, sizeof(code_x64))); OK(uc_reg_write(uc, UC_X86_REG_RSP, &stack_top)); OK(uc_emu_start(uc, MEM_TEXT, MEM_TEXT + sizeof(code_x64), 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_RAX, &value)); TEST_CHECK(value == ((uint64_t)MEM_TEXT + X64_NOP_OFFSET)); OK(uc_mem_unmap(uc, MEM_BASE, MEM_SIZE)); OK(uc_close(uc)); } static void test_bswap_ax(void) { // References: // - https://gynvael.coldwind.pl/?id=268 // - https://github.com/JonathanSalwan/Triton/issues/1131 { uint8_t code[] = { // bswap ax 0x66, 0x0F, 0xC8, }; TEST_CODE(UC_MODE_32, code); TEST_IN_REG(EAX, 0x44332211); TEST_OUT_REG(EAX, 0x44330000); TEST_RUN(); } { uint8_t code[] = { // bswap ax 0x66, 0x0F, 0xC8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_OUT_REG(RAX, 0x8877665544330000); TEST_RUN(); } { uint8_t code[] = { // bswap rax (66h ignored) 0x66, 0x48, 0x0F, 0xC8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_OUT_REG(RAX, 0x1122334455667788); TEST_RUN(); } { uint8_t code[] = { // bswap ax (rex ignored) 0x48, 0x66, 0x0F, 0xC8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_OUT_REG(RAX, 0x8877665544330000); TEST_RUN(); } { uint8_t code[] = { // bswap eax 0x0F, 0xC8, }; TEST_CODE(UC_MODE_32, code); TEST_IN_REG(EAX, 0x44332211); TEST_OUT_REG(EAX, 0x11223344); TEST_RUN(); } { uint8_t code[] = { // bswap eax 0x0F, 0xC8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_OUT_REG(RAX, 0x0000000011223344); TEST_RUN(); } } static void test_rex_x64(void) { { uint8_t code[] = { // mov ax, bx (rex.w ignored) 0x48, 0x66, 0x89, 0xD8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_IN_REG(RBX, 0x1122334455667788); TEST_OUT_REG(RAX, 0x8877665544337788); TEST_RUN(); } { uint8_t code[] = { // mov rax, rbx (66h ignored) 0x66, 0x48, 0x89, 0xD8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_IN_REG(RBX, 0x1122334455667788); TEST_OUT_REG(RAX, 0x1122334455667788); TEST_RUN(); } { uint8_t code[] = { // mov ax, bx (expected encoding) 0x66, 0x89, 0xD8, }; TEST_CODE(UC_MODE_64, code); TEST_IN_REG(RAX, 0x8877665544332211); TEST_IN_REG(RBX, 0x1122334455667788); TEST_OUT_REG(RAX, 0x8877665544337788); TEST_RUN(); } } static bool test_x86_ro_segfault_cb(uc_engine *uc, uc_mem_type type, uint64_t address, int size, uint64_t value, void *user_data) { const char code[] = "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; OK(uc_mem_write(uc, address, code, sizeof(code) - 1)); return true; } static void test_x86_ro_segfault(void) { uc_engine *uc; // mov eax, [0x1000] // mov eax, [0x1000] const char code[] = "\xA1\x00\x10\x00\x00\xA1\x00\x10\x00\x00"; uint32_t out; uc_hook hh; OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); OK(uc_mem_map(uc, 0, 0x1000, UC_PROT_ALL)); OK(uc_mem_write(uc, 0, code, sizeof(code) - 1)); OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_READ)); OK(uc_hook_add(uc, &hh, UC_HOOK_MEM_READ, test_x86_ro_segfault_cb, NULL, 1, 0)); OK(uc_emu_start(uc, 0, sizeof(code) - 1, 0, 0)); OK(uc_reg_read(uc, UC_X86_REG_EAX, (void *)&out)); TEST_CHECK(out == 0x001000a1); OK(uc_close(uc)); } static bool test_x86_hook_insn_rdtsc_cb(uc_engine *uc, void *user_data) { uint64_t h = 0x00000000FEDCBA98; OK(uc_reg_write(uc, UC_X86_REG_RDX, &h)); uint64_t l = 0x0000000076543210; OK(uc_reg_write(uc, UC_X86_REG_RAX, &l)); return true; } static void test_x86_hook_insn_rdtsc(void) { char code[] = "\x0F\x31"; // RDTSC uc_engine *uc; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof code - 1); uc_hook hook; OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdtsc_cb, NULL, 1, 0, UC_X86_INS_RDTSC)); OK(uc_emu_start(uc, code_start, code_start + sizeof code - 1, 0, 0)); OK(uc_hook_del(uc, hook)); uint64_t h = 0; OK(uc_reg_read(uc, UC_X86_REG_RDX, &h)); TEST_CHECK(h == 0x00000000FEDCBA98); uint64_t l = 0; OK(uc_reg_read(uc, UC_X86_REG_RAX, &l)); TEST_CHECK(l == 0x0000000076543210); OK(uc_close(uc)); } static bool test_x86_hook_insn_rdtscp_cb(uc_engine *uc, void *user_data) { uint64_t h = 0x0000000001234567; OK(uc_reg_write(uc, UC_X86_REG_RDX, &h)); uint64_t l = 0x0000000089ABCDEF; OK(uc_reg_write(uc, UC_X86_REG_RAX, &l)); uint64_t i = 0x00000000DEADBEEF; OK(uc_reg_write(uc, UC_X86_REG_RCX, &i)); return true; } static void test_x86_hook_insn_rdtscp(void) { uc_engine *uc; OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc)); OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL)); OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL)); char code[] = "\x0F\x01\xF9"; // RDTSCP OK(uc_mem_write(uc, code_start, code, sizeof code - 1)); uc_hook hook; OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdtscp_cb, NULL, 1, 0, UC_X86_INS_RDTSCP)); OK(uc_emu_start(uc, code_start, code_start + sizeof code - 1, 0, 0)); OK(uc_hook_del(uc, hook)); uint64_t h = 0; OK(uc_reg_read(uc, UC_X86_REG_RDX, &h)); TEST_CHECK(h == 0x0000000001234567); uint64_t l = 0; OK(uc_reg_read(uc, UC_X86_REG_RAX, &l)); TEST_CHECK(l == 0x0000000089ABCDEF); uint64_t i = 0; OK(uc_reg_read(uc, UC_X86_REG_RCX, &i)); TEST_CHECK(i == 0x00000000DEADBEEF); OK(uc_close(uc)); } static void test_x86_dr7() { uc_engine *uc; char code[] = "\x48\xC7\xC0\x05\x00\x01\x00\x0F\x23\xF8"; // mov rax, 0x10005 // mov dr7, rax uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof(code) - 1); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } static void test_x86_hook_block_cb(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) { uint32_t pc; OK(uc_reg_read(uc, UC_X86_REG_EIP, (void *)&pc)); TEST_CHECK(pc == address); *((uint64_t *)user_data) += 1; } static void test_x86_hook_block() { uc_engine *uc; char code[] = "\xeb\x02\x90\x90\x90\x90\x90\x90"; // jmp 4; nop; nop; nop; // nop; nop; nop uint64_t cnt = 0; uc_hook hk; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_hook_add(uc, &hk, UC_HOOK_BLOCK, test_x86_hook_block_cb, (void *)&cnt, 1, 0)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(cnt == 2); OK(uc_close(uc)); } static bool test_x86_mem_hooks_pc_guarante_mem(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, int64_t val, void *data) { if (addr >= code_start + code_len) { uint32_t eip; OK(uc_reg_read(uc, UC_X86_REG_EIP, (void*)&eip)); TEST_CHECK(eip == code_start + 1); } return true; } static void test_x86_mem_hooks_pc_guarantee(void) { uc_engine *uc; // bs, _ = ks.asm("inc edx; t: mov eax, [ebx]; inc ebx; cmp ebx, ecx; jnz t;") char code[] = "\x42\x8b\x03\x43\x39\xcb\x75\xf9"; uint32_t ebx=code_start + code_len, ecx = code_start + code_len + 0x10; uc_hook hk; uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1); OK(uc_mem_map(uc, code_start + code_len, 0x1000, UC_PROT_ALL)); OK(uc_hook_add(uc, &hk, UC_HOOK_MEM_READ, test_x86_mem_hooks_pc_guarante_mem, NULL, 1, 0)); OK(uc_reg_write(uc, UC_X86_REG_EBX, (void*)&ebx)); OK(uc_reg_write(uc, UC_X86_REG_ECX, (void*)&ecx)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_close(uc)); } TEST_LIST = { {"test_x86_in", test_x86_in}, {"test_x86_out", test_x86_out}, {"test_x86_mem_hook_all", test_x86_mem_hook_all}, {"test_x86_inc_dec_pxor", test_x86_inc_dec_pxor}, {"test_x86_relative_jump", test_x86_relative_jump}, {"test_x86_loop", test_x86_loop}, {"test_x86_invalid_mem_read", test_x86_invalid_mem_read}, {"test_x86_invalid_mem_write", test_x86_invalid_mem_write}, {"test_x86_invalid_jump", test_x86_invalid_jump}, {"test_x86_64_syscall", test_x86_64_syscall}, {"test_x86_16_add", test_x86_16_add}, {"test_x86_reg_save", test_x86_reg_save}, {"test_x86_invalid_mem_read_stop_in_cb", test_x86_invalid_mem_read_stop_in_cb}, {"test_x86_x87_fnstenv", test_x86_x87_fnstenv}, {"test_x86_mmio", test_x86_mmio}, {"test_x86_missing_code", test_x86_missing_code}, {"test_x86_smc_xor", test_x86_smc_xor}, {"test_x86_smc_add", test_x86_smc_add}, {"test_x86_smc_mem_hook", test_x86_smc_mem_hook}, {"test_x86_mmio_uc_mem_rw", test_x86_mmio_uc_mem_rw}, {"test_x86_sysenter", test_x86_sysenter}, {"test_x86_hook_cpuid", test_x86_hook_cpuid}, {"test_x86_486_cpuid", test_x86_486_cpuid}, {"test_x86_clear_tb_cache", test_x86_clear_tb_cache}, {"test_x86_clear_empty_tb", test_x86_clear_empty_tb}, {"test_x86_hook_tcg_op", test_x86_hook_tcg_op}, {"test_x86_cmpxchg", test_x86_cmpxchg}, {"test_x86_nested_emu_start", test_x86_nested_emu_start}, {"test_x86_nested_emu_stop", test_x86_nested_emu_stop}, {"test_x86_64_nested_emu_start_error", test_x86_64_nested_emu_start_error}, {"test_x86_eflags_reserved_bit", test_x86_eflags_reserved_bit}, {"test_x86_nested_uc_emu_start_exits", test_x86_nested_uc_emu_start_exits}, {"test_x86_clear_count_cache", test_x86_clear_count_cache}, {"test_x86_correct_address_in_small_jump_hook", test_x86_correct_address_in_small_jump_hook}, {"test_x86_correct_address_in_long_jump_hook", test_x86_correct_address_in_long_jump_hook}, {"test_x86_invalid_vex_l", test_x86_invalid_vex_l}, #if !defined(TARGET_READ_INLINED) && defined(BOOST_LITTLE_ENDIAN) {"test_x86_unaligned_access", test_x86_unaligned_access}, {"test_x86_64_unaligned_access", test_x86_64_unaligned_access}, #endif {"test_x86_lazy_mapping", test_x86_lazy_mapping}, {"test_x86_16_incorrect_ip", test_x86_16_incorrect_ip}, {"test_x86_mmu", test_x86_mmu}, {"test_x86_vtlb", test_x86_vtlb}, {"test_x86_segmentation", test_x86_segmentation}, {"test_x86_0xff_lcall", test_x86_0xff_lcall}, {"test_x86_64_not_overwriting_tmp0_for_pc_update", test_x86_64_not_overwriting_tmp0_for_pc_update}, {"test_fxsave_fpip_x86", test_fxsave_fpip_x86}, {"test_fxsave_fpip_x64", test_fxsave_fpip_x64}, {"test_bswap_x64", test_bswap_ax}, {"test_rex_x64", test_rex_x64}, {"test_x86_ro_segfault", test_x86_ro_segfault}, {"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc}, {"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp}, {"test_x86_dr7", test_x86_dr7}, {"test_x86_hook_block", test_x86_hook_block}, {"test_x86_mem_hooks_pc_guarantee", test_x86_mem_hooks_pc_guarantee}, {NULL, NULL}};