Files
unicorn/tests/unit/test_x86.c
Takacs, Philipp 7f1eb4532d add basic mmu tests
Some simple tests for diffrent mmu.
Basicly add some tlb entries, enable the mmu try to read from virtual address

The aarm64 test was provided by imre-kis-arm in #1718
2023-03-28 13:50:11 +02:00

1432 lines
43 KiB
C

#include "unicorn_test.h"
const uint64_t code_start = 0x1000;
const uint64_t code_len = 0x4000;
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 _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};
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_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 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_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));
}
#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_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);
}
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_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},
#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},
{NULL, NULL}};