Files
unicorn/tests/unit/test_x86.c

2188 lines
67 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++) {
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, &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, &reg));
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, &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));
}
// 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}};