Merge branch 'master' into m1

This commit is contained in:
Nguyen Anh Quynh
2016-03-09 15:13:42 +08:00
51 changed files with 760 additions and 221 deletions

30
tests/regress/LICENSE Normal file
View File

@@ -0,0 +1,30 @@
This is the software license for Unicorn regression tests. The regression tests
are written by several Unicorn contributors (See CREDITS.TXT) and maintained by
Hoang-Vu Dang <dang.hvu@gmail.com>
Copyright (c) 2015, Unicorn contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the developer(s) nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

167
tests/regress/jumping.py Executable file
View File

@@ -0,0 +1,167 @@
#!/usr/bin/env python
# Mariano Graziano
from unicorn import *
from unicorn.x86_const import *
import regress
#echo -ne "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90" | ndisasm - -b64
#00000000 4831C0 xor rax,rax
#00000003 48B8040000000000 mov rax,0x4
# -0000
#0000000D 483D05000000 cmp rax,0x5
#00000013 7405 jz 0x1a
#00000015 E90F000000 jmp qword 0x29
#0000001A 48BABEBA00000000 mov rdx,0xbabe
# -0000
#00000024 E90F000000 jmp qword 0x38
#00000029 48BACAC000000000 mov rdx,0xc0ca
# -0000
#00000033 E900000000 jmp qword 0x38
#00000038 90 nop
mu = 0
zf = 1 # (0:clear, 1:set)
class Init(regress.RegressTest):
def clear_zf(self):
eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS)
eflags = eflags_cur & ~(1 << 6)
#eflags = 0x0
print "[clear_zf] - eflags from %x to %x" % (eflags_cur, eflags)
if eflags != eflags_cur:
print "[clear_zf] - writing new eflags..."
mu.reg_write(UC_X86_REG_EFLAGS, eflags)
def set_zf(self):
eflags_cur = mu.reg_read(UC_X86_REG_EFLAGS)
eflags = eflags_cur | (1 << 6)
#eflags = 0xFFFFFFFF
print "[set_zf] - eflags from %x to %x" % (eflags_cur, eflags)
if eflags != eflags_cur:
print "[set_zf] - writing new eflags..."
mu.reg_write(UC_X86_REG_EFLAGS, eflags)
def handle_zf(self, zf):
print "[handle_zf] - eflags " , zf
if zf == 0: self.clear_zf()
else: self.set_zf()
def multipath(self):
print "[multipath] - handling ZF (%s) - default" % zf
self.handle_zf(zf)
# callback for tracing basic blocks
def hook_block(self, uc, address, size, user_data):
print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size))
# callback for tracing instructions
def hook_code(self, uc, address, size, user_data):
print(">>> Tracing instruction at 0x%x, instruction size = %u" %(address, size))
rax = mu.reg_read(UC_X86_REG_RAX)
rbx = mu.reg_read(UC_X86_REG_RBX)
rcx = mu.reg_read(UC_X86_REG_RCX)
rdx = mu.reg_read(UC_X86_REG_RDX)
rsi = mu.reg_read(UC_X86_REG_RSI)
rdi = mu.reg_read(UC_X86_REG_RDI)
r8 = mu.reg_read(UC_X86_REG_R8)
r9 = mu.reg_read(UC_X86_REG_R9)
r10 = mu.reg_read(UC_X86_REG_R10)
r11 = mu.reg_read(UC_X86_REG_R11)
r12 = mu.reg_read(UC_X86_REG_R12)
r13 = mu.reg_read(UC_X86_REG_R13)
r14 = mu.reg_read(UC_X86_REG_R14)
r15 = mu.reg_read(UC_X86_REG_R15)
eflags = mu.reg_read(UC_X86_REG_EFLAGS)
print(">>> RAX = %x" %rax)
print(">>> RBX = %x" %rbx)
print(">>> RCX = %x" %rcx)
print(">>> RDX = %x" %rdx)
print(">>> RSI = %x" %rsi)
print(">>> RDI = %x" %rdi)
print(">>> R8 = %x" %r8)
print(">>> R9 = %x" %r9)
print(">>> R10 = %x" %r10)
print(">>> R11 = %x" %r11)
print(">>> R12 = %x" %r12)
print(">>> R13 = %x" %r13)
print(">>> R14 = %x" %r14)
print(">>> R15 = %x" %r15)
print(">>> ELAGS = %x" %eflags)
print "-"*11
self.multipath()
print "-"*11
# callback for tracing memory access (READ or WRITE)
def hook_mem_access(self, uc, access, address, size, value, user_data):
if access == UC_MEM_WRITE:
print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \
%(address, size, value))
else: # READ
print(">>> Memory is being READ at 0x%x, data size = %u" \
%(address, size))
# callback for tracing invalid memory access (READ or WRITE)
def hook_mem_invalid(self, uc, access, address, size, value, user_data):
print("[ HOOK_MEM_INVALID - Address: %s ]" % hex(address))
if access == UC_MEM_WRITE_UNMAPPED:
print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" %(address, size, value))
return True
else:
print(">>> Missing memory is being READ at 0x%x, data size = %u, data value = 0x%x" %(address, size, value))
return True
def hook_mem_fetch_unmapped(self, uc, access, address, size, value, user_data):
print("[ HOOK_MEM_FETCH - Address: %s ]" % hex(address))
print("[ mem_fetch_unmapped: faulting address at %s ]" % hex(address).strip("L"))
return True
def runTest(self):
global mu
JUMP = "\x48\x31\xc0\x48\xb8\x04\x00\x00\x00\x00\x00\x00\x00\x48\x3d\x05\x00\x00\x00\x74\x05\xe9\x0f\x00\x00\x00\x48\xba\xbe\xba\x00\x00\x00\x00\x00\x00\xe9\x0f\x00\x00\x00\x48\xba\xca\xc0\x00\x00\x00\x00\x00\x00\xe9\x00\x00\x00\x00\x90"
ADDRESS = 0x1000000
print("Emulate x86_64 code")
# Initialize emulator in X86-64bit mode
mu = Uc(UC_ARCH_X86, UC_MODE_64)
# map 2MB memory for this emulation
mu.mem_map(ADDRESS, 2 * 1024 * 1024)
# write machine code to be emulated to memory
mu.mem_write(ADDRESS, JUMP)
# setup stack
mu.reg_write(UC_X86_REG_RSP, ADDRESS + 0x200000)
# tracing all basic blocks with customized callback
mu.hook_add(UC_HOOK_BLOCK, self.hook_block)
# tracing all instructions in range [ADDRESS, ADDRESS+0x60]
mu.hook_add(UC_HOOK_CODE, self.hook_code, None, ADDRESS, ADDRESS+0x60)
# tracing all memory READ & WRITE access
mu.hook_add(UC_HOOK_MEM_WRITE, self.hook_mem_access)
mu.hook_add(UC_HOOK_MEM_READ, self.hook_mem_access)
mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, self.hook_mem_fetch_unmapped)
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
try:
# emulate machine code in infinite time
mu.emu_start(ADDRESS, ADDRESS + len(JUMP))
except UcError as e:
print("ERROR: %s" % e)
rdx = mu.reg_read(UC_X86_REG_RDX)
self.assertEqual(rdx, 0xbabe, "RDX contains the wrong value. Eflags modification failed.")
if __name__ == '__main__':
regress.main()

View File

@@ -5,7 +5,7 @@ CFLAGS += -lcmocka -lunicorn
CFLAGS += -I ../../include
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \
test_tb_x86 test_multihook test_pc_change
test_tb_x86 test_multihook test_pc_change test_x86_soft_paging
.PHONY: all
all: ${ALL_TESTS}
@@ -25,6 +25,7 @@ test: ${ALL_TESTS}
./test_tb_x86
./test_multihook
./test_pc_change
./test_x86_soft_paging
test_sanity: test_sanity.c
test_x86: test_x86.c
@@ -34,6 +35,7 @@ test_mem_high: test_mem_high.c
test_tb_x86: test_tb_x86.c
test_multihook: test_multihook.c
test_pc_change: test_pc_change.c
test_x86_soft_paging: test_x86_soft_paging.c
${ALL_TESTS}:
${CC} ${CFLAGS} -o $@ $^

View File

@@ -79,7 +79,7 @@ static void test_high_address_reads(void **state)
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, (uint64_t)1, (uint64_t)0));
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);

View File

@@ -158,6 +158,15 @@ static void test_strange_map(void **state)
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 write(uc_engine* uc, uint64_t addr, uint64_t len){
uint8_t* buff = alloca(len);
memset(buff,0,len);
@@ -220,6 +229,7 @@ int main(void) {
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

@@ -96,8 +96,8 @@ static void test_basic_blocks(void **state)
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, (uint64_t)1, (uint64_t)0));
OK(uc_hook_add(uc, &trace2, UC_HOOK_BLOCK, test_basic_blocks_hook2, &bbtest, (uint64_t)1, (uint64_t)0));
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));
}

View File

@@ -83,7 +83,7 @@ static void test_pc_change(void **state)
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, (uint64_t)1, (uint64_t)0));
OK(uc_hook_add(uc, &trace1, UC_HOOK_CODE, test_code_hook, NULL, 1, 0));
OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0));

View File

@@ -273,16 +273,16 @@ static void test_tb_x86_64_32_imul_Gv_Ev_Ib(void **state)
UC_HOOK_CODE,
hook_code32,
NULL,
(uint64_t)1,
(uint64_t)0));
1,
0));
uc_assert_success(uc_hook_add(uc,
&trace2,
UC_HOOK_MEM_VALID,
hook_mem32,
NULL,
(uint64_t)1,
(uint64_t)0));
1,
0));
uc_assert_success(uc_emu_start(uc,
#ifdef RIP_NEXT_TO_THE_SELFMODIFY_OPCODE

View File

@@ -85,7 +85,7 @@ static void test_basic_blocks(void **state)
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, (uint64_t)1, (uint64_t)0));
OK(uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, test_basic_blocks_hook, &bbtest, 1, 0));
OK(uc_emu_start(uc, address, address+sizeof(code), 0, 0));
}
@@ -144,11 +144,11 @@ static void test_i386(void **state)
uc_assert_success(err);
// tracing all basic blocks with customized callback
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
uc_assert_success(err);
// tracing all instruction by having @begin > @end
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
uc_assert_success(err);
// emulate machine code in infinite time
@@ -194,11 +194,11 @@ static void test_i386_jump(void **state)
uc_assert_success(err);
// tracing 1 basic block with customized callback
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)address, (uint64_t)address);
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, address, address);
uc_assert_success(err);
// tracing 1 instruction at address
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)address, (uint64_t)address);
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, address, address);
uc_assert_success(err);
// emulate machine code in infinite time
@@ -302,19 +302,19 @@ static void test_i386_inout(void **state)
uc_assert_success(err);
// tracing all basic blocks with customized callback
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
uc_assert_success(err);
// tracing all instructions
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0);
uc_assert_success(err);
// uc IN instruction
err = uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, UC_X86_INS_IN);
err = uc_hook_add(uc, &trace3, UC_HOOK_INSN, hook_in, NULL, 1, 0, UC_X86_INS_IN);
uc_assert_success(err);
// uc OUT instruction
err = uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, UC_X86_INS_OUT);
err = uc_hook_add(uc, &trace4, UC_HOOK_INSN, hook_out, NULL, 1, 0, UC_X86_INS_OUT);
uc_assert_success(err);
// emulate machine code in infinite time
@@ -566,19 +566,19 @@ static void test_x86_64(void **state)
uc_assert_success(uc_reg_write(uc, UC_X86_REG_R15, &r15));
// tracing all basic blocks with customized callback
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0);
uc_assert_success(err);
// tracing all instructions in the range [address, address+20]
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, (uint64_t)address, (uint64_t)(address+20));
err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code64, NULL, address, address+20);
uc_assert_success(err);
// tracing all memory WRITE access (with @begin > @end)
err = uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace3, UC_HOOK_MEM_WRITE, hook_mem64, NULL, 1, 0);
uc_assert_success(err);
// tracing all memory READ access (with @begin > @end)
err = uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, (uint64_t)1, (uint64_t)0);
err = uc_hook_add(uc, &trace4, UC_HOOK_MEM_READ, hook_mem64, NULL, 1, 0);
uc_assert_success(err);
// emulate machine code in infinite time (last param = 0), or when
@@ -662,7 +662,7 @@ static void test_x86_64_syscall(void **state)
uc_assert_success(err);
// hook interrupts for syscall
err = uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, UC_X86_INS_SYSCALL);
err = uc_hook_add(uc, &trace1, UC_HOOK_INSN, hook_syscall, NULL, 1, 0, UC_X86_INS_SYSCALL);
uc_assert_success(err);
// initialize machine registers

View File

@@ -0,0 +1,210 @@
#include "unicorn_test.h"
#include <inttypes.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);
}