fix merge conflict
This commit is contained in:
@ -36,6 +36,7 @@ TESTS += emu_stop_in_hook_overrun
|
||||
TESTS += mips_branch_likely_issue
|
||||
TESTS += hook_extrainvoke
|
||||
TESTS += sysenter_hook_x86
|
||||
TESTS += emu_clear_errors
|
||||
|
||||
TESTS += memleak_x86
|
||||
TESTS += memleak_arm
|
||||
|
18
tests/regress/arm_vldr_invalid.py
Executable file
18
tests/regress/arm_vldr_invalid.py
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.arm_const import *
|
||||
|
||||
import regress
|
||||
|
||||
class VldrPcInsn(regress.RegressTest):
|
||||
|
||||
def runTest(self):
|
||||
uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)
|
||||
uc.mem_map(0x1000, 0x1000)
|
||||
uc.mem_write(0x1000, 'ed9f8a3d'.decode('hex')) # vldr s16, [pc, #244]
|
||||
# this will raise invalid insn
|
||||
uc.emu_start(0x1000, 0x1004)
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
154
tests/regress/emu_clear_errors.c
Normal file
154
tests/regress/emu_clear_errors.c
Normal file
@ -0,0 +1,154 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
static int count = 1;
|
||||
|
||||
bool cb_hookunmapped(uc_engine *uc, uc_mem_type type, uint64_t address, uint32_t size, int64_t value, void *user_data) {
|
||||
uint32_t pc = 0;
|
||||
uc_reg_read(uc, UC_X86_REG_EIP, &pc);
|
||||
fprintf(stderr, "mem unmapped: 0x%x type: %x address: 0x%"PRIx64" length: %x value: 0x%"PRIx64"\n",
|
||||
pc, type, address, size, value);
|
||||
|
||||
uc_err err = UC_ERR_OK;
|
||||
err = uc_emu_stop(uc);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "stop not ok");
|
||||
exit(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// move esi, dword ptr [ecx + eax + 0x28]
|
||||
// add esi, eax
|
||||
// lea eax, dword ptr [ebp - 4]
|
||||
// push eax
|
||||
// push 0x40
|
||||
// push 0x10
|
||||
// push esi
|
||||
// call some address
|
||||
#define CODE "\x8B\x74\x01\x28" \
|
||||
"\x0C\xF0" \
|
||||
"\x8D\x45\xFC" \
|
||||
"\x50" \
|
||||
"\x6A\x40" \
|
||||
"\x6A\x10" \
|
||||
"\x56" \
|
||||
"\xFF\x15\x20\x20\x00\x10"
|
||||
|
||||
int main() {
|
||||
uc_engine *uc;
|
||||
|
||||
uc_err err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_open\n", count++);
|
||||
|
||||
err = uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_ALL);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_mem_map: code\n", count++);
|
||||
|
||||
uint8_t code[0x1000];
|
||||
memset(code, 0x0, sizeof(code));
|
||||
memcpy(code, CODE, sizeof(CODE));
|
||||
|
||||
err = uc_mem_write(uc, 0x1000, code, sizeof(code));
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_mem_write: code\n", count++);
|
||||
|
||||
uint32_t eip = 0x1000;
|
||||
err = uc_reg_write(uc, UC_X86_REG_EIP, &eip);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_reg_write: eip\n", count++);
|
||||
|
||||
err = uc_mem_map(uc, 0x4000, 0x4000, UC_PROT_ALL);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_mem_map: stack\n", count++);
|
||||
|
||||
uint8_t stack[0x4000];
|
||||
memset(stack, 0x0, sizeof(stack));
|
||||
|
||||
err = uc_mem_write(uc, 0x4000, code, sizeof(code));
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_mem_write: stack\n", count++);
|
||||
|
||||
uint32_t esp = 0x6000;
|
||||
err = uc_reg_write(uc, UC_X86_REG_ESP, &esp);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_reg_write: esp\n", count++);
|
||||
|
||||
uint32_t ebp = 0x6000;
|
||||
err = uc_reg_write(uc, UC_X86_REG_EBP, &ebp);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_reg_write: ebp\n", count++);
|
||||
|
||||
uc_hook h1;
|
||||
|
||||
err = uc_hook_add(uc, &h1, UC_HOOK_MEM_UNMAPPED, cb_hookunmapped, NULL);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_hook_add\n", count++);
|
||||
|
||||
// this should execute only a single instruction at 0x1000, because
|
||||
// that instruction accesses invalid memory.
|
||||
err = uc_emu_start(uc, 0x1000, 0x100F, 0, 0);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_emu_start\n", count++);
|
||||
|
||||
// yes, not necessary, but to demonstrate the UC API is working as expected
|
||||
eip = 0x1004;
|
||||
err = uc_reg_write(uc, UC_X86_REG_EIP, &eip);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_reg_write: eip\n", count++);
|
||||
|
||||
// this should execute the remaining instructions up to (but not includign) 0x100F.
|
||||
// currently, it returns an error about an unmapped read.
|
||||
// seems that this error should have been returned in the previous call
|
||||
// to emu_start.
|
||||
err = uc_emu_start(uc, 0x1004, 0x100F, 0, 0);
|
||||
if (err != UC_ERR_OK) {
|
||||
fprintf(stderr, "not ok %d - %s\n", count++, uc_strerror(err));
|
||||
exit(0);
|
||||
}
|
||||
fprintf(stderr, "ok %d - uc_emu_start\n", count++);
|
||||
|
||||
fprintf(stderr, "ok %d - Done", count++);
|
||||
|
||||
return 0;
|
||||
}
|
80
tests/regress/emu_clear_errors.py
Executable file
80
tests/regress/emu_clear_errors.py
Executable file
@ -0,0 +1,80 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from __future__ import print_function
|
||||
import binascii
|
||||
import regress
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
|
||||
CODE = binascii.unhexlify(b"".join([
|
||||
b"8B 74 01 28", # mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000
|
||||
b"03 F0", # add esi, eax 0x1004
|
||||
b"8D 45 FC", # lea eax, dword ptr [ebp - 4] 0x1006
|
||||
b"50", # push eax 0x1009
|
||||
b"6A 40", # push 0x40 0x100A
|
||||
b"6A 10", # push 0x10 0x100C
|
||||
b"56", # push esi 0x100E
|
||||
b"FF 15 20 20 00 10" # call some address 0x100F
|
||||
]).replace(" ", ""))
|
||||
|
||||
|
||||
def showpc(mu):
|
||||
pc = mu.reg_read(UC_X86_REG_EIP)
|
||||
print("pc: 0x%x" % (pc))
|
||||
|
||||
|
||||
class HookCodeStopEmuTest(regress.RegressTest):
|
||||
def test_hook_code_stop_emu(self):
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
|
||||
# base of CODE
|
||||
mu.mem_map(0x1000, 0x1000)
|
||||
mu.mem_write(0x1000, CODE)
|
||||
mu.reg_write(UC_X86_REG_EIP, 0x1000)
|
||||
|
||||
# base of STACK
|
||||
mu.mem_map(0x4000, 0x4000)
|
||||
mu.mem_write(0x4000, "\x00" * 0x4000)
|
||||
mu.reg_write(UC_X86_REG_ESP, 0x6000)
|
||||
mu.reg_write(UC_X86_REG_EBP, 0x6000)
|
||||
|
||||
mu.reg_write(UC_X86_REG_ECX, 0x0)
|
||||
mu.reg_write(UC_X86_REG_EAX, 0x0)
|
||||
|
||||
def _hook(_, access, address, length, value, context):
|
||||
pc = mu.reg_read(UC_X86_REG_EIP)
|
||||
print("mem unmapped: pc: %x access: %x address: %x length: %x value: %x" % (
|
||||
pc, access, address, length, value))
|
||||
mu.emu_stop()
|
||||
return True
|
||||
|
||||
mu.hook_add(UC_HOOK_MEM_UNMAPPED, _hook)
|
||||
|
||||
# we only expect the following instruction to execute,
|
||||
# and it will fail, because it accesses unmapped memory.
|
||||
# mov esi, dword ptr [ecx + eax + 0x28] mapped: 0x1000
|
||||
mu.emu_start(0x1000, 0x100F)
|
||||
showpc(mu)
|
||||
|
||||
# now, we want to reuse the emulator, and keep executing
|
||||
# from the next instruction
|
||||
mu.reg_write(UC_X86_REG_EIP, 0x1004)
|
||||
self.assertEqual(0x1004, mu.reg_read(UC_X86_REG_EIP))
|
||||
|
||||
# we expect the following instructions to execute
|
||||
# add esi, eax 0x1004
|
||||
# lea eax, dword ptr [ebp - 4] 0x1006
|
||||
# push eax 0x1009
|
||||
# push 0x40 0x100A
|
||||
# push 0x10 0x100C
|
||||
# push esi 0x100E
|
||||
#
|
||||
# currently, a UC_ERR_READ_UNMAPPED exception is raised here
|
||||
mu.emu_start(0x1004, 0x100F)
|
||||
showpc(mu)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
47
tests/regress/invalid_write.py
Executable file
47
tests/regress/invalid_write.py
Executable file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
# Test callback that returns False to cancel emulation
|
||||
|
||||
from __future__ import print_function
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
|
||||
import regress
|
||||
|
||||
X86_CODE32_MEM_WRITE = b"\x89\x0D\xAA\xAA\xAA\xAA\x41\x4a" # mov [0xaaaaaaaa], ecx; INC ecx; DEC edx
|
||||
|
||||
|
||||
# callback for tracing invalid memory access (READ or WRITE)
|
||||
def hook_mem_invalid(uc, access, address, size, value, user_data):
|
||||
return False
|
||||
|
||||
|
||||
class InvalidWrite(regress.RegressTest):
|
||||
def test(self):
|
||||
# Initialize emulator in X86-32bit mode
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32)
|
||||
|
||||
# memory address where emulation starts
|
||||
ADDRESS = 0x1000000
|
||||
|
||||
# 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, X86_CODE32_MEM_WRITE)
|
||||
|
||||
# initialize machine registers
|
||||
mu.reg_write(UC_X86_REG_ECX, 0x1234)
|
||||
mu.reg_write(UC_X86_REG_EDX, 0x7890)
|
||||
|
||||
# intercept invalid memory events
|
||||
mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_invalid)
|
||||
|
||||
try:
|
||||
# emulation should return with error UC_ERR_WRITE_UNMAPPED
|
||||
mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE32_MEM_WRITE))
|
||||
except UcError as e:
|
||||
self.assertEqual(e.errno, UC_ERR_WRITE_UNMAPPED)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
regress.main()
|
@ -5,7 +5,7 @@ from unicorn.sparc_const import *
|
||||
|
||||
PAGE_SIZE = 1 * 1024 * 1024
|
||||
|
||||
uc = Uc(UC_ARCH_SPARC, UC_MODE_64)
|
||||
uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN)
|
||||
uc.reg_write(UC_SPARC_REG_SP, 100)
|
||||
print 'writing sp = 100'
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
#define HARDWARE_ARCHITECTURE UC_ARCH_SPARC
|
||||
#define HARDWARE_MODE UC_MODE_32
|
||||
#define HARDWARE_MODE UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN
|
||||
|
||||
#define MEMORY_STARTING_ADDRESS 0x1000000
|
||||
#define MEMORY_SIZE 2 * 1024 * 1024
|
||||
|
@ -5,7 +5,7 @@ from unicorn.sparc_const import *
|
||||
|
||||
PAGE_SIZE = 1 * 1024 * 1024
|
||||
|
||||
uc = Uc(UC_ARCH_SPARC, UC_MODE_32)
|
||||
uc = Uc(UC_ARCH_SPARC, UC_MODE_SPARC32|UC_MODE_BIG_ENDIAN)
|
||||
uc.reg_write(UC_SPARC_REG_SP, 100)
|
||||
uc.reg_write(UC_SPARC_REG_FP, 200)
|
||||
|
||||
|
@ -4,7 +4,8 @@ CFLAGS += -L ../../
|
||||
CFLAGS += -lcmocka -lunicorn
|
||||
CFLAGS += -I ../../include
|
||||
|
||||
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr
|
||||
ALL_TESTS = test_sanity test_x86 test_mem_map test_mem_high test_mem_map_ptr \
|
||||
test_tb_x86 test_multihook test_pc_change
|
||||
|
||||
.PHONY: all
|
||||
all: ${ALL_TESTS}
|
||||
@ -21,12 +22,18 @@ test: ${ALL_TESTS}
|
||||
./test_mem_map
|
||||
./test_mem_map_ptr
|
||||
./test_mem_high
|
||||
./test_tb_x86
|
||||
./test_multihook
|
||||
./test_pc_change
|
||||
|
||||
test_sanity: test_sanity.c
|
||||
test_x86: test_x86.c
|
||||
test_mem_map: test_mem_map.c
|
||||
test_mem_map_ptr: test_mem_map_ptr.c
|
||||
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
|
||||
|
||||
${ALL_TESTS}:
|
||||
${CC} ${CFLAGS} -o $@ $^
|
||||
|
111
tests/unit/test_multihook.c
Normal file
111
tests/unit/test_multihook.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include "unicorn_test.h"
|
||||
#include <inttypes.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, (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_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);
|
||||
}
|
104
tests/unit/test_pc_change.c
Normal file
104
tests/unit/test_pc_change.c
Normal file
@ -0,0 +1,104 @@
|
||||
// Test PC change during the callback. by Nguyen Anh Quynh, 2016
|
||||
#include "unicorn_test.h"
|
||||
#include <inttypes.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;
|
||||
|
||||
#define BASEADDR 0x1000000
|
||||
|
||||
uint64_t address = BASEADDR;
|
||||
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
|
||||
};
|
||||
|
||||
#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)));
|
||||
|
||||
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, (uint64_t)1, (uint64_t)0));
|
||||
|
||||
OK(uc_emu_start(uc, address, address+sizeof(code), 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
311
tests/unit/test_tb_x86.c
Normal file
311
tests/unit/test_tb_x86.c
Normal file
@ -0,0 +1,311 @@
|
||||
/**
|
||||
* 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 <inttypes.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)
|
||||
|
||||
#define X86_CODE32_ALPHA_MIXED \
|
||||
"\x89\xe1\xd9\xcd\xd9\x71\xf4\x5d\x55\x59\x49\x49\x49\x49\x49\x49" \
|
||||
"\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58" \
|
||||
"\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30" \
|
||||
"\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x51\x51\x51\x52" \
|
||||
"\x47\x33\x47\x34\x51\x55\x51\x56\x50\x47\x47\x38\x47\x39\x50\x4a" \
|
||||
"\x50\x4b\x50\x4c\x50\x4d\x50\x4e\x50\x4f\x50\x50\x50\x31\x47\x42" \
|
||||
"\x47\x42\x50\x34\x50\x5a\x50\x45\x51\x52\x46\x32\x47\x31\x50\x4d" \
|
||||
"\x51\x51\x50\x4e\x41\x41"
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
uint8_t tmp[256];
|
||||
uint32_t size;
|
||||
|
||||
size = sizeof(X86_CODE32_ALPHA_MIXED);
|
||||
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)
|
||||
{
|
||||
//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);
|
||||
|
||||
|
||||
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;
|
||||
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);
|
||||
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,
|
||||
X86_CODE32_ALPHA_MIXED,
|
||||
sizeof(X86_CODE32_ALPHA_MIXED) - 1));
|
||||
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,
|
||||
(uint64_t)1,
|
||||
(uint64_t)0));
|
||||
|
||||
uc_assert_success(uc_hook_add(uc,
|
||||
&trace2,
|
||||
UC_HOOK_MEM_VALID,
|
||||
hook_mem32,
|
||||
NULL,
|
||||
(uint64_t)1,
|
||||
(uint64_t)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+sizeof(X86_CODE32_ALPHA_MIXED) - 1,
|
||||
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);
|
||||
}
|
Reference in New Issue
Block a user