fix conflicts
This commit is contained in:
3
tests/unit/.gitignore
vendored
3
tests/unit/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
!*.c
|
||||
test_*
|
||||
*.bin
|
||||
1912
tests/unit/acutest.h
Normal file
1912
tests/unit/acutest.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
.text
|
||||
sidt (esp)
|
||||
sgdt (esp+6)
|
||||
@@ -1,6 +0,0 @@
|
||||
dec %eax
|
||||
mov (%eax), %eax
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
@@ -1,9 +0,0 @@
|
||||
.text
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
inc %edx
|
||||
inc %edx
|
||||
@@ -1,90 +0,0 @@
|
||||
mov %esp,%ecx
|
||||
fxch %st(5)
|
||||
fnstenv -0xc(%ecx)
|
||||
pop %ebp
|
||||
push %ebp
|
||||
pop %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
dec %ecx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
inc %ebx
|
||||
aaa
|
||||
push %ecx
|
||||
pop %edx
|
||||
push $0x41
|
||||
pop %eax
|
||||
push %eax
|
||||
xor %al,0x30(%ecx)
|
||||
inc %ecx
|
||||
imul $0x51,0x41(%ecx),%eax
|
||||
xor 0x42(%ecx),%al
|
||||
xor 0x42(%edx),%al
|
||||
xor %al,0x42(%edx)
|
||||
inc %ecx
|
||||
inc %edx
|
||||
pop %eax
|
||||
push %eax
|
||||
cmp %al,0x42(%ecx)
|
||||
jne .+0x4c
|
||||
dec %ecx
|
||||
push %ecx
|
||||
push %ecx
|
||||
push %ecx
|
||||
push %edx
|
||||
inc %edi
|
||||
xor 0x34(%edi),%eax
|
||||
push %ecx
|
||||
push %ebp
|
||||
push %ecx
|
||||
push %esi
|
||||
push %eax
|
||||
inc %edi
|
||||
inc %edi
|
||||
cmp %al,0x39(%edi)
|
||||
push %eax
|
||||
dec %edx
|
||||
push %eax
|
||||
dec %ebx
|
||||
push %eax
|
||||
dec %esp
|
||||
push %eax
|
||||
dec %ebp
|
||||
push %eax
|
||||
dec %esi
|
||||
push %eax
|
||||
dec %edi
|
||||
push %eax
|
||||
push %eax
|
||||
push %eax
|
||||
xor %eax, 0x42(%edi)
|
||||
inc %edi
|
||||
inc %edx
|
||||
push %eax
|
||||
xor $0x50,%al
|
||||
pop %edx
|
||||
push %eax
|
||||
inc %ebp
|
||||
push %ecx
|
||||
push %edx
|
||||
inc %esi
|
||||
xor 0x31(%edi),%al
|
||||
push %eax
|
||||
dec %ebp
|
||||
push %ecx
|
||||
push %ecx
|
||||
push %eax
|
||||
dec %esi
|
||||
inc %ecx
|
||||
inc %ecx
|
||||
612
tests/unit/test_arm.c
Normal file
612
tests/unit/test_arm.c
Normal file
@@ -0,0 +1,612 @@
|
||||
#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, uc_cpu_arm cpu)
|
||||
{
|
||||
OK(uc_open(arch, mode, uc));
|
||||
OK(uc_ctl_set_cpu_model(*uc, cpu));
|
||||
OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL));
|
||||
OK(uc_mem_write(*uc, code_start, code, size));
|
||||
}
|
||||
|
||||
static void test_arm_nop()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x00\xf0\x20\xe3"; // nop
|
||||
int r_r0 = 0x1234;
|
||||
int r_r2 = 0x6789;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_A15);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
TEST_CHECK(r_r0 == 0x1234);
|
||||
TEST_CHECK(r_r2 == 0x6789);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumb_sub()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x83\xb0"; // sub sp, #0xc
|
||||
int r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_A15);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
TEST_CHECK(r_sp == 0x1228);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_armeb_sub()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] =
|
||||
"\xe3\xa0\x00\x37\xe0\x42\x10\x03"; // mov r0, #0x37; sub r1, r2, r3
|
||||
int r_r0 = 0x1234;
|
||||
int r_r2 = 0x6789;
|
||||
int r_r3 = 0x3333;
|
||||
int r_r1;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM | UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1, UC_CPU_ARM_CORTEX_A15);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R1, &r_r1));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
TEST_CHECK(r_r0 == 0x37);
|
||||
TEST_CHECK(r_r2 == 0x6789);
|
||||
TEST_CHECK(r_r3 == 0x3333);
|
||||
TEST_CHECK(r_r1 == 0x3456);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumbeb_sub()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\xb0\x83"; // sub sp, #0xc
|
||||
int r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1, UC_CPU_ARM_CORTEX_A15);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
TEST_CHECK(r_sp == 0x1228);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumb_ite_count_callback(uc_engine *uc, uint64_t address,
|
||||
uint32_t size, void *user_data)
|
||||
{
|
||||
uint64_t *count = (uint64_t *)user_data;
|
||||
|
||||
(*count) += 1;
|
||||
}
|
||||
|
||||
static void test_arm_thumb_ite()
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook hook;
|
||||
char code[] =
|
||||
"\x9a\x42\x15\xbf\x00\x9a\x01\x9a\x78\x23\x15\x23"; // cmp r2, r3; itete
|
||||
// ne; ldrne r2,
|
||||
// [sp]; ldreq r2,
|
||||
// [sp,#4]; movne
|
||||
// r3, #0x78; moveq
|
||||
// r3, #0x15
|
||||
int r_sp = 0x8000;
|
||||
int r_r2 = 0;
|
||||
int r_r3 = 1;
|
||||
int r_pc = 0;
|
||||
uint64_t count = 0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_A15);
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
OK(uc_mem_map(uc, r_sp, 0x1000, UC_PROT_ALL));
|
||||
r_r2 = 0x68;
|
||||
OK(uc_mem_write(uc, r_sp, &r_r2, 4));
|
||||
r_r2 = 0x4d;
|
||||
OK(uc_mem_write(uc, r_sp + 4, &r_r2, 4));
|
||||
|
||||
OK(uc_hook_add(uc, &hook, UC_HOOK_CODE, test_arm_thumb_ite_count_callback,
|
||||
&count, 1, 0));
|
||||
|
||||
// Execute four instructions at a time.
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R3, &r_r3));
|
||||
TEST_CHECK(r_r2 == 0x68);
|
||||
TEST_CHECK(count == 4);
|
||||
|
||||
r_pc = code_start;
|
||||
r_r2 = 0;
|
||||
count = 0;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
for (int i = 0; i < 6 && r_pc < code_start + sizeof(code) - 1; i++) {
|
||||
// Execute one instruction at a time.
|
||||
OK(uc_emu_start(uc, r_pc | 1, code_start + sizeof(code) - 1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
}
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R2, &r_r2));
|
||||
|
||||
TEST_CHECK(r_r2 == 0x68);
|
||||
TEST_CHECK(r_r3 == 0x78);
|
||||
TEST_CHECK(count == 4);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_m_thumb_mrs()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] =
|
||||
"\xef\xf3\x14\x80\xef\xf3\x00\x81"; // mrs r0, control; mrs r1, apsr
|
||||
uint32_t r_control = 0b10;
|
||||
uint32_t r_apsr = (0b10101 << 27);
|
||||
uint32_t r_r0, r_r1;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS, code,
|
||||
sizeof(code) - 1, UC_CPU_ARM_CORTEX_A15);
|
||||
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_APSR_NZCVQ, &r_apsr));
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R0, &r_r0));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r0 == 0b10);
|
||||
TEST_CHECK(r_r1 == (0b10101 << 27));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_m_control()
|
||||
{
|
||||
uc_engine *uc;
|
||||
int r_control, r_msp, r_psp;
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS, &uc));
|
||||
|
||||
r_control = 0; // Make sure we are using MSP.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
r_msp = 0x1000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_msp));
|
||||
|
||||
r_control = 0b10; // Make the switch.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_psp));
|
||||
TEST_CHECK(r_psp != r_msp);
|
||||
|
||||
r_psp = 0x2000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_psp));
|
||||
|
||||
r_control = 0; // Switch again
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_msp));
|
||||
TEST_CHECK(r_psp != r_msp);
|
||||
TEST_CHECK(r_msp == 0x1000);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
//
|
||||
// Some notes:
|
||||
// Qemu raise a special exception EXCP_EXCEPTION_EXIT to handle the
|
||||
// EXC_RETURN. We can't help user handle EXC_RETURN since unicorn is designed
|
||||
// not to handle any CPU exception.
|
||||
//
|
||||
static void test_arm_m_exc_return_hook_interrupt(uc_engine *uc, int intno,
|
||||
void *data)
|
||||
{
|
||||
int r_pc;
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
TEST_CHECK(intno == 8); // EXCP_EXCEPTION_EXIT: Return from v7M exception.
|
||||
TEST_CHECK((r_pc | 1) == 0xFFFFFFFD);
|
||||
OK(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
static void test_arm_m_exc_return()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x6f\xf0\x02\x00\x00\x47"; // mov r0, #0xFFFFFFFD; bx r0;
|
||||
int r_ipsr;
|
||||
int r_sp = 0x8000;
|
||||
uc_hook hook;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS, code,
|
||||
sizeof(code) - 1, UC_CPU_ARM_CORTEX_A15);
|
||||
OK(uc_mem_map(uc, r_sp - 0x1000, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_hook_add(uc, &hook, UC_HOOK_INTR,
|
||||
test_arm_m_exc_return_hook_interrupt, NULL, 0, 0));
|
||||
|
||||
r_sp -= 0x1c;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
r_ipsr = 16; // We are in whatever exception.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_IPSR, &r_ipsr));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0,
|
||||
2)); // Just execute 2 instructions.
|
||||
|
||||
OK(uc_hook_del(uc, hook));
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
// For details, see https://github.com/unicorn-engine/unicorn/issues/1494.
|
||||
static void test_arm_und32_to_svc32()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// # MVN r0, #0
|
||||
// # MOVS pc, lr
|
||||
// # MVN r0, #0
|
||||
// # MVN r0, #0
|
||||
char code[] =
|
||||
"\x00\x00\xe0\xe3\x0e\xf0\xb0\xe1\x00\x00\xe0\xe3\x00\x00\xe0\xe3";
|
||||
int r_cpsr, r_sp, r_spsr, r_lr;
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc));
|
||||
OK(uc_ctl_set_cpu_model(uc, UC_CPU_ARM_CORTEX_A9));
|
||||
|
||||
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, code_start, code, sizeof(code) - 1));
|
||||
|
||||
// https://www.keil.com/pack/doc/CMSIS/Core_A/html/group__CMSIS__CPSR__M.html
|
||||
r_cpsr = 0x40000093; // SVC32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_sp = 0x12345678;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
r_cpsr = 0x4000009b; // UND32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_spsr = 0x40000093; // Save previous CPSR
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SPSR, &r_spsr));
|
||||
r_sp = 0xDEAD0000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
r_lr = code_start + 8;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_LR, &r_lr));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 3));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
|
||||
TEST_CHECK(r_sp == 0x12345678);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_usr32_to_svc32()
|
||||
{
|
||||
uc_engine *uc;
|
||||
int r_cpsr, r_sp, r_spsr, r_lr;
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc));
|
||||
OK(uc_ctl_set_cpu_model(uc, UC_CPU_ARM_CORTEX_A9));
|
||||
|
||||
// https://www.keil.com/pack/doc/CMSIS/Core_A/html/group__CMSIS__CPSR__M.html
|
||||
r_cpsr = 0x40000093; // SVC32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_sp = 0x12345678;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
r_lr = 0x00102220;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_LR, &r_lr));
|
||||
|
||||
r_cpsr = 0x4000009b; // UND32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_spsr = 0x40000093; // Save previous CPSR
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SPSR, &r_spsr));
|
||||
r_sp = 0xDEAD0000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
r_lr = 0x00509998;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_LR, &r_lr));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
TEST_CHECK((r_cpsr & ((1 << 4) - 1)) == 0xb); // We are in UND32
|
||||
|
||||
r_cpsr = 0x40000090; // USR32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_sp = 0x0010000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_sp));
|
||||
r_lr = 0x0001234;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_LR, &r_lr));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
TEST_CHECK((r_cpsr & ((1 << 4) - 1)) == 0); // We are in USR32
|
||||
|
||||
r_cpsr = 0x40000093; // SVC32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
TEST_CHECK((r_cpsr & ((1 << 4) - 1)) == 3); // We are in SVC32
|
||||
TEST_CHECK(r_sp == 0x12345678);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_v8()
|
||||
{
|
||||
char code[] = "\xd0\xe8\xff\x17"; // LDAEXD.W R1, [R0]
|
||||
uc_engine *uc;
|
||||
uint32_t r_r1 = 0xdeadbeef;
|
||||
uint32_t r_r0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_M33);
|
||||
|
||||
r_r0 = 0x8000;
|
||||
OK(uc_mem_map(uc, r_r0, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, r_r0, (void *)&r_r1, 4));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r1 == 0xdeadbeef);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_thumb_smlabb()
|
||||
{
|
||||
char code[] = "\x13\xfb\x01\x23";
|
||||
uint32_t r_r1, r_r2, r_r3;
|
||||
uc_engine *uc;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_M7);
|
||||
|
||||
r_r3 = 5;
|
||||
r_r1 = 7;
|
||||
r_r2 = 9;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R3, &r_r3));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R1, &r_r1));
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R2, &r_r2));
|
||||
|
||||
OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R3, &r_r3));
|
||||
|
||||
TEST_CHECK(r_r3 == 5 * 7 + 9);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_not_allow_privilege_escalation()
|
||||
{
|
||||
uc_engine *uc;
|
||||
int r_cpsr, r_sp, r_spsr, r_lr;
|
||||
// E3C6601F : BIC r6, r6, #&1F
|
||||
// E3866013 : ORR r6, r6, #&13
|
||||
// E121F006 : MSR cpsr_c, r6 ; switch to SVC32 (should be ineffective
|
||||
// from USR32)
|
||||
// E1A00000 : MOV r0,r0 EF000011 : SWI OS_Exit
|
||||
char code[] = "\x1f\x60\xc6\xe3\x13\x60\x86\xe3\x06\xf0\x21\xe1\x00\x00\xa0"
|
||||
"\xe1\x11\x00\x00\xef";
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_A15);
|
||||
|
||||
// https://www.keil.com/pack/doc/CMSIS/Core_A/html/group__CMSIS__CPSR.html
|
||||
r_cpsr = 0x40000013; // SVC32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_spsr = 0x40000013;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SPSR, &r_spsr));
|
||||
r_sp = 0x12345678;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
r_lr = 0x00102220;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_LR, &r_lr));
|
||||
|
||||
r_cpsr = 0x40000010; // USR32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_sp = 0x0010000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SP, &r_sp));
|
||||
r_lr = 0x0001234;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_LR, &r_lr));
|
||||
|
||||
uc_assert_err(
|
||||
UC_ERR_EXCEPTION,
|
||||
uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_SP, &r_sp));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_LR, &r_lr));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
|
||||
TEST_CHECK((r_cpsr & ((1 << 4) - 1)) == 0); // Stay in USR32
|
||||
TEST_CHECK(r_lr == 0x1234);
|
||||
TEST_CHECK(r_sp == 0x10000);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_mrc()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// mrc p15, #0, r0, c1, c1, #0
|
||||
char code[] = "\x11\x0F\x11\xEE";
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_MAX);
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm_hflags_rebuilt()
|
||||
{
|
||||
// MRS r6, apsr
|
||||
// BIC r6, r6, #&1F
|
||||
// ORR r6, r6, #&10
|
||||
// MSR cpsr_c, r6
|
||||
// SWI OS_EnterOS
|
||||
// MSR cpsr_c, r6
|
||||
char code[] = "\x00\x60\x0f\xe1\x1f\x60\xc6\xe3\x10\x60\x86\xe3\x06\xf0\x21"
|
||||
"\xe1\x16\x00\x02\xef\x06\xf0\x21\xe1";
|
||||
uc_engine *uc;
|
||||
uint32_t r_cpsr, r_spsr, r_r13, r_r14, r_pc;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_A9);
|
||||
|
||||
r_cpsr = 0x40000013; // SVC32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_spsr = 0x40000013;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_SPSR, &r_spsr));
|
||||
r_r13 = 0x12345678; // SP
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_r13));
|
||||
r_r14 = 0x00102220; // LR
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R14, &r_r14));
|
||||
|
||||
r_cpsr = 0x40000010; // USR32
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_r13 = 0x0010000; // SP
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_r13));
|
||||
r_r14 = 0x0001234; // LR
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R14, &r_r14));
|
||||
|
||||
uc_assert_err(
|
||||
UC_ERR_EXCEPTION,
|
||||
uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
r_cpsr = 0x60000013;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_cpsr = 0x60000010;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
r_cpsr = 0x60000013;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
|
||||
OK(uc_emu_start(uc, r_pc, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_CPSR, &r_cpsr));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_r13));
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R14, &r_r14));
|
||||
|
||||
TEST_CHECK(r_cpsr == 0x60000010);
|
||||
TEST_CHECK(r_r13 == 0x00010000);
|
||||
TEST_CHECK(r_r14 == 0x00001234);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static bool test_arm_mem_access_abort_hook_mem(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t addr, int size,
|
||||
int64_t val, void *data)
|
||||
{
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, data));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool test_arm_mem_access_abort_hook_insn_invalid(uc_engine *uc,
|
||||
void *data)
|
||||
{
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, data));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void test_arm_mem_access_abort()
|
||||
{
|
||||
// LDR r0, [r0]
|
||||
// Undefined instruction
|
||||
char code[] = "\x00\x00\x90\xe5\x00\xa0\xf0\xf7";
|
||||
uc_engine *uc;
|
||||
uint32_t r_pc, r_r0, r_pc_in_hook;
|
||||
uc_hook hk, hkk;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_ARM_CORTEX_A9);
|
||||
|
||||
r_r0 = 0x990000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
|
||||
|
||||
OK(uc_hook_add(uc, &hk,
|
||||
UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED |
|
||||
UC_HOOK_MEM_FETCH_UNMAPPED,
|
||||
test_arm_mem_access_abort_hook_mem, (void *)&r_pc_in_hook, 1,
|
||||
0));
|
||||
OK(uc_hook_add(uc, &hkk, UC_HOOK_INSN_INVALID,
|
||||
test_arm_mem_access_abort_hook_insn_invalid,
|
||||
(void *)&r_pc_in_hook, 1, 0));
|
||||
|
||||
uc_assert_err(UC_ERR_READ_UNMAPPED,
|
||||
uc_emu_start(uc, code_start, code_start + 4, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_pc == r_pc_in_hook);
|
||||
|
||||
uc_assert_err(UC_ERR_INSN_INVALID,
|
||||
uc_emu_start(uc, code_start + 4, code_start + 8, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_pc == r_pc_in_hook);
|
||||
|
||||
uc_assert_err(UC_ERR_FETCH_UNMAPPED,
|
||||
uc_emu_start(uc, 0x900000, 0x900000 + 8, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_pc == r_pc_in_hook);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_arm_nop", test_arm_nop},
|
||||
{"test_arm_thumb_sub", test_arm_thumb_sub},
|
||||
{"test_armeb_sub", test_armeb_sub},
|
||||
{"test_arm_thumbeb_sub", test_arm_thumbeb_sub},
|
||||
{"test_arm_thumb_ite", test_arm_thumb_ite},
|
||||
{"test_arm_m_thumb_mrs", test_arm_m_thumb_mrs},
|
||||
{"test_arm_m_control", test_arm_m_control},
|
||||
{"test_arm_m_exc_return", test_arm_m_exc_return},
|
||||
{"test_arm_und32_to_svc32", test_arm_und32_to_svc32},
|
||||
{"test_arm_usr32_to_svc32", test_arm_usr32_to_svc32},
|
||||
{"test_arm_v8", test_arm_v8},
|
||||
{"test_arm_thumb_smlabb", test_arm_thumb_smlabb},
|
||||
{"test_arm_not_allow_privilege_escalation",
|
||||
test_arm_not_allow_privilege_escalation},
|
||||
{"test_arm_mrc", test_arm_mrc},
|
||||
{"test_arm_hflags_rebuilt", test_arm_hflags_rebuilt},
|
||||
{"test_arm_mem_access_abort", test_arm_mem_access_abort},
|
||||
{NULL, NULL}};
|
||||
145
tests/unit/test_arm64.c
Normal file
145
tests/unit/test_arm64.c
Normal file
@@ -0,0 +1,145 @@
|
||||
#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, uc_cpu_arm cpu)
|
||||
{
|
||||
OK(uc_open(arch, mode, uc));
|
||||
OK(uc_ctl_set_cpu_model(*uc, cpu));
|
||||
OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL));
|
||||
OK(uc_mem_write(*uc, code_start, code, size));
|
||||
}
|
||||
|
||||
static void test_arm64_until()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x30\x00\x80\xd2\x11\x04\x80\xd2\x9c\x23\x00\x91";
|
||||
|
||||
/*
|
||||
mov x16, #1
|
||||
mov x17, #0x20
|
||||
add x28, x28, 8
|
||||
*/
|
||||
|
||||
uint64_t r_x16 = 0x12341234;
|
||||
uint64_t r_x17 = 0x78907890;
|
||||
uint64_t r_pc = 0x00000000;
|
||||
uint64_t r_x28 = 0x12341234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM64, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_AARCH64_A72);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X16, &r_x16));
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X17, &r_x17));
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X28, &r_x28));
|
||||
|
||||
// emulate the three instructions
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 3));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X16, &r_x16));
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X17, &r_x17));
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X28, &r_x28));
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_x16 == 0x1);
|
||||
TEST_CHECK(r_x17 == 0x20);
|
||||
TEST_CHECK(r_x28 == 0x1234123c);
|
||||
TEST_CHECK(r_pc == (code_start + sizeof(code) - 1));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm64_code_patching()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x00\x04\x00\x11"; // add w0, w0, 0x1
|
||||
uc_common_setup(&uc, UC_ARCH_ARM64, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_AARCH64_A72);
|
||||
// zero out x0
|
||||
uint64_t r_x0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
TEST_CHECK(r_x0 == 0x1);
|
||||
// patch instruction
|
||||
char patch_code[] = "\x00\xfc\x1f\x11"; // add w0, w0, 0x7FF
|
||||
OK(uc_mem_write(uc, code_start, patch_code, sizeof(patch_code) - 1));
|
||||
// zero out x0
|
||||
r_x0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(patch_code) - 1, 0, 0));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
TEST_CHECK(r_x0 != 0x1);
|
||||
TEST_CHECK(r_x0 == 0x7ff);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
// Need to flush the cache before running the emulation after patching
|
||||
static void test_arm64_code_patching_count()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x00\x04\x00\x11"; // add w0, w0, 0x1
|
||||
uc_common_setup(&uc, UC_ARCH_ARM64, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_AARCH64_A72);
|
||||
// zero out x0
|
||||
uint64_t r_x0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
TEST_CHECK(r_x0 == 0x1);
|
||||
// patch instruction
|
||||
char patch_code[] = "\x00\xfc\x1f\x11"; // add w0, w0, 0x7FF
|
||||
OK(uc_mem_write(uc, code_start, patch_code, sizeof(patch_code) - 1));
|
||||
OK(uc_ctl_remove_cache(uc, code_start,
|
||||
code_start + sizeof(patch_code) - 1));
|
||||
// zero out x0
|
||||
r_x0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_ARM64_REG_X0, &r_x0));
|
||||
TEST_CHECK(r_x0 != 0x1);
|
||||
TEST_CHECK(r_x0 == 0x7ff);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_arm64_v8_pac()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x28\xfd\xea\xc8"; // casal x10, x8, [x9]
|
||||
uint64_t r_x9, r_x8, mem;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_ARM64, UC_MODE_ARM, code, sizeof(code) - 1,
|
||||
UC_CPU_AARCH64_MAX);
|
||||
|
||||
OK(uc_mem_map(uc, 0x40000, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, 0x40000, "\x00\x00\x00\x00\x00\x00\x00\x00", 8));
|
||||
r_x9 = 0x40000;
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X9, &r_x9));
|
||||
r_x8 = 0xdeadbeafdeadbeaf;
|
||||
OK(uc_reg_write(uc, UC_ARM64_REG_X8, &r_x8));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_mem_read(uc, 0x40000, (void *)&mem, 8));
|
||||
|
||||
TEST_CHECK(mem == r_x8);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_arm64_until", test_arm64_until},
|
||||
{"test_arm64_code_patching", test_arm64_code_patching},
|
||||
{"test_arm64_code_patching_count", test_arm64_code_patching_count},
|
||||
{"test_arm64_v8_pac", test_arm64_v8_pac},
|
||||
{NULL, NULL}};
|
||||
236
tests/unit/test_ctl.c
Normal file
236
tests/unit/test_ctl.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "unicorn_test.h"
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
// We have to copy this for Android.
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
#define NANOSECONDS_PER_SECOND 1000000000LL
|
||||
|
||||
static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||
{
|
||||
union {
|
||||
uint64_t ll;
|
||||
struct {
|
||||
uint32_t low, high;
|
||||
} l;
|
||||
} u, res;
|
||||
uint64_t rl, rh;
|
||||
|
||||
u.ll = a;
|
||||
rl = (uint64_t)u.l.low * (uint64_t)b;
|
||||
rh = (uint64_t)u.l.high * (uint64_t)b;
|
||||
rh += (rl >> 32);
|
||||
res.l.high = rh / c;
|
||||
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
|
||||
return res.ll;
|
||||
}
|
||||
|
||||
static int64_t get_freq(void)
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
int ret = QueryPerformanceFrequency(&freq);
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "Could not calibrate ticks\n");
|
||||
exit(1);
|
||||
}
|
||||
return freq.QuadPart;
|
||||
}
|
||||
|
||||
static inline int64_t get_clock_realtime(void)
|
||||
{
|
||||
LARGE_INTEGER ti;
|
||||
QueryPerformanceCounter(&ti);
|
||||
return muldiv64(ti.QuadPart, NANOSECONDS_PER_SECOND, get_freq());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
/* get host real time in nanosecond */
|
||||
static inline int64_t get_clock_realtime(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
#define GEN_SIMPLE_READ_TEST(field, ctl_type, arg_type, expected) \
|
||||
static void test_uc_ctl_##field() \
|
||||
{ \
|
||||
uc_engine *uc; \
|
||||
arg_type arg; \
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc)); \
|
||||
OK(uc_ctl(uc, UC_CTL_READ(ctl_type, 1), &arg)); \
|
||||
TEST_CHECK(arg == expected); \
|
||||
OK(uc_close(uc)); \
|
||||
}
|
||||
|
||||
GEN_SIMPLE_READ_TEST(mode, UC_CTL_UC_MODE, int, 4)
|
||||
GEN_SIMPLE_READ_TEST(arch, UC_CTL_UC_ARCH, int, 4)
|
||||
GEN_SIMPLE_READ_TEST(page_size, UC_CTL_UC_PAGE_SIZE, uint32_t, 4096)
|
||||
GEN_SIMPLE_READ_TEST(time_out, UC_CTL_UC_TIMEOUT, uint64_t, 0)
|
||||
|
||||
static void test_uc_ctl_exits()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// cmp eax, 0;
|
||||
// jg lb;
|
||||
// inc eax;
|
||||
// nop; <---- exit1
|
||||
// lb:
|
||||
// inc ebx;
|
||||
// nop; <---- exit2
|
||||
char code[] = "\x83\xf8\x00\x7f\x02\x40\x90\x43\x90";
|
||||
int r_eax;
|
||||
int r_ebx;
|
||||
uint64_t exits[] = {code_start + 6, code_start + 8};
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1);
|
||||
OK(uc_ctl_exits_enable(uc));
|
||||
OK(uc_ctl_set_exits(uc, exits, 2));
|
||||
r_eax = 0;
|
||||
r_ebx = 0;
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_eax));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EAX, &r_ebx));
|
||||
|
||||
// Run two times.
|
||||
OK(uc_emu_start(uc, code_start, 0, 0, 0));
|
||||
OK(uc_emu_start(uc, code_start, 0, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_X86_REG_EAX, &r_eax));
|
||||
OK(uc_reg_read(uc, UC_X86_REG_EAX, &r_ebx));
|
||||
|
||||
TEST_CHECK(r_eax == 1);
|
||||
TEST_CHECK(r_ebx == 1);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
double time_emulation(uc_engine *uc, uint64_t start, uint64_t end)
|
||||
{
|
||||
int64_t t1, t2;
|
||||
|
||||
t1 = get_clock_realtime();
|
||||
|
||||
OK(uc_emu_start(uc, start, end, 0, 0));
|
||||
|
||||
t2 = get_clock_realtime();
|
||||
|
||||
return t2 - t1;
|
||||
}
|
||||
|
||||
#define TB_COUNT (8)
|
||||
#define TCG_MAX_INSNS (512) // from tcg.h
|
||||
#define CODE_LEN TB_COUNT *TCG_MAX_INSNS
|
||||
|
||||
static void test_uc_ctl_tb_cache()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[CODE_LEN];
|
||||
double standard, cached, evicted;
|
||||
|
||||
memset(code, 0x90, CODE_LEN);
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_32, code, sizeof(code) - 1);
|
||||
|
||||
standard = time_emulation(uc, code_start, code_start + sizeof(code) - 1);
|
||||
|
||||
for (int i = 0; i < TB_COUNT; i++) {
|
||||
OK(uc_ctl_request_cache(uc, code_start + i * TCG_MAX_INSNS, NULL));
|
||||
}
|
||||
|
||||
cached = time_emulation(uc, code_start, code_start + sizeof(code) - 1);
|
||||
|
||||
for (int i = 0; i < TB_COUNT; i++) {
|
||||
OK(uc_ctl_remove_cache(uc, code_start + i * TCG_MAX_INSNS,
|
||||
code_start + i * TCG_MAX_INSNS + 1));
|
||||
}
|
||||
evicted = time_emulation(uc, code_start, code_start + sizeof(code) - 1);
|
||||
|
||||
// In fact, evicted is also slightly faster than standard but we don't do
|
||||
// this guarantee.
|
||||
TEST_CHECK(cached < standard);
|
||||
TEST_CHECK(evicted > cached);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_uc_ctl_change_page_size()
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_engine *uc2;
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc));
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc2));
|
||||
|
||||
OK(uc_ctl_set_page_size(uc, 4096));
|
||||
|
||||
OK(uc_mem_map(uc2, 1 << 10, 1 << 10, UC_PROT_ALL));
|
||||
uc_assert_err(UC_ERR_ARG, uc_mem_map(uc, 1 << 10, 1 << 10, UC_PROT_ALL));
|
||||
|
||||
OK(uc_close(uc));
|
||||
OK(uc_close(uc2));
|
||||
}
|
||||
|
||||
// Copy from test_arm.c but with new API.
|
||||
static void test_uc_ctl_arm_cpu()
|
||||
{
|
||||
uc_engine *uc;
|
||||
int r_control, r_msp, r_psp;
|
||||
|
||||
OK(uc_open(UC_ARCH_ARM, UC_MODE_THUMB, &uc));
|
||||
|
||||
OK(uc_ctl_set_cpu_model(uc, UC_CPU_ARM_CORTEX_M7));
|
||||
|
||||
r_control = 0; // Make sure we are using MSP.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
r_msp = 0x1000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_msp));
|
||||
|
||||
r_control = 0b10; // Make the switch.
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_psp));
|
||||
TEST_CHECK(r_psp != r_msp);
|
||||
|
||||
r_psp = 0x2000;
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_R13, &r_psp));
|
||||
|
||||
r_control = 0; // Switch again
|
||||
OK(uc_reg_write(uc, UC_ARM_REG_CONTROL, &r_control));
|
||||
|
||||
OK(uc_reg_read(uc, UC_ARM_REG_R13, &r_msp));
|
||||
TEST_CHECK(r_psp != r_msp);
|
||||
TEST_CHECK(r_msp == 0x1000);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_uc_ctl_mode", test_uc_ctl_mode},
|
||||
{"test_uc_ctl_page_size", test_uc_ctl_page_size},
|
||||
{"test_uc_ctl_arch", test_uc_ctl_arch},
|
||||
{"test_uc_ctl_time_out", test_uc_ctl_time_out},
|
||||
{"test_uc_ctl_exits", test_uc_ctl_exits},
|
||||
{"test_uc_ctl_tb_cache", test_uc_ctl_tb_cache},
|
||||
{"test_uc_ctl_change_page_size", test_uc_ctl_change_page_size},
|
||||
{"test_uc_ctl_arm_cpu", test_uc_ctl_arm_cpu},
|
||||
{NULL, NULL}};
|
||||
@@ -1,127 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void test_idt_gdt_i386(/*void **state*/)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
uint8_t buf[6];
|
||||
uc_x86_mmr idt;
|
||||
uc_x86_mmr gdt;
|
||||
uc_x86_mmr ldt;
|
||||
uc_x86_mmr tr;
|
||||
|
||||
struct stat info;
|
||||
char * code = read_file("gdt_idx.bin", &info);
|
||||
|
||||
const uint64_t address = 0x1000000;
|
||||
|
||||
int r_esp = address + 0x1000 - 0x100; // initial esp
|
||||
|
||||
idt.base = 0x12345678;
|
||||
idt.limit = 0xabcd;
|
||||
gdt.base = 0x87654321;
|
||||
gdt.limit = 0xdcba;
|
||||
|
||||
ldt.base = 0xfedcba98;
|
||||
ldt.limit = 0x11111111;
|
||||
ldt.selector = 0x3333;
|
||||
ldt.flags = 0x55555555;
|
||||
|
||||
tr.base = 0x22222222;
|
||||
tr.limit = 0x33333333;
|
||||
tr.selector = 0x4444;
|
||||
tr.flags = 0x66666666;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
// map 1 page memory for this emulation
|
||||
err = uc_mem_map(uc, address, 0x1000, UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
err = uc_mem_write(uc, address, code, info.st_size);
|
||||
uc_assert_success(err);
|
||||
|
||||
// initialize machine registers
|
||||
err = uc_reg_write(uc, UC_X86_REG_ESP, &r_esp);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_IDTR, &idt);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_GDTR, &gdt);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_LDTR, &ldt);
|
||||
uc_assert_success(err);
|
||||
err = uc_reg_write(uc, UC_X86_REG_TR, &tr);
|
||||
uc_assert_success(err);
|
||||
|
||||
memset(&idt, 0, sizeof(idt));
|
||||
memset(&gdt, 0, sizeof(gdt));
|
||||
memset(&ldt, 0, sizeof(ldt));
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
err = uc_emu_start(uc, address, address+sizeof(code)-1, 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_IDTR, &idt);
|
||||
assert(idt.base == 0x12345678);
|
||||
assert(idt.limit == 0xabcd);
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_GDTR, &gdt);
|
||||
assert(gdt.base == 0x87654321);
|
||||
assert(gdt.limit == 0xdcba);
|
||||
|
||||
//userspace can only set ldt selector, remainder are loaded from
|
||||
//GDT/LDT, but we allow all to emulator user
|
||||
uc_reg_read(uc, UC_X86_REG_LDTR, &ldt);
|
||||
assert(ldt.base == 0xfedcba98);
|
||||
assert(ldt.limit == 0x11111111);
|
||||
assert(ldt.selector == 0x3333);
|
||||
assert(ldt.flags == 0x55555555);
|
||||
|
||||
//userspace can only set tr selector, remainder are loaded from
|
||||
//GDT/LDT, but we allow all to emulator user
|
||||
uc_reg_read(uc, UC_X86_REG_TR, &tr);
|
||||
assert(tr.base == 0x22222222);
|
||||
assert(tr.limit == 0x33333333);
|
||||
assert(tr.selector == 0x4444);
|
||||
assert(tr.flags == 0x66666666);
|
||||
|
||||
// read from memory
|
||||
err = uc_mem_read(uc, r_esp, buf, 6);
|
||||
uc_assert_success(err);
|
||||
|
||||
assert(memcmp(buf, "\xcd\xab\x78\x56\x34\x12", 6) == 0);
|
||||
|
||||
// read from memory
|
||||
err = uc_mem_read(uc, r_esp + 6, buf, 6);
|
||||
uc_assert_success(err);
|
||||
|
||||
assert(memcmp(buf, "\xba\xdc\x21\x43\x65\x87", 6) == 0);
|
||||
|
||||
uc_close(uc);
|
||||
free(code);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
/*
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_idt_gdt_i386)
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
*/
|
||||
test_idt_gdt_i386();
|
||||
|
||||
fprintf(stderr, "success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
refer to issue #575.
|
||||
to run correctly unicorn needs to be compiled for AArch64.
|
||||
*/
|
||||
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
uint64_t trunc_page(uint64_t addr)
|
||||
{
|
||||
return (addr & ~(4095));
|
||||
}
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int init(void **state)
|
||||
{
|
||||
printf("[+] Initializing Unicorn...\n");
|
||||
uc_engine *uc;
|
||||
|
||||
if (uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc) != UC_ERR_OK) {
|
||||
printf("Error on open. Be sure that your unicorn library supports AArch64.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*state = uc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
printf("[+] Exiting...\n");
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_close(uc);
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_hang(void **state)
|
||||
{
|
||||
uint32_t code[] = {
|
||||
0xd503201f, /* NOP */
|
||||
0xd503201f, /* NOP */
|
||||
0xd503201f, /* NOP */
|
||||
0xaa0103e0 /* MOV X0, X1 */
|
||||
};
|
||||
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint64_t x0 = 0;
|
||||
uint64_t x1 = 1;
|
||||
|
||||
/*
|
||||
* emulation will hang if some instruction hits every quarter of a page,
|
||||
* i.e. these offsets:
|
||||
* 0x1400, 0x1800, 0x1c00, 0x2000
|
||||
*
|
||||
* in this test, the code to be emulated is mapped just before the 0x1400
|
||||
* offset, so that the final instruction emulated (MOV X0, X1) hits the offset,
|
||||
* causing the hang.
|
||||
* If you try to write the code just four bytes behind, the hang doesn't occur.
|
||||
*
|
||||
* So far, this strange behaviour has only been observed with AArch64 Unicorn APIs.
|
||||
*/
|
||||
|
||||
uint64_t addr = 0x13f0; // try to map at (0x13f0 - 0x4) and the hang doesn't occur
|
||||
uint64_t trunc_addr = trunc_page(addr); // round down to nearest page
|
||||
|
||||
uc_mem_map(uc, trunc_addr, 2 * 1024 * 1024, UC_PROT_ALL);
|
||||
|
||||
if (uc_mem_write(uc, addr, &code, sizeof(code))) {
|
||||
printf("error on write\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uc_reg_write(uc, UC_ARM64_REG_X0, &x0);
|
||||
uc_reg_write(uc, UC_ARM64_REG_X1, &x1);
|
||||
|
||||
if (uc_emu_start(uc, addr, addr + sizeof(code), 0, 0)) {
|
||||
printf("error on start\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uc_reg_read(uc, UC_ARM64_REG_X0, &x0);
|
||||
uc_reg_read(uc, UC_ARM64_REG_X1, &x1);
|
||||
|
||||
printf("x0: %"PRIx64"\n", x0);
|
||||
printf("x1: %"PRIx64"\n", x1);
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_hang, init, teardown),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);;
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
// Test hook evocation count
|
||||
//
|
||||
// Objective is to demonstrate finer duration control of
|
||||
// emulation by counts of instruction code
|
||||
//
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
volatile int expected_instructions = 0;
|
||||
volatile int total_instructions = 0;
|
||||
|
||||
|
||||
// NOTE: It would appear that this UC_HOOK_CODE is being done before the
|
||||
// uc_count_fb hook.
|
||||
// So, termination by uc->emu_count has not been done yet here...
|
||||
static void test_code_hook(uc_engine *uc,
|
||||
uint64_t address,
|
||||
uint32_t size,
|
||||
void *user_data)
|
||||
{
|
||||
|
||||
++total_instructions;
|
||||
if (total_instructions == expected_instructions)
|
||||
{
|
||||
uc_emu_stop(uc);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("instruction at 0x%"PRIx64": ", address);
|
||||
uint8_t tmp[256];
|
||||
if (!uc_mem_read(uc, address, tmp, size)) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("0x%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup32(void **state)
|
||||
{
|
||||
uc_hook trace1;
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
|
||||
// trace all instructions
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
OK(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void
|
||||
test_hook_count(uc_engine *uc,
|
||||
const uint8_t *code,
|
||||
int start_offset,
|
||||
int code_length,
|
||||
int count)
|
||||
{
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
#define MEMSIZE (2 * 1024 * 1024)
|
||||
|
||||
uint64_t address = BASEADDR + (count * MEMSIZE);
|
||||
total_instructions = 0;
|
||||
|
||||
#undef BASEADDR
|
||||
|
||||
// map a new 2MB memory for this emulation
|
||||
OK(uc_mem_map(uc, address, MEMSIZE, UC_PROT_ALL));
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
OK(uc_mem_write(uc, address, code, code_length));
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Address: %"PRIx64"\n", address);
|
||||
printf("Start : %"PRIx64"\n", address + start_offset);
|
||||
printf("End : %"PRIx64"\n", address + code_length - 1);
|
||||
printf("Count : %d\n", count);
|
||||
#endif
|
||||
expected_instructions = count;
|
||||
OK(uc_emu_start(uc,
|
||||
address+start_offset,
|
||||
address+code_length,
|
||||
0,
|
||||
count));
|
||||
|
||||
assert_int_equal(expected_instructions, total_instructions);
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
OK(uc_mem_unmap(uc, address, MEMSIZE));
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control of exactly 1 instruction */
|
||||
/* of 1-opcode code space*/
|
||||
static void test_hook_count_1_begin(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset = 0;
|
||||
int ins_count = 1;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control of exactly 1 instruction */
|
||||
static void test_hook_count_1_midpoint(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset = code_length/2;
|
||||
int ins_count = 1;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control of exactly 1 instruction */
|
||||
static void test_hook_count_1_end(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset = code_length - 1;
|
||||
int ins_count = 1;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
/* Perform fine-grain emulation control over a range of */
|
||||
/* varied instruction steps. */
|
||||
static void test_hook_count_range(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int start_offset;
|
||||
int ins_count = 2;
|
||||
|
||||
for (start_offset = 2; start_offset < (code_length - ins_count); start_offset++)
|
||||
{
|
||||
printf("Iteration %d\n", start_offset);
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_hook_count_end(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int ins_count = 3;
|
||||
int start_offset = sizeof(code) - ins_count;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
static void test_hook_count_begins(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int ins_count = 3;
|
||||
int start_offset = 0;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
static void test_hook_count_midpoint(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint8_t code[] = {
|
||||
0x41, // inc ECX @0x1000000
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX @0x1000003
|
||||
0x41, // inc ECX
|
||||
0x41, // inc ECX
|
||||
0x42, // inc EDX @0x1000006
|
||||
0x42, // inc EDX
|
||||
};
|
||||
int code_length = sizeof(code);
|
||||
int ins_count = 3;
|
||||
int start_offset = 2;
|
||||
|
||||
test_hook_count(uc, code, start_offset, code_length, ins_count);
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_1_begin, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_1_midpoint, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_1_end, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_begins, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_range, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_midpoint, setup32, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_hook_count_end, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
36
tests/unit/test_m68k.c
Normal file
36
tests/unit/test_m68k.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#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,
|
||||
uc_cpu_m68k cpu_model)
|
||||
{
|
||||
OK(uc_open(arch, mode, uc));
|
||||
OK(uc_ctl_set_cpu_model(*uc, cpu_model));
|
||||
OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL));
|
||||
OK(uc_mem_write(*uc, code_start, code, size));
|
||||
}
|
||||
|
||||
static void test_move_to_sr()
|
||||
{
|
||||
|
||||
uc_engine *uc;
|
||||
char code[] = "\x46\xfc\x27\x00"; // move #$2700,sr
|
||||
int r_sr;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_M68K, UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1, UC_CPU_M68K_M68000);
|
||||
OK(uc_reg_read(uc, UC_M68K_REG_SR, &r_sr));
|
||||
|
||||
r_sr = r_sr | 0x2000;
|
||||
|
||||
OK(uc_reg_write(uc, UC_M68K_REG_SR, &r_sr));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_move_to_sr", test_move_to_sr}, {NULL, NULL}};
|
||||
126
tests/unit/test_mem.c
Normal file
126
tests/unit/test_mem.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
static void test_map_correct()
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
OK(uc_mem_map(uc, 0x40000, 0x1000 * 16, UC_PROT_ALL)); // [0x40000, 0x50000]
|
||||
OK(uc_mem_map(uc, 0x60000, 0x1000 * 16, UC_PROT_ALL)); // [0x60000, 0x70000]
|
||||
OK(uc_mem_map(uc, 0x20000, 0x1000 * 16, UC_PROT_ALL)); // [0x20000, 0x30000]
|
||||
uc_assert_err(UC_ERR_MAP,
|
||||
uc_mem_map(uc, 0x10000, 0x2000 * 16, UC_PROT_ALL));
|
||||
uc_assert_err(UC_ERR_MAP,
|
||||
uc_mem_map(uc, 0x25000, 0x1000 * 16, UC_PROT_ALL));
|
||||
uc_assert_err(UC_ERR_MAP,
|
||||
uc_mem_map(uc, 0x35000, 0x1000 * 16, UC_PROT_ALL));
|
||||
uc_assert_err(UC_ERR_MAP,
|
||||
uc_mem_map(uc, 0x45000, 0x1000 * 16, UC_PROT_ALL));
|
||||
uc_assert_err(UC_ERR_MAP,
|
||||
uc_mem_map(uc, 0x55000, 0x2000 * 16, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, 0x35000, 0x5000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, 0x50000, 0x5000, UC_PROT_ALL));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_map_wrapping()
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
uc_assert_err(UC_ERR_ARG, uc_mem_map(uc, (~0ll - 0x4000) & ~0xfff, 0x8000,
|
||||
UC_PROT_ALL));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mem_protect()
|
||||
{
|
||||
uc_engine *qc;
|
||||
int r_eax = 0x2000;
|
||||
int r_esi = 0xdeadbeef;
|
||||
uint32_t mem;
|
||||
// add [eax + 4], esi
|
||||
char code[] = {0x01, 0x70, 0x04};
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &qc));
|
||||
OK(uc_reg_write(qc, UC_X86_REG_EAX, &r_eax));
|
||||
OK(uc_reg_write(qc, UC_X86_REG_ESI, &r_esi));
|
||||
OK(uc_mem_map(qc, 0x1000, 0x1000, UC_PROT_READ | UC_PROT_EXEC));
|
||||
OK(uc_mem_map(qc, 0x2000, 0x1000, UC_PROT_READ));
|
||||
OK(uc_mem_protect(qc, 0x2000, 0x1000, UC_PROT_READ | UC_PROT_WRITE));
|
||||
OK(uc_mem_write(qc, 0x1000, code, sizeof(code)));
|
||||
|
||||
OK(uc_emu_start(qc, 0x1000, 0x1000 + sizeof(code) - 1, 0, 1));
|
||||
OK(uc_mem_read(qc, 0x2000 + 4, &mem, 4));
|
||||
|
||||
TEST_CHECK(mem == 0xdeadbeef);
|
||||
|
||||
OK(uc_close(qc));
|
||||
}
|
||||
|
||||
static void test_splitting_mem_unmap()
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
OK(uc_mem_map(uc, 0x20000, 0x1000, UC_PROT_NONE));
|
||||
OK(uc_mem_map(uc, 0x21000, 0x2000, UC_PROT_NONE));
|
||||
|
||||
OK(uc_mem_unmap(uc, 0x21000, 0x1000));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static uint64_t test_splitting_mmio_unmap_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_splitting_mmio_unmap()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// mov ecx, [0x3004] <-- normal read
|
||||
// mov ebx, [0x4004] <-- mmio read
|
||||
char code[] = "\x8b\x0d\x04\x30\x00\x00\x8b\x1d\x04\x40\x00\x00";
|
||||
int r_ecx, r_ebx;
|
||||
int bytes = 0xdeadbeef;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, 0x1000, code, sizeof(code) - 1));
|
||||
|
||||
OK(uc_mmio_map(uc, 0x3000, 0x2000, test_splitting_mmio_unmap_read_callback,
|
||||
NULL, NULL, NULL));
|
||||
|
||||
// Map a ram area instead
|
||||
OK(uc_mem_unmap(uc, 0x3000, 0x1000));
|
||||
OK(uc_mem_map(uc, 0x3000, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, 0x3004, &bytes, 4));
|
||||
|
||||
OK(uc_emu_start(uc, 0x1000, 0x1000 + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx));
|
||||
OK(uc_reg_read(uc, UC_X86_REG_EBX, &r_ebx));
|
||||
|
||||
TEST_CHECK(r_ecx == 0xdeadbeef);
|
||||
TEST_CHECK(r_ebx == 0x19260817);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_map_correct", test_map_correct},
|
||||
{"test_map_wrapping", test_map_wrapping},
|
||||
{"test_mem_protect", test_mem_protect},
|
||||
{"test_splitting_mem_unmap", test_splitting_mem_unmap},
|
||||
{"test_splitting_mmio_unmap", test_splitting_mmio_unmap},
|
||||
{NULL, NULL}};
|
||||
@@ -1,131 +0,0 @@
|
||||
/**
|
||||
* Unicorn memory API tests
|
||||
*
|
||||
* This tests memory read/write and map/unmap functionality.
|
||||
* One is necessary for doing the other.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// mapping the last pages will silently fail
|
||||
static void test_last_page_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint8_t writebuf[0x10];
|
||||
memset(writebuf, 0xCC, sizeof(writebuf));
|
||||
|
||||
const uint64_t mem_len = 0x1000;
|
||||
const uint64_t last_page = 0xfffffffffffff000;
|
||||
uc_assert_success(uc_mem_map(uc, last_page, mem_len, UC_PROT_NONE));
|
||||
uc_assert_success(uc_mem_write(uc, last_page, writebuf, sizeof(writebuf)));
|
||||
}
|
||||
|
||||
// segfaults with NULL-deref (caused by UC_PROT_NONE)
|
||||
static void test_nullptr_deref_wrong_perms(void **state){
|
||||
uc_engine *uc = *state;
|
||||
const uint64_t base_addr = 0x400000;
|
||||
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_NONE));
|
||||
uc_emu_start(uc, base_addr, base_addr + 1, 0, 0);
|
||||
}
|
||||
|
||||
static int number_of_memory_reads = 0;
|
||||
|
||||
static void hook_mem64(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
number_of_memory_reads += 1;
|
||||
printf(">>> Memory is being accessed at 0x%"PRIx64 ", data size = %u\n", address, size);
|
||||
}
|
||||
|
||||
//if a read is performed from a big address whith a non-zero last digit, multiple read events are triggered
|
||||
static void test_high_address_reads(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace2;
|
||||
|
||||
uint64_t addr = 0x0010000000000001;
|
||||
//addr = 0x0010000000000000; // uncomment to fix wrong? behaviour
|
||||
//addr = 90000000; // uncomment to fix wrong? behaviour
|
||||
//
|
||||
uc_mem_map(uc, addr-(addr%4096), 4096*2, UC_PROT_ALL);
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr));
|
||||
const uint64_t base_addr = 0x40000;
|
||||
uint8_t code[] = {0x48,0x8b,0x00,0x90,0x90,0x90,0x90}; // mov rax, [rax], nops
|
||||
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, base_addr, code, 7));
|
||||
uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0));
|
||||
uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0));
|
||||
if(number_of_memory_reads != 1) {
|
||||
fail_msg("wrong number of memory reads for instruction %i", number_of_memory_reads);
|
||||
}
|
||||
}
|
||||
|
||||
//if a read is performed from a big address whith a non-zero last digit, 0 will be read
|
||||
static void test_high_address_read_values(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
struct stat info;
|
||||
char * code = read_file("high_address.bin", &info);
|
||||
if (code == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t addr = 0x0010000000000001;
|
||||
//addr = 0x000ffffffffffff8; // uncomment to fix wrong behaviour
|
||||
//addr = 90000000; // uncomment to fix wrong behaviour
|
||||
//
|
||||
uint8_t content[] = {0x42,0x42,0x42,0x42, 0x42,0x42,0x42,0x42};
|
||||
uc_assert_success(uc_mem_map(uc, addr-(addr%4096), 4096*2, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, addr, content, 8));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_RAX, &addr));
|
||||
const uint64_t base_addr = 0x40000;
|
||||
uc_assert_success(uc_mem_map(uc, base_addr, 4096, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, base_addr, code, info.st_size));
|
||||
uc_assert_success(uc_emu_start(uc, base_addr, base_addr + 3, 0, 0));
|
||||
uint64_t rax = 0;
|
||||
uc_assert_success(uc_reg_read(uc, UC_X86_REG_RAX, &rax));
|
||||
if(rax != 0x4242424242424242) {
|
||||
fail_msg("wrong memory read from code %"PRIx64, rax);
|
||||
}
|
||||
|
||||
free(code);
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_last_page_map),
|
||||
test(test_high_address_reads),
|
||||
test(test_high_address_read_values),
|
||||
test(test_nullptr_deref_wrong_perms),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
/**
|
||||
* Unicorn memory API tests
|
||||
*
|
||||
* This tests memory read/write and map/unmap functionality.
|
||||
* One is necessary for doing the other.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* A basic test showing mapping of memory, and reading/writing it
|
||||
*/
|
||||
static void test_basic(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint64_t mem_start = 0x1000;
|
||||
const uint64_t mem_len = 0x1000;
|
||||
const uint64_t test_addr = mem_start + 0x100;
|
||||
|
||||
/* Map a region */
|
||||
uc_assert_success(uc_mem_map(uc, mem_start, mem_len, UC_PROT_NONE));
|
||||
|
||||
/* Write some data to it */
|
||||
uc_assert_success(uc_mem_write(uc, test_addr, "test", 4));
|
||||
|
||||
uint8_t buf[4];
|
||||
memset(buf, 0xCC, sizeof(buf));
|
||||
|
||||
/* Read it back */
|
||||
uc_assert_success(uc_mem_read(uc, test_addr, buf, sizeof(buf)));
|
||||
|
||||
/* And make sure it matches what we expect */
|
||||
assert_memory_equal(buf, "test", 4);
|
||||
|
||||
/* Unmap the region */
|
||||
//uc_assert_success(uc_mem_unmap(uc, mem_start, mem_len));
|
||||
}
|
||||
|
||||
static void test_bad_read(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint8_t readbuf[0x10];
|
||||
memset(readbuf, 0xCC, sizeof(readbuf));
|
||||
|
||||
uint8_t checkbuf[0x10];
|
||||
memset(checkbuf, 0xCC, sizeof(checkbuf));
|
||||
|
||||
/* Reads to unmapped addresses should fail */
|
||||
/* TODO: Which error? */
|
||||
uc_assert_fail(uc_mem_read(uc, 0x1000, readbuf, sizeof(readbuf)));
|
||||
|
||||
/* And our buffer should be unchanged */
|
||||
assert_memory_equal(readbuf, checkbuf, sizeof(checkbuf));
|
||||
}
|
||||
|
||||
static void test_bad_write(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uint8_t writebuf[0x10];
|
||||
memset(writebuf, 0xCC, sizeof(writebuf));
|
||||
|
||||
/* Writes to unmapped addresses should fail */
|
||||
/* TODO: Which error? */
|
||||
uc_assert_fail(uc_mem_write(uc, 0x1000, writebuf, sizeof(writebuf)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Verify that we can read/write across memory map region boundaries
|
||||
*/
|
||||
static void test_rw_across_boundaries(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
/* Map in two adjacent regions */
|
||||
uc_assert_success(uc_mem_map(uc, 0, 0x1000, 0)); /* 0x0000 - 0x1000 */
|
||||
uc_assert_success(uc_mem_map(uc, 0x1000, 0x1000, 0)); /* 0x1000 - 0x2000 */
|
||||
|
||||
const uint64_t addr = 0x1000 - 2; /* 2 bytes before end of block */
|
||||
|
||||
/* Write some data across the boundary */
|
||||
uc_assert_success(uc_mem_write(uc, addr, "test", 4));
|
||||
|
||||
uint8_t buf[4];
|
||||
memset(buf, 0xCC, sizeof(buf));
|
||||
|
||||
/* Read the data across the boundary */
|
||||
uc_assert_success(uc_mem_read(uc, addr, buf, sizeof(buf)));
|
||||
|
||||
assert_memory_equal(buf, "test", 4);
|
||||
}
|
||||
|
||||
/* Try to unmap memory that has not been mapped */
|
||||
static void test_bad_unmap(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
/* TODO: Which error should this return? */
|
||||
uc_assert_fail(uc_mem_unmap(uc, 0x0, 0x1000));
|
||||
}
|
||||
|
||||
|
||||
/* Try to map overlapped memory range */
|
||||
static void test_unmap_double_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, 0, 0x4000, 0)); /* 0x0000 - 0x4000 */
|
||||
uc_assert_fail(uc_mem_map(uc, 0x0000, 0x1000, 0)); /* 0x1000 - 0x1000 */
|
||||
}
|
||||
|
||||
static void test_overlap_unmap_double_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_mem_map( uc, 0x1000, 0x2000, 0);
|
||||
uc_mem_map( uc, 0x1000, 0x1000, 0);
|
||||
uc_mem_unmap(uc, 0x2000, 0x1000);
|
||||
}
|
||||
|
||||
static void test_strange_map(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_mem_map( uc, 0x0,0x3000,0);
|
||||
uc_mem_unmap(uc, 0x1000,0x1000);
|
||||
uc_mem_map( uc, 0x3000,0x1000,0);
|
||||
uc_mem_map( uc, 0x4000,0x1000,0);
|
||||
uc_mem_map( uc, 0x1000,0x1000,0);
|
||||
uc_mem_map( uc, 0x5000,0x1000,0);
|
||||
uc_mem_unmap(uc, 0x0,0x1000);
|
||||
}
|
||||
|
||||
static void test_query_page_size(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
size_t page_size;
|
||||
uc_assert_success(uc_query(uc, UC_QUERY_PAGE_SIZE, &page_size));
|
||||
assert_int_equal(4096, page_size);
|
||||
}
|
||||
|
||||
void mem_write(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uint8_t* buff = alloca(len);
|
||||
memset(buff,0,len);
|
||||
uc_mem_write(uc, addr, buff, len);
|
||||
|
||||
}
|
||||
|
||||
void mem_read(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uint8_t* buff = alloca(len);
|
||||
uc_mem_read(uc, addr, buff, len);
|
||||
}
|
||||
|
||||
void map(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uc_mem_map(uc, addr, len, UC_PROT_READ | UC_PROT_WRITE);
|
||||
}
|
||||
|
||||
void unmap(uc_engine* uc, uint64_t addr, uint64_t len){
|
||||
uc_mem_unmap(uc, addr, len);
|
||||
}
|
||||
|
||||
//most likely same bug as in test_strange_map, but looked different in fuzzer (sefault instead of assertion fail)
|
||||
static void test_assertion_fail(void **state){
|
||||
uc_engine *uc = *state;
|
||||
|
||||
map(uc,0x2000,0x4000); //5
|
||||
unmap(uc,0x3000,0x2000); //11
|
||||
map(uc,0x0,0x2000); //23
|
||||
map(uc,0x3000,0x2000); //24
|
||||
map(uc,0x9000,0x4000); //32
|
||||
map(uc,0x8000,0x1000); //34
|
||||
unmap(uc,0x1000,0x4000); //35
|
||||
}
|
||||
|
||||
static void test_bad_offset(void **state){
|
||||
uc_engine *uc = *state;
|
||||
map(uc,0x9000,0x4000); //17
|
||||
map(uc,0x4000,0x2000); //32
|
||||
unmap(uc,0x5000,0x1000); //35
|
||||
map(uc,0x0,0x1000); //42
|
||||
map(uc,0x5000,0x4000); //51
|
||||
map(uc,0x2000,0x1000); //53
|
||||
map(uc,0x1000,0x1000); //55
|
||||
unmap(uc,0x7000,0x3000); //58
|
||||
unmap(uc,0x5000,0x1000); //59
|
||||
unmap(uc,0x4000,0x2000); //70
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(void) {
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_basic),
|
||||
//test(test_bad_read),
|
||||
//test(test_bad_write),
|
||||
test(test_bad_offset),
|
||||
test(test_assertion_fail),
|
||||
test(test_bad_unmap),
|
||||
test(test_rw_across_boundaries),
|
||||
test(test_unmap_double_map),
|
||||
test(test_overlap_unmap_double_map),
|
||||
test(test_strange_map),
|
||||
test(test_query_page_size),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Unicorn memory API tests
|
||||
*
|
||||
* This tests manual pointer-backed memory.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* A basic test showing mapping of memory, and reading/writing it
|
||||
*/
|
||||
static void test_basic(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
const uint64_t mem_start = 0x1000;
|
||||
const uint64_t mem_len = 0x1000;
|
||||
const uint64_t test_addr = mem_start;
|
||||
|
||||
void *host_mem = calloc(1, mem_len);
|
||||
|
||||
/* Map a region */
|
||||
uc_assert_success(uc_mem_map_ptr(uc, mem_start, mem_len, UC_PROT_ALL, host_mem));
|
||||
|
||||
/* Write some data to it */
|
||||
uc_assert_success(uc_mem_write(uc, test_addr, "test", 4));
|
||||
|
||||
uint8_t buf[4];
|
||||
memset(buf, 0xCC, sizeof(buf));
|
||||
|
||||
/* Read it back */
|
||||
uc_assert_success(uc_mem_read(uc, test_addr, buf, sizeof(buf)));
|
||||
|
||||
/* And make sure it matches what we expect */
|
||||
assert_memory_equal(buf, "test", 4);
|
||||
|
||||
/* Unmap the region */
|
||||
uc_assert_success(uc_mem_unmap(uc, mem_start, mem_len));
|
||||
|
||||
assert_memory_equal(buf, host_mem, 4);
|
||||
|
||||
free(host_mem);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_basic),
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
135
tests/unit/test_mips.c
Normal file
135
tests/unit/test_mips.c
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x10000000;
|
||||
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));
|
||||
}
|
||||
|
||||
static void test_mips_el_ori()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x56\x34\x21\x34"; // ori $at, $at, 0x3456;
|
||||
int r_r1 = 0x6789;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r1 == 0x77df);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_eb_ori()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x34\x21\x34\x56"; // ori $at, $at, 0x3456;
|
||||
int r_r1 = 0x6789;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_1, &r_r1));
|
||||
|
||||
TEST_CHECK(r_r1 == 0x77df);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_stop_at_branch()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] =
|
||||
"\x02\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"; // j 0x8; nop;
|
||||
int r_pc = 0x0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// Execute one instruction with branch delay slot.
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_PC, &r_pc));
|
||||
|
||||
// Even if we just execute one instruction, the instruction in the
|
||||
// delay slot would also be executed.
|
||||
TEST_CHECK(r_pc == code_start + 0x8);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_stop_at_delay_slot()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] =
|
||||
"\x02\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"; // j 0x8; nop;
|
||||
int r_pc = 0x0;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// Stop at the delay slot by design.
|
||||
OK(uc_emu_start(uc, code_start, code_start + 4, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_PC, &r_pc));
|
||||
|
||||
// The branch instruction isn't committed and the PC is not updated.
|
||||
// Users is responsible to restart emulation at the branch instruction.
|
||||
TEST_CHECK(r_pc == code_start);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_mips_lwx_exception_issue_1314()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x0a\xc8\x79\x7e"; // lwx $t9, $t9($s3)
|
||||
int reg;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_MIPS, UC_MODE_32 | UC_MODE_LITTLE_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
OK(uc_mem_map(uc, 0x10000, 0x4000, UC_PROT_ALL));
|
||||
|
||||
// Enable DSP
|
||||
// https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00090-2B-MIPS32PRA-AFP-06.02.pdf
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_CP0_STATUS, ®));
|
||||
reg |= (1 << 24);
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_CP0_STATUS, ®));
|
||||
|
||||
reg = 0;
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_1, ®));
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_T9, ®));
|
||||
reg = 0xdeadbeef;
|
||||
OK(uc_mem_write(uc, 0x10000, ®, 4));
|
||||
reg = 0x10000;
|
||||
OK(uc_reg_write(uc, UC_MIPS_REG_S3, ®));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_MIPS_REG_T9, ®));
|
||||
|
||||
TEST_CHECK(reg == 0xdeadbeef);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_mips_stop_at_branch", test_mips_stop_at_branch},
|
||||
{"test_mips_stop_at_delay_slot", test_mips_stop_at_delay_slot},
|
||||
{"test_mips_el_ori", test_mips_el_ori},
|
||||
{"test_mips_eb_ori", test_mips_eb_ori},
|
||||
{"test_mips_lwx_exception_issue_1314", test_mips_lwx_exception_issue_1314},
|
||||
{NULL, NULL}};
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup32(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
OK(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
struct bb {
|
||||
uint64_t addr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct bbtest {
|
||||
const struct bb *blocks;
|
||||
unsigned int blocknum;
|
||||
};
|
||||
|
||||
|
||||
static void test_basic_blocks_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
struct bbtest *bbtest = user_data;
|
||||
const struct bb *bb = &bbtest->blocks[bbtest->blocknum];
|
||||
|
||||
printf("block hook 1: %d == %zu\n", size, bb->size);
|
||||
assert_int_equal(address, bb->addr);
|
||||
assert_int_equal((size_t)size, bb->size);
|
||||
}
|
||||
|
||||
static void test_basic_blocks_hook2(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
struct bbtest *bbtest = user_data;
|
||||
const struct bb *bb = &bbtest->blocks[bbtest->blocknum++];
|
||||
|
||||
printf("block hook 2: %d == %zu\n", size, bb->size);
|
||||
assert_int_equal(address, bb->addr);
|
||||
assert_int_equal((size_t)size, bb->size);
|
||||
}
|
||||
|
||||
static void test_basic_blocks(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
|
||||
uint64_t address = BASEADDR;
|
||||
const uint8_t code[] = {
|
||||
0x33, 0xC0, // xor eax, eax
|
||||
0x90, // nop
|
||||
0x90, // nop
|
||||
0xEB, 0x00, // jmp $+2
|
||||
0x90, // nop
|
||||
0x90, // nop
|
||||
0x90, // nop
|
||||
};
|
||||
|
||||
static const struct bb blocks[] = {
|
||||
{BASEADDR, 6},
|
||||
{BASEADDR+ 6, 3},
|
||||
};
|
||||
|
||||
struct bbtest bbtest = {
|
||||
.blocks = blocks,
|
||||
.blocknum = 0,
|
||||
};
|
||||
|
||||
|
||||
#undef BASEADDR
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL));
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
OK(uc_mem_write(uc, address, code, sizeof(code)));
|
||||
|
||||
// trace all basic blocks
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, 1, 0));
|
||||
|
||||
OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_basic_blocks, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Test PC change during the callback. by Nguyen Anh Quynh, 2016
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
#include "sys/stat.h"
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup32(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
OK(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void test_code_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint8_t tmp[256];
|
||||
int32_t r_eip = 0x1000006;
|
||||
printf("instruction at 0x%"PRIx64": ", address);
|
||||
|
||||
if (!uc_mem_read(uc, address, tmp, size)) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
printf("0x%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (address == 0x1000003) {
|
||||
// change the PC to "inc EDX"
|
||||
uc_reg_write(uc, UC_X86_REG_EIP, &r_eip);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_pc_change(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1;
|
||||
int32_t r_ecx = 3, r_edx = 15;
|
||||
struct stat info;
|
||||
char *code = read_file("pc_change.bin", &info);
|
||||
if (code == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
|
||||
uint64_t address = BASEADDR;
|
||||
|
||||
#undef BASEADDR
|
||||
|
||||
// map 2MB memory for this emulation
|
||||
OK(uc_mem_map(uc, address, 2 * 1024 * 1024, UC_PROT_ALL));
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
OK(uc_mem_write(uc, address, code, info.st_size));
|
||||
|
||||
uc_reg_write(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_reg_write(uc, UC_X86_REG_EDX, &r_edx);
|
||||
printf("ECX = %u, EDX = %u\n", r_ecx, r_edx);
|
||||
|
||||
// trace all instructions
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0));
|
||||
|
||||
OK(uc_emu_start(uc, address, address+info.st_size, 0, 0));
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &r_ecx);
|
||||
uc_reg_read(uc, UC_X86_REG_EDX, &r_edx);
|
||||
|
||||
printf("ECX = %u, EDX = %u\n", r_ecx, r_edx);
|
||||
assert_int_equal(r_ecx, 6);
|
||||
assert_int_equal(r_edx, 17);
|
||||
free(code);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_pc_change, setup32, teardown),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
68
tests/unit/test_ppc.c
Normal file
68
tests/unit/test_ppc.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#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));
|
||||
}
|
||||
|
||||
static void test_ppc32_add()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x7f\x46\x1a\x14"; // ADD 26, 6, 3
|
||||
int reg;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_PPC, UC_MODE_32 | UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
reg = 42;
|
||||
OK(uc_reg_write(uc, UC_PPC_REG_3, ®));
|
||||
reg = 1337;
|
||||
OK(uc_reg_write(uc, UC_PPC_REG_6, ®));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_PPC_REG_26, ®));
|
||||
|
||||
TEST_CHECK(reg == 1379);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
// https://www.ibm.com/docs/en/aix/7.2?topic=set-fadd-fa-floating-add-instruction
|
||||
static void test_ppc32_fadd()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\xfc\xc4\x28\x2a"; // fadd 6, 4, 5
|
||||
uint32_t r_msr;
|
||||
uint64_t r_fpr4, r_fpr5, r_fpr6;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_PPC, UC_MODE_32 | UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
OK(uc_reg_read(uc, UC_PPC_REG_MSR, &r_msr));
|
||||
r_msr |= (1 << 13); // Big endian
|
||||
OK(uc_reg_write(uc, UC_PPC_REG_MSR, &r_msr)); // enable FP
|
||||
|
||||
r_fpr4 = 0xC053400000000000ul;
|
||||
r_fpr5 = 0x400C000000000000ul;
|
||||
OK(uc_reg_write(uc, UC_PPC_REG_FPR4, &r_fpr4));
|
||||
OK(uc_reg_write(uc, UC_PPC_REG_FPR5, &r_fpr5));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_PPC_REG_FPR6, &r_fpr6));
|
||||
|
||||
TEST_CHECK(r_fpr6 == 0xC052600000000000ul);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_ppc32_add", test_ppc32_add},
|
||||
{"test_ppc32_fadd", test_ppc32_fadd},
|
||||
{NULL, NULL}};
|
||||
559
tests/unit/test_riscv.c
Normal file
559
tests/unit/test_riscv.c
Normal file
@@ -0,0 +1,559 @@
|
||||
#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));
|
||||
}
|
||||
|
||||
static void test_riscv32_nop()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x13\x00\x00\x00"; // nop
|
||||
uint32_t r_t0 = 0x1234;
|
||||
uint32_t r_t1 = 0x5678;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV32, code,
|
||||
sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
TEST_CHECK(r_t0 == 0x1234);
|
||||
TEST_CHECK(r_t1 == 0x5678);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv64_nop()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x13\x00\x00\x00"; // nop
|
||||
uint64_t r_t0 = 0x1234;
|
||||
uint64_t r_t1 = 0x5678;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
TEST_CHECK(r_t0 == 0x1234);
|
||||
TEST_CHECK(r_t1 == 0x5678);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv32_until_pc_update()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x93\x02\x10\x00\x13\x03\x00\x02\x13\x01\x81\x00";
|
||||
|
||||
/*
|
||||
addi t0, zero, 1
|
||||
addi t1, zero, 0x20
|
||||
addi sp, sp, 8
|
||||
*/
|
||||
|
||||
uint32_t r_t0 = 0x1234;
|
||||
uint32_t r_t1 = 0x7890;
|
||||
uint32_t r_pc = 0x0000;
|
||||
uint32_t r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV32, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
|
||||
// emulate the three instructions
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_t0 == 0x1);
|
||||
TEST_CHECK(r_t1 == 0x20);
|
||||
TEST_CHECK(r_sp == 0x123c);
|
||||
|
||||
TEST_CHECK(r_pc == (code_start + sizeof(code) - 1));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv64_until_pc_update()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x93\x02\x10\x00\x13\x03\x00\x02\x13\x01\x81\x00";
|
||||
|
||||
/*
|
||||
addi t0, zero, 1
|
||||
addi t1, zero, 0x20
|
||||
addi sp, sp, 8
|
||||
*/
|
||||
|
||||
uint64_t r_t0 = 0x1234;
|
||||
uint64_t r_t1 = 0x7890;
|
||||
uint64_t r_pc = 0x0000;
|
||||
uint64_t r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
|
||||
// emulate the three instructions
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_t0 == 0x1);
|
||||
TEST_CHECK(r_t1 == 0x20);
|
||||
TEST_CHECK(r_sp == 0x123c);
|
||||
TEST_CHECK(r_pc == (code_start + sizeof(code) - 1));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv32_3steps_pc_update()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x93\x02\x10\x00\x13\x03\x00\x02\x13\x01\x81\x00";
|
||||
|
||||
/*
|
||||
addi t0, zero, 1
|
||||
addi t1, zero, 0x20
|
||||
addi sp, sp, 8
|
||||
*/
|
||||
|
||||
uint32_t r_t0 = 0x1234;
|
||||
uint32_t r_t1 = 0x7890;
|
||||
uint32_t r_pc = 0x0000;
|
||||
uint32_t r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV32, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
|
||||
// emulate the three instructions
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 3));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_t0 == 0x1);
|
||||
TEST_CHECK(r_t1 == 0x20);
|
||||
TEST_CHECK(r_sp == 0x123c);
|
||||
|
||||
TEST_CHECK(r_pc == (code_start + sizeof(code) - 1));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv64_3steps_pc_update()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x93\x02\x10\x00\x13\x03\x00\x02\x13\x01\x81\x00";
|
||||
|
||||
/*
|
||||
addi t0, zero, 1
|
||||
addi t1, zero, 0x20
|
||||
addi sp, sp, 8
|
||||
*/
|
||||
|
||||
uint64_t r_t0 = 0x1234;
|
||||
uint64_t r_t1 = 0x7890;
|
||||
uint64_t r_pc = 0x0000;
|
||||
uint64_t r_sp = 0x1234;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
|
||||
// emulate the three instructions
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 3));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T1, &r_t1));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_SP, &r_sp));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_t0 == 0x1);
|
||||
TEST_CHECK(r_t1 == 0x20);
|
||||
TEST_CHECK(r_sp == 0x123c);
|
||||
TEST_CHECK(r_pc == (code_start + sizeof(code) - 1));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv32_fp_move(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\xd3\x81\x10\x22"; // fmv.d f3, f1
|
||||
|
||||
uint32_t r_f1 = 0x1234;
|
||||
uint32_t r_f3 = 0x5678;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV32, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
uc_reg_write(uc, UC_RISCV_REG_F1, &r_f1);
|
||||
uc_reg_write(uc, UC_RISCV_REG_F3, &r_f3);
|
||||
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_F1, &r_f1));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_F3, &r_f3));
|
||||
|
||||
TEST_CHECK(r_f1 == 0x1234);
|
||||
TEST_CHECK(r_f3 == 0x1234);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_riscv64_fp_move(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\xd3\x81\x10\x22"; // fmv.d f3, f1
|
||||
|
||||
uint64_t r_f1 = 0x12341234;
|
||||
uint64_t r_f3 = 0x56785678;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_F1, &r_f1));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_F3, &r_f3));
|
||||
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_F1, &r_f1));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_F3, &r_f3));
|
||||
|
||||
TEST_CHECK(r_f1 == 0x12341234);
|
||||
TEST_CHECK(r_f3 == 0x12341234);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_riscv64_fp_move_from_int(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
// https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf
|
||||
// https://five-embeddev.com/quickref/csrs.html
|
||||
// We have to enable mstatus.fs
|
||||
char code[] = "\xf3\x90\x01\x30\x53\x00\x0b\xf2"; // csrrw x2, mstatus, x3;
|
||||
// fmvd.d.x ft0, s6
|
||||
|
||||
uint64_t r_ft0 = 0x12341234;
|
||||
uint64_t r_s6 = 0x56785678;
|
||||
uint64_t r_x3 = 0x6000;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_FT0, &r_ft0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_S6, &r_s6));
|
||||
|
||||
// mstatus.fs
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_X3, &r_x3));
|
||||
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 2));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_FT0, &r_ft0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_S6, &r_s6));
|
||||
|
||||
TEST_CHECK(r_ft0 == 0x56785678);
|
||||
TEST_CHECK(r_s6 == 0x56785678);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_riscv64_fp_move_from_int_reg_write(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x53\x00\x0b\xf2"; // fmvd.d.x ft0, s6
|
||||
|
||||
uint64_t r_ft0 = 0x12341234;
|
||||
uint64_t r_s6 = 0x56785678;
|
||||
uint64_t r_mstatus = 0x6000;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_FT0, &r_ft0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_S6, &r_s6));
|
||||
|
||||
// mstatus.fs
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_MSTATUS, &r_mstatus));
|
||||
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_FT0, &r_ft0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_S6, &r_s6));
|
||||
|
||||
TEST_CHECK(r_ft0 == 0x56785678);
|
||||
TEST_CHECK(r_s6 == 0x56785678);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv64_fp_move_to_int(void)
|
||||
{
|
||||
uc_engine *uc;
|
||||
// https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf
|
||||
// https://five-embeddev.com/quickref/csrs.html
|
||||
// We have to enable mstatus.fs
|
||||
char code[] = "\xf3\x90\x01\x30\x53\x0b\x00\xe2"; // csrrw x2, mstatus, x3;
|
||||
// fmv.x.d s6, ft0
|
||||
|
||||
uint64_t r_ft0 = 0x12341234;
|
||||
uint64_t r_s6 = 0x56785678;
|
||||
uint64_t r_x3 = 0x6000;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
// initialize machine registers
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_FT0, &r_ft0));
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_S6, &r_s6));
|
||||
|
||||
// mstatus.fs
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_X3, &r_x3));
|
||||
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 2));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_FT0, &r_ft0));
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_S6, &r_s6));
|
||||
|
||||
TEST_CHECK(r_ft0 == 0x12341234);
|
||||
TEST_CHECK(r_s6 == 0x12341234);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_riscv64_code_patching()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x93\x82\x12\x00"; // addi t0, t0, 0x1
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
// Zero out t0 and t1
|
||||
uint64_t r_t0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
TEST_CHECK(r_t0 == 0x1);
|
||||
// patch instruction
|
||||
char patch_code[] = "\x93\x82\xf2\x7f"; // addi t0, t0, 0x7FF
|
||||
OK(uc_mem_write(uc, code_start, patch_code, sizeof(patch_code) - 1));
|
||||
// zero out t0
|
||||
r_t0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(patch_code) - 1, 0, 0));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
TEST_CHECK(r_t0 != 0x1);
|
||||
TEST_CHECK(r_t0 == 0x7ff);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
// Need to flush the cache before running the emulation after patching
|
||||
static void test_riscv64_code_patching_count()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x93\x82\x12\x00"; // addi t0, t0, 0x1
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
// Zero out t0 and t1
|
||||
uint64_t r_t0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
// emulate the instruction
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
TEST_CHECK(r_t0 == 0x1);
|
||||
// patch instruction
|
||||
char patch_code[] = "\x93\x82\xf2\x7f"; // addi t0, t0, 0x7FF
|
||||
OK(uc_mem_write(uc, code_start, patch_code, sizeof(patch_code) - 1));
|
||||
OK(uc_ctl_remove_cache(uc, code_start,
|
||||
code_start + sizeof(patch_code) - 1));
|
||||
// zero out t0
|
||||
r_t0 = 0x0;
|
||||
OK(uc_reg_write(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
OK(uc_emu_start(uc, code_start, -1, 0, 1));
|
||||
// check value
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_T0, &r_t0));
|
||||
TEST_CHECK(r_t0 != 0x1);
|
||||
TEST_CHECK(r_t0 == 0x7ff);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv64_ecall_cb(uc_engine *uc, uint32_t intno, void *data)
|
||||
{
|
||||
uc_emu_stop(uc);
|
||||
return;
|
||||
}
|
||||
|
||||
static void test_riscv64_ecall()
|
||||
{
|
||||
uc_engine *uc;
|
||||
char code[] = "\x73\x00\x00\x00"; // ecall
|
||||
uint64_t r_pc;
|
||||
uc_hook h;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
OK(uc_hook_add(uc, &h, UC_HOOK_INTR, test_riscv64_ecall_cb, NULL, 1, 0));
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_pc == code_start + 4);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static uint64_t test_riscv32_mmio_map_read_cb(uc_engine *uc, uint64_t offset,
|
||||
unsigned size, void *data)
|
||||
{
|
||||
int r_a4;
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_A4, &r_a4));
|
||||
TEST_CHECK(r_a4 == 0x40021 << 12);
|
||||
TEST_CHECK(offset == 0x21018);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_riscv32_mmio_map()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// 37 17 02 40 lui a4, 0x40021
|
||||
// 1c 4f c.lw a5, 0x18(a4)
|
||||
//
|
||||
char code[] = "\x37\x17\x02\x40\x1c\x4f";
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV32, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
OK(uc_mmio_map(uc, 0x40000000, 0x40000, test_riscv32_mmio_map_read_cb, NULL,
|
||||
NULL, NULL));
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static void test_riscv32_map()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// 37 17 02 40 lui a4, 0x40021
|
||||
// 1c 4f c.lw a5, 0x18(a4)
|
||||
//
|
||||
char code[] = "\x37\x17\x02\x40\x1c\x4f";
|
||||
uint64_t val = 0xdeadbeef;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV32, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
OK(uc_mem_map(uc, 0x40000000, 0x40000, UC_PROT_ALL));
|
||||
OK(uc_mem_write(uc, 0x40000000 + 0x21018, &val, 8));
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_A5, &val));
|
||||
|
||||
TEST_CHECK(val == 0xdeadbeef);
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
static uint64_t test_riscv64_mmio_map_read_cb(uc_engine *uc, uint64_t offset,
|
||||
unsigned size, void *data)
|
||||
{
|
||||
uint64_t r_a4;
|
||||
OK(uc_reg_read(uc, UC_RISCV_REG_A4, &r_a4));
|
||||
TEST_CHECK(r_a4 == 0x40021 << 12);
|
||||
TEST_CHECK(offset == 0x21018);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_riscv64_mmio_map()
|
||||
{
|
||||
uc_engine *uc;
|
||||
// 37 17 02 40 lui a4, 0x40021
|
||||
// 1c 4f c.lw a5, 0x18(a4)
|
||||
//
|
||||
char code[] = "\x37\x17\x02\x40\x1c\x4f";
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_RISCV, UC_MODE_RISCV64, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
OK(uc_mmio_map(uc, 0x40000000, 0x40000, test_riscv64_mmio_map_read_cb, NULL,
|
||||
NULL, NULL));
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_riscv32_nop", test_riscv32_nop},
|
||||
{"test_riscv64_nop", test_riscv64_nop},
|
||||
{"test_riscv32_3steps_pc_update", test_riscv32_3steps_pc_update},
|
||||
{"test_riscv64_3steps_pc_update", test_riscv64_3steps_pc_update},
|
||||
{"test_riscv32_until_pc_update", test_riscv32_until_pc_update},
|
||||
{"test_riscv64_until_pc_update", test_riscv64_until_pc_update},
|
||||
{"test_riscv32_fp_move", test_riscv32_fp_move},
|
||||
{"test_riscv64_fp_move", test_riscv64_fp_move},
|
||||
{"test_riscv64_fp_move_from_int", test_riscv64_fp_move_from_int},
|
||||
{"test_riscv64_fp_move_from_int_reg_write",
|
||||
test_riscv64_fp_move_from_int_reg_write},
|
||||
{"test_riscv64_fp_move_to_int", test_riscv64_fp_move_to_int},
|
||||
{"test_riscv64_ecall", test_riscv64_ecall},
|
||||
{"test_riscv32_mmio_map", test_riscv32_mmio_map},
|
||||
{"test_riscv64_mmio_map", test_riscv64_mmio_map},
|
||||
{"test_riscv32_map", test_riscv32_map},
|
||||
{"test_riscv64_code_patching", test_riscv64_code_patching},
|
||||
{"test_riscv64_code_patching_count", test_riscv64_code_patching_count},
|
||||
{NULL, NULL}};
|
||||
36
tests/unit/test_s390x.c
Normal file
36
tests/unit/test_s390x.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#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));
|
||||
}
|
||||
|
||||
static void test_s390x_lr()
|
||||
{
|
||||
char code[] = "\x18\x23"; // lr %r2, %r3
|
||||
uint64_t r_pc, r_r2, r_r3 = 0x114514;
|
||||
uc_engine *uc;
|
||||
|
||||
uc_common_setup(&uc, UC_ARCH_S390X, UC_MODE_BIG_ENDIAN, code,
|
||||
sizeof(code) - 1);
|
||||
|
||||
OK(uc_reg_write(uc, UC_S390X_REG_R3, &r_r3));
|
||||
|
||||
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
|
||||
|
||||
OK(uc_reg_read(uc, UC_S390X_REG_R2, &r_r2));
|
||||
OK(uc_reg_read(uc, UC_S390X_REG_PC, &r_pc));
|
||||
|
||||
TEST_CHECK(r_r2 == 0x114514);
|
||||
TEST_CHECK(r_pc == code_start + sizeof(code) - 1);
|
||||
|
||||
OK(uc_close(uc));
|
||||
}
|
||||
|
||||
TEST_LIST = {{"test_s390x_lr", test_s390x_lr}, {NULL, NULL}};
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
/* Make sure the uc_assert macros work with constants */
|
||||
static void test_uc_assert_macros_constants(void **state)
|
||||
{
|
||||
const uc_err nomem = UC_ERR_NOMEM;
|
||||
|
||||
uc_assert_success(UC_ERR_OK);
|
||||
uc_assert_err(UC_ERR_NOMEM, nomem);
|
||||
uc_assert_fail(UC_ERR_VERSION);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static uc_err feedback(uc_err err, int *callcount)
|
||||
{
|
||||
assert_int_equal(++(*callcount), 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the uc_assert macros work with function calls
|
||||
* and only evaluate them once!
|
||||
*/
|
||||
static void test_uc_assert_macros_func_calls(void **state)
|
||||
{
|
||||
int callcount;
|
||||
|
||||
callcount = 0;
|
||||
uc_assert_success(feedback(UC_ERR_OK, &callcount));
|
||||
|
||||
callcount = 0;
|
||||
uc_assert_err(UC_ERR_NOMEM, feedback(UC_ERR_NOMEM, &callcount));
|
||||
|
||||
callcount = 0;
|
||||
uc_assert_fail(feedback(UC_ERR_VERSION, &callcount));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void fail_uc_assert_success(void **state)
|
||||
{
|
||||
uc_assert_success(UC_ERR_NOMEM);
|
||||
}
|
||||
|
||||
static void fail_uc_assert_err(void **state)
|
||||
{
|
||||
const uc_err ok = UC_ERR_OK;
|
||||
uc_assert_err(UC_ERR_VERSION, ok);
|
||||
}
|
||||
|
||||
static void fail_uc_assert_fail(void **state)
|
||||
{
|
||||
uc_assert_fail(UC_ERR_OK);
|
||||
}
|
||||
|
||||
static void test_uc_assert_macros_fail(void **state)
|
||||
{
|
||||
/* A test-inside-a-test */
|
||||
|
||||
const struct CMUnitTest tests[] = {
|
||||
/* these should all fail */
|
||||
cmocka_unit_test(fail_uc_assert_success),
|
||||
cmocka_unit_test(fail_uc_assert_err),
|
||||
cmocka_unit_test(fail_uc_assert_fail),
|
||||
};
|
||||
|
||||
print_message("\n\n--------------------------------------------------------------------------------\n");
|
||||
print_message("START: Failure of the following tests is expected.\n\n");
|
||||
|
||||
assert_int_not_equal(0, cmocka_run_group_tests(tests, NULL, NULL));
|
||||
|
||||
print_message("\n\nEND: Failure of the preceding tests was expected.\n");
|
||||
print_message("--------------------------------------------------------------------------------\n\n");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_uc_assert_macros_constants),
|
||||
cmocka_unit_test(test_uc_assert_macros_func_calls),
|
||||
cmocka_unit_test(test_uc_assert_macros_fail),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
6
tests/unit/test_sparc.c
Normal file
6
tests/unit/test_sparc.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "unicorn_test.h"
|
||||
|
||||
const uint64_t code_start = 0x1000;
|
||||
const uint64_t code_len = 0x4000;
|
||||
|
||||
TEST_LIST = {{NULL, NULL}};
|
||||
@@ -1,306 +0,0 @@
|
||||
/**
|
||||
* Unicorn x86_32 self-modifying unit test
|
||||
*
|
||||
* This test demonstrates the flushing of instruction translation cache
|
||||
* after a self-modification of Intel's x8's "IMUL Gv,Ev,Ib" instruction.
|
||||
*/
|
||||
#include "unicorn_test.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
#define RIP_NEXT_TO_THE_SELFMODIFY_OPCODE (1)
|
||||
|
||||
|
||||
// Demostration of a self-modifying "IMUL eax,mem,Ib" opcode
|
||||
// And the QEMU's ability to flush the translation buffer properly
|
||||
|
||||
#define MIN(a, b) (a < b? a: b)
|
||||
|
||||
#define CODE_SPACE (2 * 1024 * 1024)
|
||||
#define PHY_STACK_REGION (0x60000000)
|
||||
|
||||
/* Called before every test to set up a new instance */
|
||||
static int setup(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called after every test to clean up */
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dump_stack_mem(uc_engine *uc, const struct stat info)
|
||||
{
|
||||
uint8_t tmp[256];
|
||||
uint32_t size;
|
||||
|
||||
size = sizeof(info.st_size);
|
||||
if (size > 255) size = 255;
|
||||
if (!uc_mem_read(uc, PHY_STACK_REGION, tmp, size))
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
printf("Stack region dump");
|
||||
for (i=0; i<size; i++) {
|
||||
if ((i % 16) == 0) printf("\n%x: ", PHY_STACK_REGION+i);
|
||||
printf("%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_registers(uc_engine *uc)
|
||||
{
|
||||
int32_t eax, ecx, edx, ebx;
|
||||
int32_t esp, ebp, esi, edi;
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
|
||||
uc_reg_read(uc, UC_X86_REG_EDX, &edx);
|
||||
uc_reg_read(uc, UC_X86_REG_EBX, &ebx);
|
||||
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
|
||||
uc_reg_read(uc, UC_X86_REG_EBP, &ebp);
|
||||
uc_reg_read(uc, UC_X86_REG_ESI, &esi);
|
||||
uc_reg_read(uc, UC_X86_REG_EDI, &edi);
|
||||
|
||||
printf("Register dump:\n");
|
||||
printf("eax %8.8x ", eax);
|
||||
printf("ecx %8.8x ", ecx);
|
||||
printf("edx %8.8x ", edx);
|
||||
printf("ebx %8.8x\n", ebx);
|
||||
printf("esp %8.8x ", esp);
|
||||
printf("ebp %8.8x ", ebp);
|
||||
printf("esi %8.8x ", esi);
|
||||
printf("edi %8.8x ", edi);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
static void hook_code32(uc_engine *uc,
|
||||
uint64_t address,
|
||||
uint32_t size,
|
||||
void *user_data,
|
||||
const struct stat info)
|
||||
{
|
||||
//uint8_t opcode[256];
|
||||
uint8_t tmp[16];
|
||||
uint32_t tmp4[1];
|
||||
uint32_t ecx;
|
||||
|
||||
printf("\nhook_code32: Address: %"PRIx64", Opcode Size: %d\n", address, size);
|
||||
print_registers(uc);
|
||||
size = MIN(sizeof(tmp), size);
|
||||
if (!uc_mem_read(uc, address, tmp, size))
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
printf("Opcode: ");
|
||||
for (i=0; i<size; i++) {
|
||||
printf("%x ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
dump_stack_mem(uc, info);
|
||||
|
||||
|
||||
if (address == 0x60000025)
|
||||
{
|
||||
// double-check that opcode is
|
||||
// IMUL aex,[eax+0x41],0x10
|
||||
if ((tmp[0] != 0x6b) ||
|
||||
(tmp[1] != 0x41) ||
|
||||
(tmp[2] != 0x41) ||
|
||||
(tmp[3] != 0x10))
|
||||
{
|
||||
printf("FAILED set-up of opcode\n");
|
||||
exit(-1);
|
||||
}
|
||||
printf("IMUL eax,[ecx+0x41],0x10\n");
|
||||
|
||||
// double-check that memory operand points to 0x6000003a
|
||||
uc_reg_read(uc, UC_X86_REG_ECX, &ecx);
|
||||
if (ecx != 0x5ffffff9)
|
||||
{
|
||||
printf("FAILED EAX register not having 0x5ffffff9\n");
|
||||
exit(-1);
|
||||
}
|
||||
printf("ECX = %8.8x\n", ecx);
|
||||
|
||||
printf("%8.8x + 0x41 = %8.8x\n", 0x5ffffff9, 0x5ffffff9 + 0x41);
|
||||
|
||||
// double-check that memory location 0x60000039
|
||||
// contains 0x5151494a
|
||||
if (!uc_mem_read(uc, 0x6000003a, tmp4, 4))
|
||||
{
|
||||
if (tmp4[0] != 0x5151494a)
|
||||
{
|
||||
printf("FAILED set-up\n");
|
||||
exit(-1);
|
||||
}
|
||||
printf("Proved that 0x6000003a contains the proper 0x5151494a\n");
|
||||
}
|
||||
// dump_stack_mem(uc);
|
||||
}
|
||||
|
||||
// Stop after 'imul eax,[ecx+0x41],0x10
|
||||
if (address == 0x60000029)
|
||||
{
|
||||
uint32_t eax;
|
||||
// IMUL eax,mem,Ib
|
||||
// mem = [ecx+0x41]
|
||||
// ecx = 0x5ffffff9
|
||||
// [6000003A] = 0x5151494a
|
||||
// Stop after 'imul eax,[ecx+0x41],0x10
|
||||
// This step basically shifts left 8-bit...elaborately.
|
||||
// multiplying 0x5151494a x 0x10 = 0x151494a0
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
if (eax != 0x151494a0)
|
||||
{
|
||||
fail_msg("FAIL: TB did not flush; eax is not the expected 0x151494a0\n");
|
||||
print_registers(uc);
|
||||
//dump_stack_mem(uc);
|
||||
exit(-1);
|
||||
}
|
||||
printf("PASS\n");
|
||||
}
|
||||
print_registers(uc);
|
||||
// dump_stack_mem(uc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void hook_mem32(uc_engine *uc,
|
||||
uc_mem_type type,
|
||||
uint64_t address,
|
||||
int size,
|
||||
uint64_t value,
|
||||
void *user_data)
|
||||
{
|
||||
char ctype;
|
||||
//uint32_t tmp[1];
|
||||
|
||||
ctype = '?';
|
||||
if (type == UC_MEM_READ) ctype = 'R';
|
||||
if (type == UC_MEM_WRITE) ctype = 'W';
|
||||
printf("hook_mem32(%c): Address: 0x%"PRIx64", Size: %d, Value:0x%"PRIx64"\n", ctype, address, size, value);
|
||||
|
||||
// if (!uc_mem_read(uc, 0x6000003a, tmp, 4))
|
||||
// {
|
||||
// printf(" hook_mem32 0x6000003a: %8.8x\n", tmp[0]);
|
||||
// }
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
struct stat info;
|
||||
char * code = read_file("tb_x86.bin", &info);
|
||||
//void *mem;
|
||||
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
|
||||
// These values assumes just before PC = 0x60000021
|
||||
int64_t eax = 0x00000041;
|
||||
int64_t ecx = 0x5ffffff8;
|
||||
int64_t edx = 0x5ffffff8;
|
||||
int64_t ebx = 0x034a129b;
|
||||
int64_t esp = 0x6010229a;
|
||||
int64_t ebp = 0x60000002;
|
||||
int64_t esi = 0x1f350211;
|
||||
int64_t edi = 0x488ac239;
|
||||
#else
|
||||
// These values assumes PC == 0x6000000
|
||||
int64_t eax = 0x73952c43;
|
||||
int64_t ecx = 0x6010229a;
|
||||
int64_t edx = 0x2a500e50;
|
||||
int64_t ebx = 0x034a1295;
|
||||
int64_t esp = 0x6010229a;
|
||||
int64_t ebp = 0x60000000;
|
||||
int64_t esi = 0x1f350211;
|
||||
int64_t edi = 0x488ac239;
|
||||
#endif
|
||||
|
||||
//mem = calloc(1, CODE_SPACE);
|
||||
// TODO examine
|
||||
//assert_int_not_equal(0, mem);
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86,
|
||||
UC_MODE_32,
|
||||
&uc));
|
||||
uc_assert_success(uc_mem_map(uc,
|
||||
PHY_STACK_REGION,
|
||||
CODE_SPACE,
|
||||
UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc,
|
||||
PHY_STACK_REGION,
|
||||
code,
|
||||
info.st_size));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EAX, &eax));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ECX, &ecx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDX, &edx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBX, &ebx));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESP, &esp));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EBP, &ebp));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_ESI, &esi));
|
||||
uc_assert_success(uc_reg_write(uc, UC_X86_REG_EDI, &edi));
|
||||
|
||||
uc_assert_success(uc_hook_add(uc,
|
||||
&trace1,
|
||||
UC_HOOK_CODE,
|
||||
hook_code32,
|
||||
NULL,
|
||||
1,
|
||||
0,
|
||||
info));
|
||||
|
||||
uc_assert_success(uc_hook_add(uc,
|
||||
&trace2,
|
||||
UC_HOOK_MEM_VALID,
|
||||
hook_mem32,
|
||||
NULL,
|
||||
1,
|
||||
0));
|
||||
|
||||
uc_assert_success(uc_emu_start(uc,
|
||||
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE
|
||||
// Register set (before self-modifying IMUL opcode)
|
||||
// Start at "0x00000021: xorb %al, 0x30(%ecx)
|
||||
// Start at "0x00000021: xor byte ptr [ecx + 0x30], al
|
||||
PHY_STACK_REGION+0x0021, // 0x0024 didn't work
|
||||
#else
|
||||
PHY_STACK_REGION+0x0000,
|
||||
#endif
|
||||
PHY_STACK_REGION+info.st_size,
|
||||
0, 0));
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
#define test(x) cmocka_unit_test_setup_teardown(x, setup, teardown)
|
||||
const struct CMUnitTest tests[] = {
|
||||
test(test_tb_x86_64_32_imul_Gv_Ev_Ib)
|
||||
};
|
||||
#undef test
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
#include "unicorn_test.h"
|
||||
|
||||
/**
|
||||
* Initialize i386 Unicorn Instance
|
||||
*/
|
||||
static int setup_i386(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize amd64 Unicorn Instance
|
||||
*/
|
||||
static int setup_amd64(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
|
||||
uc_assert_success(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
|
||||
|
||||
*state = uc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown a Unicorn Instance
|
||||
*/
|
||||
static int teardown(void **state)
|
||||
{
|
||||
uc_engine *uc = *state;
|
||||
|
||||
uc_assert_success(uc_close(uc));
|
||||
|
||||
*state = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
bool good;
|
||||
uint64_t actual;
|
||||
uint64_t expected;
|
||||
} TestData;
|
||||
|
||||
const uint64_t CodePage = 0x10000;
|
||||
const uint64_t CodeSize = 0x4000;
|
||||
|
||||
/**
|
||||
* Hook for reading unmapped memory in the i386 Unicorn Instance.
|
||||
*
|
||||
* BUG: EIP from uc_reg_read does not match expected value.
|
||||
*/
|
||||
static bool mem_hook_i386(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
TestData *data = user_data;
|
||||
if (type == UC_MEM_READ_UNMAPPED)
|
||||
{
|
||||
uint32_t eip;
|
||||
uint32_t eax;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_EIP, &eip);
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &eax);
|
||||
|
||||
data->actual = eip;
|
||||
data->expected = CodePage + 0x05;
|
||||
|
||||
/**
|
||||
* Code:
|
||||
* 0x10000: mov eax, 0x41414141 ;; <- Returned EIP
|
||||
* 0x10005: mov ecx, [eax] ;; <- Expected EIP
|
||||
*/
|
||||
if ((eax == 0x41414141) && // Proof we're at 0x10005.
|
||||
(eip != (CodePage + 0x5))) // Proof uc_reg_read is wrong
|
||||
{
|
||||
data->good = false;
|
||||
}
|
||||
else
|
||||
data->good = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for reading unmapped memory in the amd64 Unicorn Instance.
|
||||
*
|
||||
* BUG: RIP from uc_reg_read does not match expected value.
|
||||
*/
|
||||
static bool mem_hook_amd64(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
TestData *data = user_data;
|
||||
if (type == UC_MEM_READ_UNMAPPED)
|
||||
{
|
||||
uint64_t rip;
|
||||
uint64_t rax;
|
||||
|
||||
uc_reg_read(uc, UC_X86_REG_RIP, &rip);
|
||||
uc_reg_read(uc, UC_X86_REG_RAX, &rax);
|
||||
|
||||
data->actual = rip;
|
||||
data->expected = CodePage + 0x0A;
|
||||
|
||||
/**
|
||||
* Code:
|
||||
* 0x10000: mov rax, 0x4141414141414141 ;; <- Returned RIP
|
||||
* 0x10005: mov rcx, [rax] ;; <- Expected RIP
|
||||
*/
|
||||
if ((rax == 0x4141414141414141) && // Proof we're at 0x10005
|
||||
(rip != (CodePage + 0xA))) // Proof uc_reg_read is wrong
|
||||
{
|
||||
data->good = false;
|
||||
}
|
||||
else
|
||||
data->good = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty Code Hook.
|
||||
*/
|
||||
static void code_hook(uc_engine *uc, uint64_t addr, uint32_t size, void *user)
|
||||
{
|
||||
(void) uc;
|
||||
(void) addr;
|
||||
(void) size;
|
||||
(void) user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the bug for i386.
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Run the VM.
|
||||
*/
|
||||
static void test_i386(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1;
|
||||
|
||||
const uint8_t i386_bug[] = {
|
||||
0xb8, 0x41, 0x41, 0x41, 0x41, // mov eax, 0x41414141
|
||||
0x8b, 0x08 // mov ecx, [eax]
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, i386_bug, sizeof(i386_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_i386, &data, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(i386_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the bug for amd64..
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Run the VM.
|
||||
*/
|
||||
static void test_amd64(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1;
|
||||
|
||||
const uint8_t amd64_bug[] = {
|
||||
0x48, 0xb8, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41,
|
||||
0x48, 0x8b, 0x08
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, amd64_bug, sizeof(amd64_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_amd64, &data, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(amd64_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test temporary fix for bug for i386.
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Install Code hook.
|
||||
* 5. Run the VM.
|
||||
*/
|
||||
static void test_i386_fix(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
const uint8_t i386_bug[] = {
|
||||
0xb8, 0x41, 0x41, 0x41, 0x41, // mov eax, 0x41414141
|
||||
0x8b, 0x08 // mov ecx, [eax]
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, i386_bug, sizeof(i386_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_i386, &data, 1, 0));
|
||||
uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_CODE, code_hook, NULL, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(i386_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test temporary fix for bug for amd64..
|
||||
*
|
||||
* 1. Map Code Page
|
||||
* 2. Write Code to page.
|
||||
* 3. Install Unmapped Read hook.
|
||||
* 4. Install Code hook.
|
||||
* 5. Run the VM.
|
||||
*/
|
||||
static void test_amd64_fix(void **state)
|
||||
{
|
||||
TestData data;
|
||||
uc_engine *uc = *state;
|
||||
uc_hook trace1, trace2;
|
||||
|
||||
const uint8_t amd64_bug[] = {
|
||||
0x48, 0xb8, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41,
|
||||
0x48, 0x8b, 0x08
|
||||
};
|
||||
|
||||
uc_assert_success(uc_mem_map(uc, CodePage, CodeSize, UC_PROT_ALL));
|
||||
uc_assert_success(uc_mem_write(uc, CodePage, amd64_bug, sizeof(amd64_bug)));
|
||||
uc_assert_success(uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED, mem_hook_amd64, &data, 1, 0));
|
||||
uc_assert_success(uc_hook_add(uc, &trace2, UC_HOOK_CODE, code_hook, NULL, 1, 0));
|
||||
uc_assert_fail(uc_emu_start(uc, CodePage, CodePage + sizeof(amd64_bug), 0, 0));
|
||||
|
||||
if (!data.good)
|
||||
fail_msg("De-synced RIP value. 0x%"PRIX64" != 0x%"PRIX64"\n", data.expected, data.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all tests
|
||||
*/
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(test_i386, setup_i386, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_amd64, setup_amd64, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_i386_fix, setup_i386, teardown),
|
||||
cmocka_unit_test_setup_teardown(test_amd64_fix, setup_amd64, teardown)
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
#include "unicorn/unicorn.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "unicorn_test.h"
|
||||
|
||||
|
||||
#define OK(x) uc_assert_success(x)
|
||||
|
||||
#define CF_MASK (1<<0)
|
||||
#define PF_MASK (1<<2)
|
||||
#define ZF_MASK (1<<6)
|
||||
#define SF_MASK (1<<7)
|
||||
#define OF_MASK (1<<11)
|
||||
#define ALL_MASK (OF_MASK|SF_MASK|ZF_MASK|PF_MASK|CF_MASK)
|
||||
#define NO_MASK 0xFFFFFFFF
|
||||
|
||||
typedef struct _reg_value
|
||||
{
|
||||
uint32_t regId, regValue, mask;
|
||||
} reg_value;
|
||||
|
||||
typedef struct _instruction
|
||||
{
|
||||
const char* asmStr;
|
||||
uint8_t code[16]; //x86 inst == 15 bytes max
|
||||
uint32_t codeSize;
|
||||
reg_value* values;
|
||||
uint32_t nbValues;
|
||||
uint32_t addr;
|
||||
} instruction;
|
||||
|
||||
typedef struct _block
|
||||
{
|
||||
instruction* insts[255];
|
||||
uint32_t nbInsts;
|
||||
uint32_t size;
|
||||
} block;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
#define CAT2(X, Y) X ## Y
|
||||
#define CAT(X, Y) CAT2(X, Y)
|
||||
|
||||
#define BLOCK_START(BLOCK) \
|
||||
{ \
|
||||
block* blockPtr = &BLOCK; \
|
||||
blockPtr->nbInsts = 0; \
|
||||
instruction* instPtr = NULL;
|
||||
|
||||
#define BLOCK_END() }
|
||||
|
||||
#define BLOCK_ADD(CODE_ASM, CODE) \
|
||||
const uint8_t CAT(code, __LINE__)[] = CODE; \
|
||||
instPtr = newInstruction(CAT(code, __LINE__), sizeof(CAT(code, __LINE__)), CODE_ASM, NULL, 0); \
|
||||
addInstructionToBlock(blockPtr, instPtr);
|
||||
|
||||
#define BLOCK_ADD_CHECK(CODE_ASM, CODE, REGVALUES) \
|
||||
const uint8_t CAT(code, __LINE__)[] = CODE; \
|
||||
const reg_value CAT(regValues, __LINE__)[] = REGVALUES; \
|
||||
instPtr = newInstruction(CAT(code, __LINE__), sizeof(CAT(code, __LINE__)), CODE_ASM, CAT(regValues, __LINE__), sizeof(CAT(regValues, __LINE__)) / sizeof(reg_value)); \
|
||||
addInstructionToBlock(blockPtr, instPtr);
|
||||
|
||||
#define V(...) { __VA_ARGS__ }
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
instruction* newInstruction(const uint8_t * _code, uint32_t _codeSize, const char* _asmStr, const reg_value* _values, uint32_t _nbValues);
|
||||
void addInstructionToBlock(block* _b, instruction* _i);
|
||||
uint32_t loadBlock(uc_engine *_uc, block* _block, uint32_t _at);
|
||||
void freeBlock(block* _block);
|
||||
const char* getRegisterName(uint32_t _regid);
|
||||
uint32_t getRegisterValue(uc_engine *uc, uint32_t _regid);
|
||||
instruction* getInstruction(block * _block, uint32_t _addr);
|
||||
void initRegisters(uc_engine *uc);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
void hook_code_test_i386_shl(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint32_t i;
|
||||
block* b = (block*)user_data;
|
||||
instruction* currInst = getInstruction(b, (uint32_t)address);
|
||||
assert_true(currInst != NULL);
|
||||
|
||||
printf("|\teip=%08x - %s\n", (uint32_t)address, currInst->asmStr);
|
||||
|
||||
for (i = 0; i < currInst->nbValues; i++)
|
||||
{
|
||||
uint32_t regValue = getRegisterValue(uc, currInst->values[i].regId);
|
||||
printf("|\t\ttesting %s : ", getRegisterName(currInst->values[i].regId));
|
||||
assert_int_equal(regValue & currInst->values[i].mask, currInst->values[i].regValue);
|
||||
printf("ok\n");
|
||||
}
|
||||
|
||||
if (currInst->code[0] == 0xCC)
|
||||
OK(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
printf("hook_mem_invalid: UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", type, addr); break;
|
||||
case UC_MEM_READ_UNMAPPED:
|
||||
printf("hook_mem_invalid: Read from invalid memory at 0x%" PRIx64 ", data size = %u\n", addr, size); break;
|
||||
case UC_MEM_WRITE_UNMAPPED:
|
||||
printf("hook_mem_invalid: Write to invalid memory at 0x%" PRIx64 ", data size = %u, data value = 0x%" PRIx64 "\n", addr, size, value); break;
|
||||
case UC_MEM_FETCH_PROT:
|
||||
printf("hook_mem_invalid: Fetch from non-executable memory at 0x%" PRIx64 "\n", addr); break;
|
||||
case UC_MEM_WRITE_PROT:
|
||||
printf("hook_mem_invalid: Write to non-writeable memory at 0x%" PRIx64 ", data size = %u, data value = 0x%" PRIx64 "\n", addr, size, value); break;
|
||||
case UC_MEM_READ_PROT:
|
||||
printf("hook_mem_invalid: Read from non-readable memory at 0x%" PRIx64 ", data size = %u\n", addr, size); break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define ADDR_CODE 0x100000
|
||||
#define ADDR_STACK 0x200000
|
||||
|
||||
|
||||
|
||||
static void test_i386_shl_cl(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov ebx, 3Ch", V(0xBB, 0x3C, 0x00, 0x00, 0x00));
|
||||
BLOCK_ADD_CHECK("mov cl, 2", V(0xB1, 0x02), V(V(UC_X86_REG_EBX, 0x3C, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("shl ebx, cl", V(0xD3, 0xE3), V(V(UC_X86_REG_CL, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("lahf", V(0x9F), V(V(UC_X86_REG_EBX, 0xF0, NO_MASK), V(UC_X86_REG_EFLAGS, 0x4, ALL_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_AH, 0x4, PF_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_shl_imm(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov ebx, 3Ch", V(0xBB, 0x3C, 0x00, 0x00, 0x00));
|
||||
BLOCK_ADD( "shl ebx, 2", V(0xC1, 0xE3, 0x02));
|
||||
BLOCK_ADD_CHECK("lahf", V(0x9F), V(V(UC_X86_REG_EBX, 0xF0, NO_MASK), V(UC_X86_REG_EFLAGS, 0x4, ALL_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_AH, 0x4, PF_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_enter_leave(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, ADDR_STACK - 0x1000, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov esp, 0x200000", V(0xBC, 0x00, 0x00, 0x20, 0x00));
|
||||
BLOCK_ADD_CHECK("mov eax, 1", V(0xB8, 0x01, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("call 0x100015", V(0xE8, 0x06, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x1, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 3", V(0xB8, 0x03, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_EAX, 0x3, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("enter 0x10,0", V(0xC8, 0x10, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 2", V(0xB8, 0x02, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000 - 4 - 4 - 0x10, NO_MASK), V(UC_X86_REG_EBP, 0x200000 - 4 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("leave", V(0xC9), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("ret", V(0xC3), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
static void test_i386_enter_nested_leave(void **state)
|
||||
{
|
||||
uc_engine *uc;
|
||||
uc_hook trace1;
|
||||
block b;
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
OK(uc_open(UC_ARCH_X86, UC_MODE_32, &uc));
|
||||
OK(uc_mem_map(uc, ADDR_CODE, 0x1000, UC_PROT_ALL));
|
||||
OK(uc_mem_map(uc, ADDR_STACK - 0x1000, 0x1000, UC_PROT_ALL));
|
||||
|
||||
initRegisters(uc);
|
||||
|
||||
BLOCK_START(b);
|
||||
BLOCK_ADD( "mov esp, 0x200000", V(0xBC, 0x00, 0x00, 0x20, 0x00));
|
||||
BLOCK_ADD_CHECK("mov eax, 1", V(0xB8, 0x01, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("call 0x100015", V(0xE8, 0x06, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x1, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 3", V(0xB8, 0x03, 0x00, 0x00, 0x00), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("int3", V(0xCC), V(V(UC_X86_REG_EAX, 0x3, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov ebp, esp", V(0x89, 0xE5), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("enter 0x10,1", V(0xC8, 0x10, 0x00, 0x01), V(V(UC_X86_REG_EBP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("mov eax, 2", V(0xB8, 0x02, 0x00, 0x00, 0x00), V(V(UC_X86_REG_ESP, 0x200000 - 4 - 2*4 - 0x10, NO_MASK), V(UC_X86_REG_EBP, 0x200000 - 4 - 4, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("leave", V(0xC9), V(V(UC_X86_REG_EAX, 0x2, NO_MASK)));
|
||||
BLOCK_ADD_CHECK("ret", V(0xC3), V(V(UC_X86_REG_ESP, 0x200000 - 4, NO_MASK)));
|
||||
BLOCK_END();
|
||||
|
||||
loadBlock(uc, &b, ADDR_CODE);
|
||||
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, hook_code_test_i386_shl, &b, 1, 0));
|
||||
OK(uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL, 1, 0));
|
||||
|
||||
// emulate machine code in infinite time
|
||||
OK(uc_emu_start(uc, ADDR_CODE, ADDR_CODE + b.size, 0, 0));
|
||||
|
||||
freeBlock(&b);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
int main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
|
||||
cmocka_unit_test(test_i386_shl_cl),
|
||||
cmocka_unit_test(test_i386_shl_imm),
|
||||
cmocka_unit_test(test_i386_enter_leave),
|
||||
cmocka_unit_test(test_i386_enter_nested_leave),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
instruction* newInstruction(const uint8_t * _code, uint32_t _codeSize, const char* _asmStr, const reg_value* _values, uint32_t _nbValues)
|
||||
{
|
||||
instruction* inst = (instruction*)malloc(sizeof(instruction));
|
||||
|
||||
inst->asmStr = _asmStr;
|
||||
memcpy(inst->code, _code, _codeSize);
|
||||
inst->codeSize = _codeSize;
|
||||
inst->nbValues = 0;
|
||||
if (_values)
|
||||
{
|
||||
inst->values = (reg_value*)malloc(_nbValues*sizeof(reg_value));
|
||||
memcpy(inst->values, _values, _nbValues*sizeof(reg_value));
|
||||
inst->nbValues = _nbValues;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void addInstructionToBlock(block* _b, instruction* _i)
|
||||
{
|
||||
_b->insts[_b->nbInsts++] = _i;
|
||||
}
|
||||
|
||||
uint32_t loadBlock(uc_engine *_uc, block* _block, uint32_t _at)
|
||||
{
|
||||
uint32_t i, j, offset;
|
||||
|
||||
for (i = 0, offset = 0; i < _block->nbInsts; i++)
|
||||
{
|
||||
const uint32_t codeSize = _block->insts[i]->codeSize;
|
||||
const uint8_t* code = _block->insts[i]->code;
|
||||
_block->insts[i]->addr = _at + offset;
|
||||
printf("load: %08X: ", _block->insts[i]->addr);
|
||||
for (j = 0; j < codeSize; j++) printf("%02X ", code[j]);
|
||||
for (j = 0; j < 15 - codeSize; j++) printf(" ");
|
||||
printf("%s\n", _block->insts[i]->asmStr);
|
||||
OK(uc_mem_write(_uc, _at + offset, code, codeSize));
|
||||
offset += codeSize;
|
||||
}
|
||||
_block->size = offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void freeBlock(block* _block)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < _block->nbInsts; i++)
|
||||
{
|
||||
if (_block->insts[i]->nbValues > 0)
|
||||
free(_block->insts[i]->values);
|
||||
free(_block->insts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void initRegisters(uc_engine *uc)
|
||||
{
|
||||
// initialize machine registers
|
||||
uint32_t zero = 0;
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EAX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EBX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_ECX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EDX, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EBP, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_ESP, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EDI, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_ESI, &zero));
|
||||
OK(uc_reg_write(uc, UC_X86_REG_EFLAGS, &zero));
|
||||
}
|
||||
|
||||
instruction* getInstruction(block* _block, uint32_t _addr)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < _block->nbInsts; i++)
|
||||
{
|
||||
if (_block->insts[i]->addr == _addr)
|
||||
return _block->insts[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* getRegisterName(uint32_t _regid)
|
||||
{
|
||||
switch (_regid)
|
||||
{
|
||||
//8
|
||||
case UC_X86_REG_AH: return "AH";
|
||||
case UC_X86_REG_AL: return "AL";
|
||||
case UC_X86_REG_BH: return "BH";
|
||||
case UC_X86_REG_BL: return "BL";
|
||||
case UC_X86_REG_CL: return "CL";
|
||||
case UC_X86_REG_CH: return "CH";
|
||||
case UC_X86_REG_DH: return "DH";
|
||||
case UC_X86_REG_DL: return "DL";
|
||||
//16
|
||||
case UC_X86_REG_AX: return "AX";
|
||||
case UC_X86_REG_BX: return "BX";
|
||||
case UC_X86_REG_CX: return "CX";
|
||||
case UC_X86_REG_DX: return "DX";
|
||||
//32
|
||||
case UC_X86_REG_EAX: return "EAX";
|
||||
case UC_X86_REG_EBX: return "EBX";
|
||||
case UC_X86_REG_ECX: return "ECX";
|
||||
case UC_X86_REG_EDX: return "EDX";
|
||||
case UC_X86_REG_EDI: return "EDI";
|
||||
case UC_X86_REG_ESI: return "ESI";
|
||||
case UC_X86_REG_EBP: return "EBP";
|
||||
case UC_X86_REG_ESP: return "ESP";
|
||||
case UC_X86_REG_EIP: return "EIP";
|
||||
case UC_X86_REG_EFLAGS: return "EFLAGS";
|
||||
|
||||
default: fail();
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
uint32_t getRegisterValue(uc_engine *uc, uint32_t _regid)
|
||||
{
|
||||
switch (_regid)
|
||||
{
|
||||
//8
|
||||
case UC_X86_REG_AH: case UC_X86_REG_AL:
|
||||
case UC_X86_REG_BH: case UC_X86_REG_BL:
|
||||
case UC_X86_REG_CL: case UC_X86_REG_CH:
|
||||
case UC_X86_REG_DH: case UC_X86_REG_DL:
|
||||
{
|
||||
uint8_t val = 0;
|
||||
OK(uc_reg_read(uc, _regid, &val));
|
||||
return val;
|
||||
}
|
||||
//16
|
||||
case UC_X86_REG_AX: case UC_X86_REG_BX:
|
||||
case UC_X86_REG_CX: case UC_X86_REG_DX:
|
||||
{
|
||||
uint16_t val = 0;
|
||||
OK(uc_reg_read(uc, _regid, &val));
|
||||
return val;
|
||||
}
|
||||
//32
|
||||
case UC_X86_REG_EAX: case UC_X86_REG_EBX:
|
||||
case UC_X86_REG_ECX: case UC_X86_REG_EDX:
|
||||
case UC_X86_REG_EDI: case UC_X86_REG_ESI:
|
||||
case UC_X86_REG_EBP: case UC_X86_REG_ESP:
|
||||
case UC_X86_REG_EIP: case UC_X86_REG_EFLAGS:
|
||||
{
|
||||
uint32_t val = 0;
|
||||
OK(uc_reg_read(uc, _regid, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
default: fail();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
#include "unicorn_test.h"
|
||||
#include "unicorn/unicorn.h"
|
||||
|
||||
/*
|
||||
Two tests here for software paging
|
||||
Low paging: Test paging using virtual addresses already mapped by Unicorn
|
||||
High paging: Test paging using virtual addresses not mapped by Unicorn
|
||||
*/
|
||||
|
||||
static void test_low_paging(void **state) {
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
int r_eax;
|
||||
|
||||
/* The following x86 code will map emulated physical memory
|
||||
to virtual memory using pages and attempt
|
||||
to read/write from virtual memory
|
||||
|
||||
Specifically, the virtual memory address range
|
||||
has been mapped by Unicorn (0x7FF000 - 0x7FFFFF)
|
||||
|
||||
Memory area purposes:
|
||||
0x1000 = page directory
|
||||
0x2000 = page table (identity map first 4 MiB)
|
||||
0x3000 = page table (0x007FF000 -> 0x00004000)
|
||||
0x4000 = data area (0xBEEF)
|
||||
*/
|
||||
const uint8_t code[] = {
|
||||
/* Zero memory for page directories and page tables */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB9, 0x00, 0x10, 0x00, 0x00, /* MOV ECX, 0x1000 */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
0xF3, 0xAB, /* REP STOSD */
|
||||
|
||||
/* Load DWORD [0x4000] with 0xDEADBEEF to retrieve later */
|
||||
0xBF, 0x00, 0x40, 0x00, 0x00, /* MOV EDI, 0x4000 */
|
||||
0xB8, 0xEF, 0xBE, 0x00, 0x00, /* MOV EAX, 0xBEEF */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Identity map the first 4MiB of memory */
|
||||
0xB9, 0x00, 0x04, 0x00, 0x00, /* MOV ECX, 0x400 */
|
||||
0xBF, 0x00, 0x20, 0x00, 0x00, /* MOV EDI, 0x2000 */
|
||||
0xB8, 0x03, 0x00, 0x00, 0x00, /* MOV EAX, 3 */
|
||||
/* aLoop: */
|
||||
0xAB, /* STOSD */
|
||||
0x05, 0x00, 0x10, 0x00, 0x00, /* ADD EAX, 0x1000 */
|
||||
0xE2, 0xF8, /* LOOP aLoop */
|
||||
|
||||
/* Map physical address 0x4000 to virtual address 0x7FF000 */
|
||||
0xBF, 0xFC, 0x3F, 0x00, 0x00, /* MOV EDI, 0x3FFC */
|
||||
0xB8, 0x03, 0x40, 0x00, 0x00, /* MOV EAX, 0x4003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Add page tables into page directory */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB8, 0x03, 0x20, 0x00, 0x00, /* MOV EAX, 0x2003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
0xBF, 0x04, 0x10, 0x00, 0x00, /* MOV EDI, 0x1004 */
|
||||
0xB8, 0x03, 0x30, 0x00, 0x00, /* MOV EAX, 0x3003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Load the page directory register */
|
||||
0xB8, 0x00, 0x10, 0x00, 0x00, /* MOV EAX, 0x1000 */
|
||||
0x0F, 0x22, 0xD8, /* MOV CR3, EAX */
|
||||
|
||||
/* Enable paging */
|
||||
0x0F, 0x20, 0xC0, /* MOV EAX, CR0 */
|
||||
0x0D, 0x00, 0x00, 0x00, 0x80, /* OR EAX, 0x80000000 */
|
||||
0x0F, 0x22, 0xC0, /* MOV CR0, EAX */
|
||||
|
||||
/* Clear EAX */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
|
||||
/* Load using virtual memory address; EAX = 0xBEEF */
|
||||
0xBE, 0x00, 0xF0, 0x7F, 0x00, /* MOV ESI, 0x7FF000 */
|
||||
0x8B, 0x06, /* MOV EAX, [ESI] */
|
||||
0xF4, /* HLT */
|
||||
};
|
||||
|
||||
/* Initialise X86-32bit mode */
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Map 8MB of memory at base address 0 */
|
||||
err = uc_mem_map(uc, 0, (8 * 1024 * 1024), UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Write code into memory at address 0 */
|
||||
err = uc_mem_write(uc, 0, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Start emulation */
|
||||
err = uc_emu_start(uc, 0, sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* The code should have loaded 0xBEEF into EAX */
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
|
||||
assert_int_equal(r_eax, 0xBEEF);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
static void test_high_paging(void **state) {
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
int r_eax;
|
||||
|
||||
/* The following x86 code will map emulated physical memory
|
||||
to virtual memory using pages and attempt
|
||||
to read/write from virtual memory
|
||||
|
||||
Specifically, the virtual memory address range
|
||||
has not been mapped by UC (0xFFFFF000 - 0xFFFFFFFF)
|
||||
|
||||
Memory area purposes:
|
||||
0x1000 = page directory
|
||||
0x2000 = page table (identity map first 4 MiB)
|
||||
0x3000 = page table (0xFFFFF000 -> 0x00004000)
|
||||
0x4000 = data area (0xDEADBEEF)
|
||||
*/
|
||||
const uint8_t code[] = {
|
||||
/* Zero memory for page directories and page tables */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB9, 0x00, 0x10, 0x00, 0x00, /* MOV ECX, 0x1000 */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
0xF3, 0xAB, /* REP STOSD */
|
||||
|
||||
/* Load DWORD [0x4000] with 0xDEADBEEF to retrieve later */
|
||||
0xBF, 0x00, 0x40, 0x00, 0x00, /* MOV EDI, 0x4000 */
|
||||
0xB8, 0xEF, 0xBE, 0x00, 0x00, /* MOV EAX, 0xBEEF */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Identity map the first 4MiB of memory */
|
||||
0xB9, 0x00, 0x04, 0x00, 0x00, /* MOV ECX, 0x400 */
|
||||
0xBF, 0x00, 0x20, 0x00, 0x00, /* MOV EDI, 0x2000 */
|
||||
0xB8, 0x03, 0x00, 0x00, 0x00, /* MOV EAX, 3 */
|
||||
/* aLoop: */
|
||||
0xAB, /* STOSD */
|
||||
0x05, 0x00, 0x10, 0x00, 0x00, /* ADD EAX, 0x1000 */
|
||||
0xE2, 0xF8, /* LOOP aLoop */
|
||||
|
||||
/* Map physical address 0x4000 to virtual address 0xFFFFF000 */
|
||||
0xBF, 0xFC, 0x3F, 0x00, 0x00, /* MOV EDI, 0x3FFC */
|
||||
0xB8, 0x03, 0x40, 0x00, 0x00, /* MOV EAX, 0x4003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Add page tables into page directory */
|
||||
0xBF, 0x00, 0x10, 0x00, 0x00, /* MOV EDI, 0x1000 */
|
||||
0xB8, 0x03, 0x20, 0x00, 0x00, /* MOV EAX, 0x2003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
0xBF, 0xFC, 0x1F, 0x00, 0x00, /* MOV EDI, 0x1FFC */
|
||||
0xB8, 0x03, 0x30, 0x00, 0x00, /* MOV EAX, 0x3003 */
|
||||
0x89, 0x07, /* MOV [EDI], EAX */
|
||||
|
||||
/* Load the page directory register */
|
||||
0xB8, 0x00, 0x10, 0x00, 0x00, /* MOV EAX, 0x1000 */
|
||||
0x0F, 0x22, 0xD8, /* MOV CR3, EAX */
|
||||
|
||||
/* Enable paging */
|
||||
0x0F, 0x20, 0xC0, /* MOV EAX, CR0 */
|
||||
0x0D, 0x00, 0x00, 0x00, 0x80, /* OR EAX, 0x80000000 */
|
||||
0x0F, 0x22, 0xC0, /* MOV CR0, EAX */
|
||||
|
||||
/* Clear EAX */
|
||||
0x31, 0xC0, /* XOR EAX, EAX */
|
||||
|
||||
/* Load using virtual memory address; EAX = 0xBEEF */
|
||||
0xBE, 0x00, 0xF0, 0xFF, 0xFF, /* MOV ESI, 0xFFFFF000 */
|
||||
0x8B, 0x06, /* MOV EAX, [ESI] */
|
||||
0xF4, /* HLT */
|
||||
};
|
||||
|
||||
/* Initialise X86-32bit mode */
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Map 4MB of memory at base address 0 */
|
||||
err = uc_mem_map(uc, 0, (4 * 1024 * 1024), UC_PROT_ALL);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Write code into memory at address 0 */
|
||||
err = uc_mem_write(uc, 0, code, sizeof(code));
|
||||
uc_assert_success(err);
|
||||
|
||||
/* Start emulation */
|
||||
err = uc_emu_start(uc, 0, sizeof(code), 0, 0);
|
||||
uc_assert_success(err);
|
||||
|
||||
/* The code should have loaded 0xBEEF into EAX */
|
||||
uc_reg_read(uc, UC_X86_REG_EAX, &r_eax);
|
||||
assert_int_equal(r_eax, 0xBEEF);
|
||||
|
||||
uc_close(uc);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
|
||||
int main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(test_low_paging),
|
||||
cmocka_unit_test(test_high_paging),
|
||||
};
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
@@ -1,57 +1,25 @@
|
||||
#ifndef UNICORN_TEST_H
|
||||
#define UNICORN_TEST_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <sys/stat.h>
|
||||
#include "acutest.h"
|
||||
|
||||
/**
|
||||
* Assert that err matches expect
|
||||
*/
|
||||
#define uc_assert_err(expect, err) \
|
||||
do { \
|
||||
uc_err __err = err; \
|
||||
if (__err != expect) { \
|
||||
fail_msg("%s", uc_strerror(__err)); \
|
||||
} \
|
||||
} while (0)
|
||||
#define uc_assert_err(expect, err) \
|
||||
do { \
|
||||
uc_err __err = err; \
|
||||
if (!TEST_CHECK(__err == expect)) { \
|
||||
TEST_MSG("%s", uc_strerror(__err)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Assert that err is UC_ERR_OK
|
||||
*/
|
||||
#define uc_assert_success(err) uc_assert_err(UC_ERR_OK, err)
|
||||
|
||||
/**
|
||||
* Assert that err is anything but UC_ERR_OK
|
||||
*
|
||||
* Note: Better to use uc_assert_err(<specific error>, err),
|
||||
* as this serves to document which errors a function will return
|
||||
* in various scenarios.
|
||||
*/
|
||||
#define uc_assert_fail(err) \
|
||||
do { \
|
||||
uc_err __err = err; \
|
||||
if (__err == UC_ERR_OK) { \
|
||||
fail_msg("%s", uc_strerror(__err)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
char * read_file(const char *filename, struct stat *info) {
|
||||
stat(filename, info);
|
||||
char *code = malloc(info->st_size);
|
||||
if (code == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
free(code);
|
||||
return NULL;
|
||||
}
|
||||
fread(code, info->st_size, 1, fp);
|
||||
return code;
|
||||
}
|
||||
#define OK(stat) uc_assert_err(UC_ERR_OK, stat)
|
||||
|
||||
#endif /* UNICORN_TEST_H */
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Zero memory for page directories and page tables
|
||||
mov $0x1000,%edi
|
||||
mov $0x1000,%ecx
|
||||
xor %eax,%eax
|
||||
rep stos %eax,(%edi)
|
||||
|
||||
// Load DWORD [0x4000] with 0xDEADBEEF to retrieve later
|
||||
mov $0x4000,%edi
|
||||
mov $0xBEEF,%eax
|
||||
mov %eax, (%edi)
|
||||
|
||||
// Identify map the first 4MiB of memory
|
||||
mov $0x400,%ecx
|
||||
mov $0x2000,%edi
|
||||
mov $3, %eax
|
||||
loop:
|
||||
stos %eax,(%edi)
|
||||
add $0x1000,%eax
|
||||
loop loop
|
||||
|
||||
// Map phyiscal address 0x4000 to cirtual address 0x7FF000
|
||||
mov $0x3ffc,%edi
|
||||
mov $0x4003,%eax
|
||||
mov %eax, (%edi)
|
||||
|
||||
// Add page tables into page directory
|
||||
mov $0x1000, %edi
|
||||
mov $0x2003, %eax
|
||||
mov %eax, (%edi)
|
||||
mov $0x1004, %edi
|
||||
mov $0x3003, %eax
|
||||
mov %eax, (%edi)
|
||||
|
||||
// Load the page directory register
|
||||
mov $0x1000, %eax
|
||||
mov %eax, %cr3
|
||||
|
||||
// Enable paging
|
||||
mov %cr0, %eax
|
||||
or $0x80000000, %eax
|
||||
|
||||
// Clear EAX
|
||||
mov %eax, %cr0
|
||||
|
||||
//Load using virtual memory address; EAX = 0xBEEF
|
||||
xor %eax,%eax
|
||||
mov $0x7FF000, %esi
|
||||
mov (%esi), %eax
|
||||
hlt
|
||||
Reference in New Issue
Block a user