import Unicorn2

This commit is contained in:
Nguyen Anh Quynh
2021-10-03 22:14:44 +08:00
parent 772558119a
commit aaaea14214
837 changed files with 368717 additions and 200912 deletions

View File

@@ -1,3 +0,0 @@
!*.c
test_*
*.bin

View File

@@ -1,65 +0,0 @@
CFLAGS += -Wall -Werror -Wno-unused-function -g
CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
CFLAGS += -L ../../ -I ../../include
CFLAGS += -L ../../cmocka/src -I ../../cmocka/include
CFLAGS += -L /usr/local/lib -I /usr/local/include
ASFLAGS += --32
OBJCOPY = objcopy
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
LDLIBS += -pthread
ifeq ($(UNAME_S), Linux)
LDLIBS += -lrt
else ifeq ($(UNAME_S), Darwin)
OBJCOPY = gobjcopy
ASFLAGS = -arch i386
endif
LDLIBS += -lcmocka -lunicorn
EXECUTE_VARS = LD_LIBRARY_PATH=../../cmocka/src:../../ DYLD_LIBRARY_PATH=../../
ifeq ($(UNICORN_ASAN),yes)
CC = clang -fsanitize=address -fno-omit-frame-pointer
CXX = clang++ -fsanitize=address -fno-omit-frame-pointer
AR = llvm-ar
LDFLAGS := -fsanitize=address ${LDFLAGS}
endif
ALL_TESTS_SOURCES = $(wildcard *.c)
TEST_ASSEMBLY = $(wildcard *.s)
TEST_PROGS = $(TEST_ASSEMBLY:%.s=%.o)
TEST_BINS = $(TEST_PROGS:%.o=%.bin)
ALL_TESTS = $(ALL_TESTS_SOURCES:%.c=%)
ifneq (,$(findstring x86,$(UNAME_M)))
ALL_TESTS += $(TEST_BINS)
endif
.PHONY: all
all: ${ALL_TESTS}
.PHONY: clean
clean:
rm -rf ${ALL_TESTS}
%.bin: %.o
${OBJCOPY} -O binary $^ $@
hexdump -C $@
.PHONY: test
test: all
${EXECUTE_VARS} ./test_sanity
${EXECUTE_VARS} ./test_x86
${EXECUTE_VARS} ./test_mem_map
${EXECUTE_VARS} ./test_mem_map_ptr
${EXECUTE_VARS} ./test_mem_high
${EXECUTE_VARS} ./test_multihook
${EXECUTE_VARS} ./test_pc_change
${EXECUTE_VARS} ./test_hookcounts
echo "skipping test_tb_x86"
echo "skipping test_x86_soft_paging"
echo "skipping test_hang"
echo "skipping test_x86_sh1_enter_leave"
echo "skipping test_x86_rip_bug"

1837
tests/unit/acutest.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
.text
sidt (esp)
sgdt (esp+6)

View File

@@ -1,6 +0,0 @@
dec %eax
mov (%eax), %eax
nop
nop
nop
nop

View File

@@ -1,9 +0,0 @@
.text
inc %ecx
inc %ecx
inc %ecx
inc %ecx
inc %ecx
inc %ecx
inc %edx
inc %edx

View File

@@ -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

249
tests/unit/test_arm.c Normal file
View File

@@ -0,0 +1,249 @@
#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_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);
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);
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);
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);
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);
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);
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);
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));
}
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 },
{ NULL, NULL }
};

8
tests/unit/test_arm64.c Normal file
View File

@@ -0,0 +1,8 @@
#include "unicorn_test.h"
const uint64_t code_start = 0x1000;
const uint64_t code_len = 0x4000;
TEST_LIST = {
{ NULL, NULL }
};

View File

@@ -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;
}

View File

@@ -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);;
}

View File

@@ -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);
}

8
tests/unit/test_m68k.c Normal file
View File

@@ -0,0 +1,8 @@
#include "unicorn_test.h"
const uint64_t code_start = 0x1000;
const uint64_t code_len = 0x4000;
TEST_LIST = {
{ NULL, NULL }
};

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

90
tests/unit/test_mips.c Normal file
View File

@@ -0,0 +1,90 @@
#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));
}
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},
{ NULL, NULL }
};

View File

@@ -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);
}

View File

@@ -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);
}

8
tests/unit/test_ppc.c Normal file
View File

@@ -0,0 +1,8 @@
#include "unicorn_test.h"
const uint64_t code_start = 0x1000;
const uint64_t code_len = 0x4000;
TEST_LIST = {
{ NULL, NULL }
};

8
tests/unit/test_riscv.c Normal file
View File

@@ -0,0 +1,8 @@
#include "unicorn_test.h"
const uint64_t code_start = 0x1000;
const uint64_t code_len = 0x4000;
TEST_LIST = {
{ NULL, NULL }
};

View File

@@ -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);
}

8
tests/unit/test_sparc.c Normal file
View File

@@ -0,0 +1,8 @@
#include "unicorn_test.h"
const uint64_t code_start = 0x1000;
const uint64_t code_len = 0x4000;
TEST_LIST = {
{ NULL, NULL }
};

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -1,12 +1,9 @@
#ifndef UNICORN_TEST_H
#define UNICORN_TEST_H
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <stdio.h>
#include <unicorn/unicorn.h>
#include <sys/stat.h>
#include "acutest.h"
/**
* Assert that err matches expect
@@ -14,44 +11,14 @@
#define uc_assert_err(expect, err) \
do { \
uc_err __err = err; \
if (__err != expect) { \
fail_msg("%s", uc_strerror(__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 */

View File

@@ -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