Files
unicorn/tests/unit/test_x86.c
PhilippTakacs 65ed715081 Snapshot use after free (#2125)
* memory snapshots fix use after free on flatview copy

When restoring a snapshot with memory the flatview must be restored
before the memory reagions are filtered. Because the
AddressSpaceDispatcher also has pointer to the MemoryRegions and on copy
they need to be cleared. The memory_filter_subregions function frees
MemoryRegions which are not used at the time of the snapshot.

* fix some memleaks in tests

These tests has forgott to call uc_close(uc), which lead to memory
leaks. Found by the LeakSanitizer.

* memory snapshots correct clean up container memory regions

* Fix further stackoverflow in tests

---------

Co-authored-by: mio <mio@lazym.io>
2025-03-06 23:23:02 +08:00

2137 lines
65 KiB
C

#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++) {
OK(uc_reg_write(uc, test->in_regs[i].reg, &test->in_regs[i].value));
}
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;
result->port = port;
result->size = size;
}
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)
{
int reg = 7;
OK(uc_reg_write(uc, UC_X86_REG_EAX, &reg));
// 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, &reg));
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));
}
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);
OK(uc_mem_write(uc, tlb_base + pml4o, &pml4e, sizeof(pml4o)));
OK(uc_mem_write(uc, tlb_base + 0x1000 + pdpo, &pdpe, sizeof(pdpe)));
OK(uc_mem_write(uc, tlb_base + 0x2000 + pdo, &pde, 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);
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(parrent == 60);
TEST_CHECK(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
uint64_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;
uint64_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, 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));
}
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},
{NULL, NULL}};