Merge branch 'mem_map_ex' of https://github.com/cseagle/unicorn into cseagle-mem_map_ex
This commit is contained in:
commit
96a274c4aa
@ -15,12 +15,6 @@
|
||||
|
||||
QTAILQ_HEAD(CPUTailQ, CPUState);
|
||||
|
||||
typedef struct MemoryBlock {
|
||||
uint64_t begin; //inclusive
|
||||
uint64_t end; //exclusive
|
||||
uint32_t perms;
|
||||
} MemoryBlock;
|
||||
|
||||
typedef struct ModuleEntry {
|
||||
void (*init)(void);
|
||||
QTAILQ_ENTRY(ModuleEntry) node;
|
||||
@ -51,7 +45,9 @@ typedef void (*uc_args_uc_long_t)(struct uc_struct*, unsigned long);
|
||||
|
||||
typedef void (*uc_args_uc_u64_t)(struct uc_struct *, uint64_t addr);
|
||||
|
||||
typedef int (*uc_args_uc_ram_size_t)(struct uc_struct*, ram_addr_t begin, size_t size);
|
||||
typedef MemoryRegion* (*uc_args_uc_ram_size_t)(struct uc_struct*, ram_addr_t begin, size_t size, uint32_t perms);
|
||||
|
||||
typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly);
|
||||
|
||||
// which interrupt should make emulation stop?
|
||||
typedef bool (*uc_args_int_t)(int intno);
|
||||
@ -94,6 +90,7 @@ struct uc_struct {
|
||||
uc_args_tcg_enable_t tcg_enabled;
|
||||
uc_args_uc_long_t tcg_exec_init;
|
||||
uc_args_uc_ram_size_t memory_map;
|
||||
uc_readonly_mem_t readonly_mem;
|
||||
// list of cpu
|
||||
void* cpu;
|
||||
|
||||
@ -174,13 +171,13 @@ struct uc_struct {
|
||||
int thumb; // thumb mode for ARM
|
||||
// full TCG cache leads to middle-block break in the last translation?
|
||||
bool block_full;
|
||||
MemoryBlock *mapped_blocks;
|
||||
MemoryRegion **mapped_blocks;
|
||||
uint32_t mapped_block_count;
|
||||
};
|
||||
|
||||
#include "qemu_macro.h"
|
||||
|
||||
// check if this address is mapped in (via uc_mem_map())
|
||||
bool memory_mapping(struct uc_struct* uc, uint64_t address);
|
||||
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address);
|
||||
|
||||
#endif
|
||||
|
@ -116,6 +116,7 @@ typedef enum uc_err {
|
||||
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
|
||||
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
|
||||
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
|
||||
UC_ERR_MEM_WRITE_RO, // Quit emulation due to invalid memory WRITE: uc_emu_start()
|
||||
} uc_err;
|
||||
|
||||
|
||||
@ -147,6 +148,7 @@ typedef enum uc_mem_type {
|
||||
UC_MEM_READ = 16, // Memory is read from
|
||||
UC_MEM_WRITE, // Memory is written to
|
||||
UC_MEM_READ_WRITE, // Memory is accessed (either READ or WRITE)
|
||||
UC_MEM_WRITE_RO, // Read only memory is written to
|
||||
} uc_mem_type;
|
||||
|
||||
// All type of hooks for uc_hook_add() API.
|
||||
@ -387,12 +389,12 @@ uc_err uc_hook_del(uch handle, uch *h2);
|
||||
typedef enum uc_prot {
|
||||
UC_PROT_READ = 1,
|
||||
UC_PROT_WRITE = 2,
|
||||
UC_PROT_EXEC = 4
|
||||
} uc_prot;
|
||||
|
||||
/*
|
||||
Map memory in for emulation.
|
||||
This API adds a memory region that can be used by emulation.
|
||||
This API adds a memory region that can be used by emulation. The region is mapped
|
||||
with permissions UC_PROT_READ | UC_PROT_WRITE.
|
||||
|
||||
@handle: handle returned by uc_open()
|
||||
@address: starting address of the new memory region to be mapped in.
|
||||
@ -406,6 +408,25 @@ typedef enum uc_prot {
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map(uch handle, uint64_t address, size_t size);
|
||||
|
||||
/*
|
||||
Map memory in for emulation.
|
||||
This API adds a memory region that can be used by emulation.
|
||||
|
||||
@handle: handle returned by uc_open()
|
||||
@address: starting address of the new memory region to be mapped in.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_MAP error.
|
||||
@size: size of the new memory region to be mapped in.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_MAP error.
|
||||
@perms: Permissions for the newly mapped region.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE,
|
||||
or this will return with UC_ERR_MAP error.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map_ex(uch handle, uint64_t address, size_t size, uint32_t perms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
6
qemu/include/exec/memory.h
Normal file → Executable file
6
qemu/include/exec/memory.h
Normal file → Executable file
@ -170,6 +170,8 @@ struct MemoryRegion {
|
||||
MemoryRegionIoeventfd *ioeventfds;
|
||||
NotifierList iommu_notify;
|
||||
struct uc_struct *uc;
|
||||
uint32_t perms; //all perms, partially redundant with readonly
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -315,12 +317,14 @@ void memory_region_init_io(struct uc_struct *uc, MemoryRegion *mr,
|
||||
* @owner: the object that tracks the region's reference count
|
||||
* @name: the name of the region.
|
||||
* @size: size of the region.
|
||||
* @perms: permissions on the region (UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC).
|
||||
* @errp: pointer to Error*, to store an error if it happens.
|
||||
*/
|
||||
void memory_region_init_ram(struct uc_struct *uc, MemoryRegion *mr,
|
||||
struct Object *owner,
|
||||
const char *name,
|
||||
uint64_t size,
|
||||
uint32_t perms,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
@ -934,7 +938,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||
|
||||
void memory_register_types(struct uc_struct *uc);
|
||||
|
||||
int memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size);
|
||||
MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms);
|
||||
int memory_free(struct uc_struct *uc);
|
||||
|
||||
#endif
|
||||
|
27
qemu/memory.c
Normal file → Executable file
27
qemu/memory.c
Normal file → Executable file
@ -31,27 +31,28 @@
|
||||
|
||||
|
||||
// Unicorn engine
|
||||
int memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size)
|
||||
MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms)
|
||||
{
|
||||
uc->ram = g_new(MemoryRegion, 1);
|
||||
|
||||
memory_region_init_ram(uc, uc->ram, NULL, "pc.ram", size, &error_abort);
|
||||
memory_region_init_ram(uc, uc->ram, NULL, "pc.ram", size, perms, &error_abort);
|
||||
|
||||
memory_region_add_subregion(get_system_memory(uc), begin, uc->ram);
|
||||
|
||||
if (uc->current_cpu)
|
||||
tlb_flush(uc->current_cpu, 1);
|
||||
|
||||
return 0;
|
||||
return uc->ram;
|
||||
}
|
||||
|
||||
int memory_free(struct uc_struct *uc)
|
||||
{
|
||||
int i;
|
||||
get_system_memory(uc)->enabled = false;
|
||||
if (uc->ram) {
|
||||
uc->ram->enabled = false;
|
||||
memory_region_del_subregion(get_system_memory(uc), uc->ram);
|
||||
g_free(uc->ram);
|
||||
for (i = 0; i < uc->mapped_block_count; i++) {
|
||||
uc->mapped_blocks[i]->enabled = false;
|
||||
memory_region_del_subregion(get_system_memory(uc), uc->mapped_blocks[i]);
|
||||
g_free(uc->mapped_blocks[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1151,10 +1152,15 @@ void memory_region_init_ram(struct uc_struct *uc, MemoryRegion *mr,
|
||||
Object *owner,
|
||||
const char *name,
|
||||
uint64_t size,
|
||||
uint32_t perms,
|
||||
Error **errp)
|
||||
{
|
||||
memory_region_init(uc, mr, owner, name, size);
|
||||
mr->ram = true;
|
||||
if (!(perms & UC_PROT_WRITE)) {
|
||||
mr->readonly = true;
|
||||
}
|
||||
mr->perms = perms;
|
||||
mr->terminates = true;
|
||||
mr->destructor = memory_region_destructor_ram;
|
||||
mr->ram_addr = qemu_ram_alloc(size, mr, errp);
|
||||
@ -1309,6 +1315,12 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
|
||||
if (mr->readonly != readonly) {
|
||||
memory_region_transaction_begin(mr->uc);
|
||||
mr->readonly = readonly;
|
||||
if (readonly) {
|
||||
mr->perms &= ~UC_PROT_WRITE;
|
||||
}
|
||||
else {
|
||||
mr->perms |= UC_PROT_WRITE;
|
||||
}
|
||||
mr->uc->memory_region_update_pending |= mr->enabled;
|
||||
memory_region_transaction_commit(mr->uc);
|
||||
}
|
||||
@ -1528,6 +1540,7 @@ static void memory_region_add_subregion_common(MemoryRegion *mr,
|
||||
assert(!subregion->container);
|
||||
subregion->container = mr;
|
||||
subregion->addr = offset;
|
||||
subregion->end = offset + int128_get64(subregion->size);
|
||||
memory_region_update_container_subregions(subregion);
|
||||
}
|
||||
|
||||
|
@ -460,31 +460,53 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
uintptr_t haddr;
|
||||
|
||||
struct uc_struct *uc = env->uc;
|
||||
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||
|
||||
// Unicorn: callback on memory write
|
||||
if (env->uc->hook_mem_write) {
|
||||
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_WRITE, addr);
|
||||
if (uc->hook_mem_write) {
|
||||
struct hook_struct *trace = hook_find((uch)uc, UC_MEM_WRITE, addr);
|
||||
if (trace) {
|
||||
((uc_cb_hookmem_t)trace->callback)((uch)env->uc, UC_MEM_WRITE,
|
||||
((uc_cb_hookmem_t)trace->callback)((uch)uc, UC_MEM_WRITE,
|
||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Unicorn: callback on invalid memory
|
||||
if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) {
|
||||
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
||||
(uch)env->uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
||||
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
||||
if (uc->hook_mem_idx && mr == NULL) {
|
||||
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||
(uch)uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
||||
uc->hook_callbacks[uc->hook_mem_idx].user_data)) {
|
||||
// save error & quit
|
||||
env->invalid_addr = addr;
|
||||
env->invalid_error = UC_ERR_MEM_WRITE;
|
||||
// printf("***** Invalid memory write at " TARGET_FMT_lx "\n", addr);
|
||||
cpu_exit(env->uc->current_cpu);
|
||||
cpu_exit(uc->current_cpu);
|
||||
return;
|
||||
} else {
|
||||
env->invalid_error = UC_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Unicorn: callback on read only memory
|
||||
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //read only memory
|
||||
bool result = false;
|
||||
if (uc->hook_mem_idx) {
|
||||
result = ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||
(uch)uc, UC_MEM_WRITE_RO, addr, DATA_SIZE, (int64_t)val,
|
||||
uc->hook_callbacks[uc->hook_mem_idx].user_data);
|
||||
}
|
||||
if (result) {
|
||||
env->invalid_error = UC_ERR_OK;
|
||||
}
|
||||
else {
|
||||
env->invalid_addr = addr;
|
||||
env->invalid_error = UC_ERR_MEM_WRITE_RO;
|
||||
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
||||
cpu_exit(uc->current_cpu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust the given return address. */
|
||||
retaddr -= GETPC_ADJ;
|
||||
@ -546,6 +568,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
Undo that for the recursion. */
|
||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||
mmu_idx, retaddr + GETPC_ADJ);
|
||||
if (env->invalid_error != UC_ERR_OK)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -574,31 +598,54 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
uintptr_t haddr;
|
||||
|
||||
struct uc_struct *uc = env->uc;
|
||||
MemoryRegion *mr = memory_mapping(uc, addr);
|
||||
|
||||
// Unicorn: callback on memory write
|
||||
if (env->uc->hook_mem_write) {
|
||||
struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_WRITE, addr);
|
||||
if (uc->hook_mem_write) {
|
||||
struct hook_struct *trace = hook_find((uch)uc, UC_MEM_WRITE, addr);
|
||||
if (trace) {
|
||||
((uc_cb_hookmem_t)trace->callback)((uch)env->uc, UC_MEM_WRITE,
|
||||
((uc_cb_hookmem_t)trace->callback)((uch)uc, UC_MEM_WRITE,
|
||||
(uint64_t)addr, (int)DATA_SIZE, (int64_t)val, trace->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Unicorn: callback on invalid memory
|
||||
if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) {
|
||||
if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)(
|
||||
(uch)env->uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
||||
env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) {
|
||||
if (uc->hook_mem_idx && mr == NULL) {
|
||||
if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||
(uch)uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val,
|
||||
uc->hook_callbacks[uc->hook_mem_idx].user_data)) {
|
||||
// save error & quit
|
||||
env->invalid_addr = addr;
|
||||
env->invalid_error = UC_ERR_MEM_WRITE;
|
||||
// printf("***** Invalid memory write at " TARGET_FMT_lx "\n", addr);
|
||||
cpu_exit(env->uc->current_cpu);
|
||||
cpu_exit(uc->current_cpu);
|
||||
return;
|
||||
} else {
|
||||
env->invalid_error = UC_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Unicorn: callback on read only memory
|
||||
if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //read only memory
|
||||
bool result = false;
|
||||
if (uc->hook_mem_idx) {
|
||||
result = ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)(
|
||||
(uch)uc, UC_MEM_WRITE_RO, addr, DATA_SIZE, (int64_t)val,
|
||||
uc->hook_callbacks[uc->hook_mem_idx].user_data);
|
||||
}
|
||||
if (result) {
|
||||
env->invalid_error = UC_ERR_OK;
|
||||
}
|
||||
else {
|
||||
env->invalid_addr = addr;
|
||||
env->invalid_error = UC_ERR_MEM_WRITE_RO;
|
||||
// printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr);
|
||||
cpu_exit(uc->current_cpu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust the given return address. */
|
||||
retaddr -= GETPC_ADJ;
|
||||
|
||||
@ -659,6 +706,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
Undo that for the recursion. */
|
||||
glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
|
||||
mmu_idx, retaddr + GETPC_ADJ);
|
||||
if (env->invalid_error != UC_ERR_OK)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
1
qemu/unicorn_common.h
Normal file → Executable file
1
qemu/unicorn_common.h
Normal file → Executable file
@ -73,6 +73,7 @@ static inline void uc_common_init(struct uc_struct* uc)
|
||||
uc->pause_all_vcpus = pause_all_vcpus;
|
||||
uc->vm_start = vm_start;
|
||||
uc->memory_map = memory_map;
|
||||
uc->readonly_mem = memory_region_set_readonly;
|
||||
|
||||
if (!uc->release)
|
||||
uc->release = release_common;
|
||||
|
1
regress/Makefile
Normal file → Executable file
1
regress/Makefile
Normal file → Executable file
@ -4,6 +4,7 @@ LDFLAGS = -L.. -lunicorn
|
||||
TESTS = map_crash map_write
|
||||
TESTS += sigill sigill2
|
||||
TESTS += block_test
|
||||
TESTS += ro_mem_test
|
||||
|
||||
all: $(TESTS)
|
||||
|
||||
|
195
regress/ro_mem_test.c
Executable file
195
regress/ro_mem_test.c
Executable file
@ -0,0 +1,195 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
const uint8_t PROGRAM[] =
|
||||
"\xeb\x1a\x58\x83\xc0\x04\x83\xe0\xfc\x83\xc0\x01\xc7\x00\x78\x56"
|
||||
"\x34\x12\x83\xc0\x07\xc7\x00\x21\x43\x65\x87\x90\xe8\xe1\xff\xff"
|
||||
"\xff" "xxxxAAAAxxxBBBB";
|
||||
// total size: 33 bytes
|
||||
|
||||
/*
|
||||
jmp short bottom
|
||||
top:
|
||||
pop eax
|
||||
add eax, 4
|
||||
and eax, 0xfffffffc
|
||||
add eax, 1 ; unaligned
|
||||
mov dword [eax], 0x12345678 ; try to write into code section
|
||||
add eax, 7 ; aligned
|
||||
mov dword [eax], 0x87654321 ; try to write into code section
|
||||
nop
|
||||
bottom:
|
||||
call top
|
||||
*/
|
||||
|
||||
// callback for tracing instruction
|
||||
static void hook_code(uch handle, uint64_t address, uint32_t size, void *user_data)
|
||||
{
|
||||
uint32_t esp;
|
||||
printf(">>> Tracing instruction at 0x%"PRIx64 ", instruction size = 0x%x\n", address, size);
|
||||
|
||||
uc_reg_read(handle, UC_X86_REG_ESP, &esp);
|
||||
printf(">>> --- ESP is 0x%x\n", esp);
|
||||
|
||||
}
|
||||
|
||||
// callback for tracing memory access (READ or WRITE)
|
||||
static bool hook_mem_invalid(uch handle, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data)
|
||||
{
|
||||
uint32_t esp;
|
||||
uc_reg_read(handle, UC_X86_REG_ESP, &esp);
|
||||
|
||||
switch(type) {
|
||||
default:
|
||||
// return false to indicate we want to stop emulation
|
||||
return false;
|
||||
case UC_MEM_WRITE:
|
||||
//if this is a push, esp has not been adjusted yet
|
||||
if (esp == (address + size)) {
|
||||
uint32_t upper;
|
||||
upper = (esp + 0xfff) & ~0xfff;
|
||||
printf(">>> Stack appears to be missing at 0x%"PRIx64 ", allocating now\n", address);
|
||||
// map this memory in with 2MB in size
|
||||
uc_mem_map_ex(handle, upper - 0x8000, 0x8000, UC_PROT_READ | UC_PROT_WRITE);
|
||||
// return true to indicate we want to continue
|
||||
return true;
|
||||
}
|
||||
printf(">>> Missing memory is being WRITTEN at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||
address, size, value);
|
||||
return false;
|
||||
case UC_MEM_WRITE_RO:
|
||||
printf(">>> RO memory is being WRITTEN at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n",
|
||||
address, size, value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define STACK 0x500000
|
||||
#define STACK_SIZE 0x5000
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
uch handle, trace1, trace2;
|
||||
uc_err err;
|
||||
uint8_t bytes[8];
|
||||
uint32_t esp;
|
||||
int result;
|
||||
int map_stack = 0;
|
||||
|
||||
if (argc == 2 && strcmp(argv[1], "--map-stack") == 0) {
|
||||
map_stack = 1;
|
||||
}
|
||||
|
||||
printf("Memory mapping test\n");
|
||||
|
||||
// Initialize emulator in X86-32bit mode
|
||||
err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle);
|
||||
if (err) {
|
||||
printf("Failed on uc_open() with error returned: %u\n", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uc_mem_map(handle, 0x100000, 0x1000);
|
||||
uc_mem_map(handle, 0x200000, 0x2000);
|
||||
uc_mem_map(handle, 0x300000, 0x3000);
|
||||
uc_mem_map_ex(handle, 0x400000, 0x4000, UC_PROT_READ);
|
||||
|
||||
if (map_stack) {
|
||||
printf("Pre-mapping stack\n");
|
||||
uc_mem_map_ex(handle, STACK, STACK_SIZE, UC_PROT_READ | UC_PROT_WRITE);
|
||||
}
|
||||
else {
|
||||
printf("Mapping stack on first invalid memory access\n");
|
||||
}
|
||||
|
||||
esp = STACK + STACK_SIZE;
|
||||
|
||||
uc_reg_write(handle, UC_X86_REG_ESP, &esp);
|
||||
|
||||
// write machine code to be emulated to memory
|
||||
if (uc_mem_write(handle, 0x400000, PROGRAM, sizeof(PROGRAM))) {
|
||||
printf("Failed to write emulation code to memory, quit!\n");
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
printf("Allowed to write to read only memory via uc_mem_write\n");
|
||||
}
|
||||
|
||||
//uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff);
|
||||
|
||||
// intercept invalid memory events
|
||||
uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL);
|
||||
|
||||
// emulate machine code in infinite time
|
||||
printf("BEGIN execution - 1\n");
|
||||
err = uc_emu_start(handle, 0x400000, 0x400000 + sizeof(PROGRAM), 0, 10);
|
||||
if (err) {
|
||||
printf("Expected failue on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
}
|
||||
else {
|
||||
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||
}
|
||||
printf("END execution - 1\n");
|
||||
|
||||
// emulate machine code in infinite time
|
||||
printf("BEGIN execution - 2\n");
|
||||
uint32_t eax = 0x40002C;
|
||||
uc_reg_write(handle, UC_X86_REG_EAX, &eax);
|
||||
err = uc_emu_start(handle, 0x400015, 0x400000 + sizeof(PROGRAM), 0, 2);
|
||||
if (err) {
|
||||
printf("Expected failure on uc_emu_start() with error returned %u: %s\n",
|
||||
err, uc_strerror(err));
|
||||
}
|
||||
else {
|
||||
printf("UNEXPECTED uc_emu_start returned UC_ERR_OK\n");
|
||||
}
|
||||
printf("END execution - 2\n");
|
||||
|
||||
printf("Verifying content at 0x400025 is unchanged\n");
|
||||
if (!uc_mem_read(handle, 0x400025, bytes, 4)) {
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)0x400025, *(uint32_t*) bytes);
|
||||
if (0x41414141 != *(uint32_t*) bytes) {
|
||||
printf("ERROR content in read only memory changed\n");
|
||||
}
|
||||
else {
|
||||
printf("SUCCESS content in read only memory unchanged\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||
return 4;
|
||||
}
|
||||
|
||||
printf("Verifying content at 0x40002C is unchanged\n");
|
||||
if (!uc_mem_read(handle, 0x40002C, bytes, 4)) {
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)0x40002C, *(uint32_t*) bytes);
|
||||
if (0x42424242 != *(uint32_t*) bytes) {
|
||||
printf("ERROR content in read only memory changed\n");
|
||||
}
|
||||
else {
|
||||
printf("SUCCESS content in read only memory unchanged\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||
return 4;
|
||||
}
|
||||
|
||||
printf("Verifying content at bottom of stack is readable and correct\n");
|
||||
if (!uc_mem_read(handle, esp - 4, bytes, 4)) {
|
||||
printf(">>> Read 4 bytes from [0x%x] = 0x%x\n", (uint32_t)(esp - 4), *(uint32_t*) bytes);
|
||||
}
|
||||
else {
|
||||
printf(">>> Failed to read 4 bytes from [0x%x]\n", (uint32_t)(esp - 4));
|
||||
return 4;
|
||||
}
|
||||
|
||||
uc_close(&handle);
|
||||
|
||||
return 0;
|
||||
}
|
0
samples/Makefile
Normal file → Executable file
0
samples/Makefile
Normal file → Executable file
53
uc.c
53
uc.c
@ -89,6 +89,8 @@ const char *uc_strerror(uc_err code)
|
||||
return "Invalid hook type (UC_ERR_HOOK)";
|
||||
case UC_ERR_MAP:
|
||||
return "Invalid memory mapping (UC_ERR_MAP)";
|
||||
case UC_ERR_MEM_WRITE_RO:
|
||||
return "Invalid memory write (UC_ERR_MEM_WRITE_RO)";
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,19 +353,32 @@ uc_err uc_mem_read(uch handle, uint64_t address, uint8_t *bytes, size_t size)
|
||||
return UC_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_write(uch handle, uint64_t address, const uint8_t *bytes, size_t size)
|
||||
{
|
||||
struct uc_struct *uc = (struct uc_struct *)(uintptr_t)handle;
|
||||
uint32_t operms;
|
||||
|
||||
if (handle == 0)
|
||||
// invalid handle
|
||||
return UC_ERR_UCH;
|
||||
|
||||
MemoryRegion *mr = memory_mapping(uc, address);
|
||||
if (mr == NULL)
|
||||
return UC_ERR_MEM_WRITE;
|
||||
|
||||
operms = mr->perms;
|
||||
if (!(operms & UC_PROT_WRITE)) //write protected
|
||||
//but this is not the program accessing memory, so temporarily mark writable
|
||||
uc->readonly_mem(mr, false);
|
||||
|
||||
if (uc->write_mem(&uc->as, address, bytes, size) == false)
|
||||
return UC_ERR_MEM_WRITE;
|
||||
|
||||
if (!(operms & UC_PROT_WRITE)) //write protected
|
||||
//now write protect it again
|
||||
uc->readonly_mem(mr, true);
|
||||
|
||||
return UC_ERR_OK;
|
||||
}
|
||||
|
||||
@ -529,9 +544,9 @@ static uc_err _hook_mem_access(uch handle, uc_mem_type type,
|
||||
}
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map(uch handle, uint64_t address, size_t size)
|
||||
uc_err uc_mem_map_ex(uch handle, uint64_t address, size_t size, uint32_t perms)
|
||||
{
|
||||
MemoryBlock *blocks;
|
||||
MemoryRegion **regions;
|
||||
struct uc_struct* uc = (struct uc_struct *)handle;
|
||||
|
||||
if (handle == 0)
|
||||
@ -550,34 +565,41 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size)
|
||||
if ((size & (4*1024 - 1)) != 0)
|
||||
return UC_ERR_MAP;
|
||||
|
||||
// check for only valid permissions
|
||||
if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE)) != 0)
|
||||
return UC_ERR_MAP;
|
||||
|
||||
if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow
|
||||
blocks = realloc(uc->mapped_blocks, sizeof(MemoryBlock) * (uc->mapped_block_count + MEM_BLOCK_INCR));
|
||||
if (blocks == NULL) {
|
||||
regions = (MemoryRegion**)realloc(uc->mapped_blocks, sizeof(MemoryRegion*) * (uc->mapped_block_count + MEM_BLOCK_INCR));
|
||||
if (regions == NULL) {
|
||||
return UC_ERR_OOM;
|
||||
}
|
||||
uc->mapped_blocks = blocks;
|
||||
uc->mapped_blocks = regions;
|
||||
}
|
||||
uc->mapped_blocks[uc->mapped_block_count].begin = address;
|
||||
uc->mapped_blocks[uc->mapped_block_count].end = address + size;
|
||||
//TODO extend uc_mem_map to accept permissions, figure out how to pass this down to qemu
|
||||
uc->mapped_blocks[uc->mapped_block_count].perms = UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC;
|
||||
uc->memory_map(uc, address, size);
|
||||
uc->mapped_blocks[uc->mapped_block_count] = uc->memory_map(uc, address, size, perms);
|
||||
uc->mapped_block_count++;
|
||||
|
||||
return UC_ERR_OK;
|
||||
}
|
||||
|
||||
bool memory_mapping(struct uc_struct* uc, uint64_t address)
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map(uch handle, uint64_t address, size_t size)
|
||||
{
|
||||
//old api, maps RW by default
|
||||
return uc_mem_map_ex(handle, address, size, UC_PROT_READ | UC_PROT_WRITE);
|
||||
}
|
||||
|
||||
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0; i < uc->mapped_block_count; i++) {
|
||||
if (address >= uc->mapped_blocks[i].begin && address < uc->mapped_blocks[i].end)
|
||||
return true;
|
||||
if (address >= uc->mapped_blocks[i]->addr && address < uc->mapped_blocks[i]->end)
|
||||
return uc->mapped_blocks[i];
|
||||
}
|
||||
|
||||
// not found
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback,
|
||||
@ -744,4 +766,3 @@ uc_err uc_hook_del(uch handle, uch *h2)
|
||||
|
||||
return hook_del(handle, h2);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user