840 lines
25 KiB
C
840 lines
25 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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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(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()
|
|
{
|
|
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()
|
|
{
|
|
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()
|
|
{
|
|
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(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()
|
|
{
|
|
uc_engine *uc;
|
|
int data = 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(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()
|
|
{
|
|
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 void test_x86_hook_cpuid_callback(uc_engine *uc, void *data)
|
|
{
|
|
int reg = 7;
|
|
|
|
OK(uc_reg_write(uc, UC_X86_REG_EAX, ®));
|
|
}
|
|
|
|
static void test_x86_hook_cpuid()
|
|
{
|
|
uc_engine *uc;
|
|
char code[] = "\x40\x0F\xA2"; // INC EAX; CPUID
|
|
uc_hook h;
|
|
int reg;
|
|
|
|
uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1);
|
|
|
|
OK(uc_hook_add(uc, &h, UC_HOOK_INSN, test_x86_hook_cpuid_callback, NULL, 1,
|
|
0, UC_X86_INS_CPUID));
|
|
|
|
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
|
|
|
OK(uc_reg_read(uc, UC_X86_REG_EAX, ®));
|
|
|
|
TEST_CHECK(reg == 7);
|
|
|
|
OK(uc_close(uc));
|
|
}
|
|
|
|
// This is a regression bug.
|
|
static void test_x86_clear_tb_cache()
|
|
{
|
|
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));
|
|
}
|
|
|
|
// This is a regression bug.
|
|
static void test_x86_clear_empty_tb()
|
|
{
|
|
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, 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()
|
|
{
|
|
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 void 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;
|
|
}
|
|
}
|
|
|
|
static void test_x86_cmpxchg()
|
|
{
|
|
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));
|
|
}
|
|
|
|
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_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},
|
|
{NULL, NULL}}; |