diff --git a/.gitignore b/.gitignore index c82b9fb1..2f19c57b 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,6 @@ regress/map_crash regress/sigill regress/sigill2 regress/block_test +regress/map_write +regress/ro_mem_test + diff --git a/include/uc_priv.h b/include/uc_priv.h index 3fde4862..686ab9e5 100755 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -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 diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index ebdb47f3..556b6c17 100755 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -116,6 +116,8 @@ 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_NW, // Quit emulation due to write to non-writable: uc_emu_start() + UC_ERR_MEM_READ_NR, // Quit emulation due to read from non-readable: uc_emu_start() } uc_err; @@ -147,6 +149,8 @@ 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_NW, // write to non-writable + UC_MEM_READ_NR, // read from non-readable } uc_mem_type; // All type of hooks for uc_hook_add() API. @@ -387,12 +391,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 +410,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 diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index c8b0c333..4df8bd85 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -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 diff --git a/qemu/memory.c b/qemu/memory.c index f7bf161b..3f8169d9 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -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); } diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index a48ee8d5..56f657a4 100755 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -178,6 +178,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t haddr; DATA_TYPE res; + struct uc_struct *uc = env->uc; + MemoryRegion *mr = memory_mapping(uc, addr); + // Unicorn: callback on memory read if (env->uc->hook_mem_read && READ_ACCESS_TYPE == MMU_DATA_LOAD) { struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_READ, addr); @@ -188,7 +191,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } // Unicorn: callback on invalid memory - if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) { + if (env->uc->hook_mem_idx && mr == NULL) { if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)( (uch)env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) { @@ -203,6 +206,26 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } } + // Unicorn: callback on read only memory + if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable + 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_READ_NR, addr, DATA_SIZE, 0, + 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_READ_NR; + // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); + cpu_exit(uc->current_cpu); + return 0; + } + } + /* Adjust the given return address. */ retaddr -= GETPC_ADJ; @@ -300,6 +323,9 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, uintptr_t haddr; DATA_TYPE res; + struct uc_struct *uc = env->uc; + MemoryRegion *mr = memory_mapping(uc, addr); + // Unicorn: callback on memory read if (env->uc->hook_mem_read && READ_ACCESS_TYPE == MMU_DATA_LOAD) { struct hook_struct *trace = hook_find((uch)env->uc, UC_MEM_READ, addr); @@ -310,7 +336,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } // Unicorn: callback on invalid memory - if (env->uc->hook_mem_idx && !memory_mapping(env->uc, addr)) { + if (env->uc->hook_mem_idx && mr == NULL) { if (!((uc_cb_eventmem_t)env->uc->hook_callbacks[env->uc->hook_mem_idx].callback)( (uch)env->uc, UC_MEM_READ, addr, DATA_SIZE, 0, env->uc->hook_callbacks[env->uc->hook_mem_idx].user_data)) { @@ -325,6 +351,26 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } } + // Unicorn: callback on read only memory + if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable + 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_READ_NR, addr, DATA_SIZE, 0, + 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_READ_NR; + // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); + cpu_exit(uc->current_cpu); + return 0; + } + } + /* Adjust the given return address. */ retaddr -= GETPC_ADJ; @@ -460,31 +506,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_NW, 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_NW; + // 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 +614,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 +644,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_NW, 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_NW; + // 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 +752,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; } diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 93f1c5f4..176900cb 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -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; diff --git a/regress/Makefile b/regress/Makefile index e5fa674c..a75cde02 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -4,6 +4,7 @@ LDFLAGS = -L.. -lunicorn TESTS = map_crash map_write TESTS += sigill sigill2 TESTS += block_test +TESTS += ro_mem_test nr_mem_test all: $(TESTS) diff --git a/regress/map_crash.c b/regress/map_crash.c index 4d6bc8fe..ca16b56b 100644 --- a/regress/map_crash.c +++ b/regress/map_crash.c @@ -4,9 +4,10 @@ #include #define UC_BUG_WRITE_SIZE 13000 -#define UC_BUG_WRITE_ADDR 0x1000 // fix this by change this to 0x2000 +#define UC_BUG_WRITE_ADDR 0x1000 -int main() { +int main() +{ int size; uint8_t *buf; uch uh; diff --git a/regress/map_write.c b/regress/map_write.c index 22913016..fc4343c5 100644 --- a/regress/map_write.c +++ b/regress/map_write.c @@ -6,44 +6,45 @@ #define SIZE 1024*64 #define OVERFLOW 1 -int main() { - uch uh; - char *buf, *buf2; - int i; - uc_err err; +int main() +{ + uch uh; + uint8_t *buf, *buf2; + int i; + uc_err err; - err = uc_open (UC_ARCH_X86, UC_MODE_64, &uh); - if (err) { - printf ("uc_open %d\n", err); - return 1; - } - err = uc_mem_map (uh, ADDR, SIZE); - if (err) { - printf ("uc_mem_map %d\n", err); - return 1; - } - buf = calloc (SIZE*2, 1); - buf2 = calloc (SIZE, 1); - for (i=0;i +#include +#include + +#include + +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_NR: + 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; +} diff --git a/regress/sigill.c b/regress/sigill.c index 099bbec3..415a2313 100644 --- a/regress/sigill.c +++ b/regress/sigill.c @@ -8,14 +8,16 @@ int got_sigill = 0; -void _interrupt(uch handle, uint32_t intno, void *user_data) { +void _interrupt(uch handle, uint32_t intno, void *user_data) +{ if (intno == 6) { uc_emu_stop (handle); - got_sigill = 1; + got_sigill = 1; } } -int main() { +int main() +{ int size; uint8_t *buf; uch uh; @@ -34,7 +36,7 @@ int main() { memset (buf, 0, size); if (!uc_mem_map (uh, UC_BUG_WRITE_ADDR, size)) { uc_mem_write (uh, UC_BUG_WRITE_ADDR, - (const uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff", 8); + (const uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff", 8); } uc_hook_add (uh, &uh_trap, UC_HOOK_INTR, _interrupt, NULL); uc_emu_start (uh, UC_BUG_WRITE_ADDR, UC_BUG_WRITE_ADDR+8, 0, 1); diff --git a/regress/sigill2.c b/regress/sigill2.c index 97cd7199..ca13282a 100644 --- a/regress/sigill2.c +++ b/regress/sigill2.c @@ -20,7 +20,7 @@ int main() size = UC_BUG_WRITE_SIZE; if (!uc_mem_map (uh, UC_BUG_WRITE_ADDR, size)) { uc_mem_write (uh, UC_BUG_WRITE_ADDR, - (const uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff", 8); + (const uint8_t*)"\xff\xff\xff\xff\xff\xff\xff\xff", 8); } err = uc_emu_start (uh, UC_BUG_WRITE_ADDR, UC_BUG_WRITE_ADDR+8, 0, 1); uc_close (&uh); diff --git a/uc.c b/uc.c index a7533196..3192c627 100755 --- a/uc.c +++ b/uc.c @@ -89,6 +89,10 @@ 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_NW: + return "Write to non-writable (UC_ERR_MEM_WRITE_NW)"; + case UC_ERR_MEM_READ_NR: + return "Read from non-readable (UC_ERR_MEM_READ_NR)"; } } @@ -351,19 +355,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 +546,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 +567,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 +768,3 @@ uc_err uc_hook_del(uch handle, uch *h2) return hook_del(handle, h2); } -