From 7f63d76908c810d6501e84a988418dbedf96fdba Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Fri, 28 Aug 2015 12:58:53 -0700 Subject: [PATCH 01/23] add comments to source for ro_mem_test.c --- regress/ro_mem_test.c | 3 +++ 1 file changed, 3 insertions(+) mode change 100644 => 100755 regress/ro_mem_test.c diff --git a/regress/ro_mem_test.c b/regress/ro_mem_test.c old mode 100644 new mode 100755 index 1cce6189..19d66bf3 --- a/regress/ro_mem_test.c +++ b/regress/ro_mem_test.c @@ -156,8 +156,11 @@ int main(int argc, char **argv, char **envp) // emulate machine code in infinite time printf("BEGIN execution - 2\n"); + //update eax to point to aligned memory (same as add eax,7 above) uint32_t eax = 0x40002C; uc_reg_write(handle, UC_X86_REG_EAX, &eax); + //resume execution at the mov dword [eax], 0x87654321 + //to test an aligned write as well 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", From 9ba59e4988b7fa84bb5fd3d52ab8c6482a98a108 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Fri, 28 Aug 2015 18:59:45 -0700 Subject: [PATCH 02/23] Step one towards uc_mem_protect, uc_mem_unmap, and support for UC_PROT_EXEC and NX regions --- include/unicorn/unicorn.h | 25 ++++++++++- qemu/softmmu_template.h | 88 ++++++++++++++++++++++++--------------- uc.c | 88 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 163 insertions(+), 38 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 556b6c17..ce887aa3 100755 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -151,6 +151,7 @@ typedef enum uc_mem_type { 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_NX, // read from non-readable } uc_mem_type; // All type of hooks for uc_hook_add() API. @@ -391,12 +392,13 @@ 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. The region is mapped - with permissions UC_PROT_READ | UC_PROT_WRITE. + with permissions UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC. @handle: handle returned by uc_open() @address: starting address of the new memory region to be mapped in. @@ -420,7 +422,7 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size); @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, + This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, 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 @@ -429,6 +431,25 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size); UNICORN_EXPORT uc_err uc_mem_map_ex(uch handle, uint64_t address, size_t size, uint32_t perms); +/* + Set memory permissions for emulation memory. + This API changes permissions on an existing memory region. + + @handle: handle returned by uc_open() + @start: starting address of the memory region to be modified. + This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. + @block_size: size of the memory region to be modified. + This size must be multiple of 4KB, or this will return with UC_ERR_MAP error. + @perms: New permissions for the mapped region. + This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, + 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_protect(uch handle, uint64_t start, size_t block_size, uint32_t perms); + #ifdef __cplusplus } #endif diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 56f657a4..8c181824 100755 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -181,6 +181,24 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); +#if defined(SOFTMMU_CODE_ACCESS) + // Unicorn: callback on fetch from NX + if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { //non-executable + if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( + (uch)uc, UC_MEM_NX, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + env->invalid_error = UC_ERR_OK; + } + else { + env->invalid_addr = addr; + env->invalid_error = UC_ERR_MEM_READ; + // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); + cpu_exit(uc->current_cpu); + return 0; + } + } +#endif + // 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); @@ -206,15 +224,11 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } } - // Unicorn: callback on read only memory + // Unicorn: callback on non-readable 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) { + if (uc->hook_mem_idx != 0 && ((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)) { env->invalid_error = UC_ERR_OK; } else { @@ -326,6 +340,24 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); +#if defined(SOFTMMU_CODE_ACCESS) + // Unicorn: callback on fetch from NX + if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { //non-executable + if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( + (uch)uc, UC_MEM_NX, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + env->invalid_error = UC_ERR_OK; + } + else { + env->invalid_addr = addr; + env->invalid_error = UC_ERR_MEM_READ; + // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); + cpu_exit(uc->current_cpu); + return 0; + } + } +#endif + // 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); @@ -351,15 +383,11 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } } - // Unicorn: callback on read only memory + // Unicorn: callback on non-readable 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) { + if (uc->hook_mem_idx != 0 && ((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)) { env->invalid_error = UC_ERR_OK; } else { @@ -534,15 +562,11 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } } - // 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) { + // Unicorn: callback on non-writable memory + if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable + if (uc->hook_mem_idx != 0 && ((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)) { env->invalid_error = UC_ERR_OK; } else { @@ -672,15 +696,11 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } } - // 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) { + // Unicorn: callback on non-writable memory + if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable + if (uc->hook_mem_idx != 0 && ((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)) { env->invalid_error = UC_ERR_OK; } else { diff --git a/uc.c b/uc.c index 3192c627..18d70b63 100755 --- a/uc.c +++ b/uc.c @@ -568,7 +568,7 @@ uc_err uc_mem_map_ex(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_MAP; // check for only valid permissions - if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE)) != 0) + if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)) != 0) return UC_ERR_MAP; if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow @@ -588,7 +588,91 @@ 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); + return uc_mem_map_ex(handle, address, size, UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC); +} + +UNICORN_EXPORT +uc_err uc_mem_protect(uch handle, uint64_t start, size_t block_size, uint32_t perms) +{ + uint64_t address; + uint64_t size; + struct uc_struct* uc = (struct uc_struct *)handle; + + if (handle == 0) + // invalid handle + return UC_ERR_UCH; + + if (block_size == 0) + // invalid memory mapping + return UC_ERR_MAP; + + // address must be aligned to 4KB + if ((start & (4*1024 - 1)) != 0) + return UC_ERR_MAP; + + // size must be multiple of 4KB + if ((block_size & (4*1024 - 1)) != 0) + return UC_ERR_MAP; + + // check for only valid permissions + if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)) != 0) + return UC_ERR_MAP; + + //check that users entire requested block is mapped + address = start; + size = block_size; + while (size > 0) { + uint64_t region_size; + MemoryRegion *mr = memory_mapping(uc, address); + if (mr == NULL) { + return UC_ERR_MAP; + } + region_size = int128_get64(mr->size); + if (address > mr->addr) { + //in case start address is not aligned with start of region + region_size -= address - mr->addr; + } + if (size < region_size) { + //entire region is covered + break; + } + size -= region_size; + address += region_size; + } + + //Now we know entire region is mapped, so change permissions + address = start; + size = block_size; + while (size > 0) { + MemoryRegion *mr = memory_mapping(uc, address); + uint64_t region_size = int128_get64(mr->size); + if (address > mr->addr) { + //in case start address is not aligned with start of region + region_size -= address - mr->addr; + //TODO Learn how to split regions + //In this case some proper subset of the region is having it's permissions changed + //need to split region and add new portions into uc->mapped_blocks list + //In this case, there is a portion of the region with original perms: mr->addr..start + //and a portion getting new perms: start..start+block_size + + //split the block and stay in the loop + } + if (size < int128_get64(mr->size)) { + //TODO Learn how to split regions + //In this case some proper subset of the region is having it's permissions changed + //need to split region and add new portions into uc->mapped_blocks list + //In this case, there is a portion of the region with new perms: start..start+block_size + //and a portion getting new perms: mr->addr+size..mr->addr+mr->size + + //split the block and break + break; + } + size -= int128_get64(mr->size); + address += int128_get64(mr->size); + mr->perms = perms; + uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + } + return UC_ERR_OK; } MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) From 6beb1b8a13087da29b289c977e3c6a010aaf798b Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sat, 29 Aug 2015 21:17:30 -0700 Subject: [PATCH 03/23] intermediate commit, working unmap of complete blocks, still need sub-blocks, and cross block --- include/uc_priv.h | 3 + include/unicorn/unicorn.h | 22 +++++- qemu/aarch64.h | 1 + qemu/arm.h | 1 + qemu/header_gen.py | 1 + qemu/include/exec/memory.h | 1 + qemu/m68k.h | 1 + qemu/memory.c | 12 +++ qemu/mips.h | 1 + qemu/mips64.h | 1 + qemu/mips64el.h | 1 + qemu/mipsel.h | 1 + qemu/powerpc.h | 1 + qemu/sparc.h | 1 + qemu/sparc64.h | 1 + qemu/unicorn_common.h | 1 + qemu/x86_64.h | 1 + uc.c | 155 +++++++++++++++++++++++-------------- 18 files changed, 147 insertions(+), 59 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index 686ab9e5..7e76513a 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -47,6 +47,8 @@ typedef void (*uc_args_uc_u64_t)(struct uc_struct *, uint64_t addr); typedef MemoryRegion* (*uc_args_uc_ram_size_t)(struct uc_struct*, ram_addr_t begin, size_t size, uint32_t perms); +typedef void (*uc_mem_unmap_t)(struct uc_struct*, MemoryRegion *mr); + typedef void (*uc_readonly_mem_t)(MemoryRegion *mr, bool readonly); // which interrupt should make emulation stop? @@ -90,6 +92,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_mem_unmap_t memory_unmap; uc_readonly_mem_t readonly_mem; // list of cpu void* cpu; diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 991db568..fee75297 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -422,9 +422,9 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); This API changes permissions on an existing memory region. @handle: handle returned by uc_open() - @start: starting address of the memory region to be modified. + @address: starting address of the memory region to be modified. This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. - @block_size: size of the memory region to be modified. + @size: size of the memory region to be modified. This size must be multiple of 4KB, or this will return with UC_ERR_MAP error. @perms: New permissions for the mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, @@ -434,7 +434,23 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); for detailed error). */ UNICORN_EXPORT -uc_err uc_mem_protect(uch handle, uint64_t start, size_t block_size, uint32_t perms); +uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms); + +/* + Unmap a region of emulation memory. + This API deletes a memory mapping from the emulation memory space. + + @handle: handle returned by uc_open() + @address: starting address of the memory region to be unmapped. + This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. + @size: size of the memory region to be modified. + This size must be multiple of 4KB, 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_unmap(uch handle, uint64_t address, size_t size); #ifdef __cplusplus } diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 9ab06273..77de1644 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_aarch64 #define tb_cleanup tb_cleanup_aarch64 #define memory_map memory_map_aarch64 +#define memory_unmap memory_unmap_aarch64 #define memory_free memory_free_aarch64 #define helper_raise_exception helper_raise_exception_aarch64 #define tcg_enabled tcg_enabled_aarch64 diff --git a/qemu/arm.h b/qemu/arm.h index c24329b3..3d405fac 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_arm #define tb_cleanup tb_cleanup_arm #define memory_map memory_map_arm +#define memory_unmap memory_unmap_arm #define memory_free memory_free_arm #define helper_raise_exception helper_raise_exception_arm #define tcg_enabled tcg_enabled_arm diff --git a/qemu/header_gen.py b/qemu/header_gen.py index ff8a1ca5..67982502 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -13,6 +13,7 @@ symbols = ( 'phys_mem_clean', 'tb_cleanup', 'memory_map', + 'memory_unmap', 'memory_free', 'helper_raise_exception', 'tcg_enabled', diff --git a/qemu/include/exec/memory.h b/qemu/include/exec/memory.h index 4df8bd85..45c51e4d 100644 --- a/qemu/include/exec/memory.h +++ b/qemu/include/exec/memory.h @@ -939,6 +939,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, void memory_register_types(struct uc_struct *uc); MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, uint32_t perms); +void memory_unmap(struct uc_struct *uc, MemoryRegion *mr); int memory_free(struct uc_struct *uc); #endif diff --git a/qemu/m68k.h b/qemu/m68k.h index 5dbefab7..4be757ba 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_m68k #define tb_cleanup tb_cleanup_m68k #define memory_map memory_map_m68k +#define memory_unmap memory_unmap_m68k #define memory_free memory_free_m68k #define helper_raise_exception helper_raise_exception_m68k #define tcg_enabled tcg_enabled_m68k diff --git a/qemu/memory.c b/qemu/memory.c index 3f8169d9..95336f5f 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -45,6 +45,18 @@ MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, ui return uc->ram; } +void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) +{ + targer_ulong addr; + //make sure all pages associated with the MemoryRegion are flushed + for (addr = mr->addr; addr < mr->end; addr += 0x1000) { + tlb_flush_page(uc->current_cpu, addr); + } + mr->enabled = false; + memory_region_del_subregion(get_system_memory(uc), mr); + g_free(mr); +} + int memory_free(struct uc_struct *uc) { int i; diff --git a/qemu/mips.h b/qemu/mips.h index 059995e5..7a3e308a 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mips #define tb_cleanup tb_cleanup_mips #define memory_map memory_map_mips +#define memory_unmap memory_unmap_mips #define memory_free memory_free_mips #define helper_raise_exception helper_raise_exception_mips #define tcg_enabled tcg_enabled_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 74daddbc..9870cb15 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mips64 #define tb_cleanup tb_cleanup_mips64 #define memory_map memory_map_mips64 +#define memory_unmap memory_unmap_mips64 #define memory_free memory_free_mips64 #define helper_raise_exception helper_raise_exception_mips64 #define tcg_enabled tcg_enabled_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 6ffc2dbc..5fde9e53 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mips64el #define tb_cleanup tb_cleanup_mips64el #define memory_map memory_map_mips64el +#define memory_unmap memory_unmap_mips64el #define memory_free memory_free_mips64el #define helper_raise_exception helper_raise_exception_mips64el #define tcg_enabled tcg_enabled_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 94c4fdf7..caf1fe4d 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_mipsel #define tb_cleanup tb_cleanup_mipsel #define memory_map memory_map_mipsel +#define memory_unmap memory_unmap_mipsel #define memory_free memory_free_mipsel #define helper_raise_exception helper_raise_exception_mipsel #define tcg_enabled tcg_enabled_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index fd627665..92e614e1 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_powerpc #define tb_cleanup tb_cleanup_powerpc #define memory_map memory_map_powerpc +#define memory_unmap memory_unmap_powerpc #define memory_free memory_free_powerpc #define helper_raise_exception helper_raise_exception_powerpc #define tcg_enabled tcg_enabled_powerpc diff --git a/qemu/sparc.h b/qemu/sparc.h index 64803c11..6aa47aa5 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_sparc #define tb_cleanup tb_cleanup_sparc #define memory_map memory_map_sparc +#define memory_unmap memory_unmap_sparc #define memory_free memory_free_sparc #define helper_raise_exception helper_raise_exception_sparc #define tcg_enabled tcg_enabled_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 5042c38d..6d3d2a1d 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_sparc64 #define tb_cleanup tb_cleanup_sparc64 #define memory_map memory_map_sparc64 +#define memory_unmap memory_unmap_sparc64 #define memory_free memory_free_sparc64 #define helper_raise_exception helper_raise_exception_sparc64 #define tcg_enabled tcg_enabled_sparc64 diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 176900cb..23ef0acb 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->memory_unmap = memory_unmap; uc->readonly_mem = memory_region_set_readonly; if (!uc->release) diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 9a148e95..ac9f34f0 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -7,6 +7,7 @@ #define phys_mem_clean phys_mem_clean_x86_64 #define tb_cleanup tb_cleanup_x86_64 #define memory_map memory_map_x86_64 +#define memory_unmap memory_unmap_x86_64 #define memory_free memory_free_x86_64 #define helper_raise_exception helper_raise_exception_x86_64 #define tcg_enabled tcg_enabled_x86_64 diff --git a/uc.c b/uc.c index 3c46e689..ec85ea61 100644 --- a/uc.c +++ b/uc.c @@ -643,87 +643,130 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) } UNICORN_EXPORT -uc_err uc_mem_protect(uch handle, uint64_t start, size_t block_size, uint32_t perms) +uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) { - uint64_t address; - uint64_t size; struct uc_struct* uc = (struct uc_struct *)handle; + MemoryRegion *mr; if (handle == 0) // invalid handle return UC_ERR_UCH; - if (block_size == 0) + if (size == 0) // invalid memory mapping return UC_ERR_MAP; // address must be aligned to 4KB - if ((start & (4*1024 - 1)) != 0) + if ((address & (4*1024 - 1)) != 0) return UC_ERR_MAP; // size must be multiple of 4KB - if ((block_size & (4*1024 - 1)) != 0) + if ((size & (4*1024 - 1)) != 0) return UC_ERR_MAP; // check for only valid permissions if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)) != 0) return UC_ERR_MAP; - //check that users entire requested block is mapped - address = start; - size = block_size; - while (size > 0) { - uint64_t region_size; - MemoryRegion *mr = memory_mapping(uc, address); - if (mr == NULL) { - return UC_ERR_MAP; - } - region_size = int128_get64(mr->size); - if (address > mr->addr) { - //in case start address is not aligned with start of region - region_size -= address - mr->addr; - } - if (size < region_size) { - //entire region is covered - break; - } - size -= region_size; - address += region_size; - } + //check that user's entire requested block is mapped + if (!check_mem_area(uc, address, size)) + return UC_ERR_MAP; //Now we know entire region is mapped, so change permissions - address = start; - size = block_size; - while (size > 0) { - MemoryRegion *mr = memory_mapping(uc, address); - uint64_t region_size = int128_get64(mr->size); - if (address > mr->addr) { - //in case start address is not aligned with start of region - region_size -= address - mr->addr; - //TODO Learn how to split regions - //In this case some proper subset of the region is having it's permissions changed - //need to split region and add new portions into uc->mapped_blocks list - //In this case, there is a portion of the region with original perms: mr->addr..start - //and a portion getting new perms: start..start+block_size - - //split the block and stay in the loop - } - if (size < int128_get64(mr->size)) { - //TODO Learn how to split regions - //In this case some proper subset of the region is having it's permissions changed - //need to split region and add new portions into uc->mapped_blocks list - //In this case, there is a portion of the region with new perms: start..start+block_size - //and a portion getting new perms: mr->addr+size..mr->addr+mr->size - - //split the block and break - break; - } - size -= int128_get64(mr->size); - address += int128_get64(mr->size); + //check trivial case first + mr = memory_mapping(uc, address); + if (address == mr->addr && size == int128_get64(mr->size)) { + //regions exactly matches an existing region just change perms mr->perms = perms; uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); } - return UC_ERR_OK; + else { + //ouch, we are going to need to subdivide blocks +/* + address = start; + size = block_size; + while (size > 0) { + MemoryRegion *mr = memory_mapping(uc, address); + uint64_t region_size = int128_get64(mr->size); + if (address > mr->addr) { + //in case start address is not aligned with start of region + region_size -= address - mr->addr; + //TODO Learn how to split regions + //In this case some proper subset of the region is having it's permissions changed + //need to split region and add new portions into uc->mapped_blocks list + //In this case, there is a portion of the region with original perms: mr->addr..start + //and a portion getting new perms: start..start+block_size + + //split the block and stay in the loop + } + if (size < int128_get64(mr->size)) { + //TODO Learn how to split regions + //In this case some proper subset of the region is having it's permissions changed + //need to split region and add new portions into uc->mapped_blocks list + //In this case, there is a portion of the region with new perms: start..start+block_size + //and a portion getting new perms: mr->addr+size..mr->addr+mr->size + + //split the block and break + break; + } + size -= int128_get64(mr->size); + address += int128_get64(mr->size); + mr->perms = perms; + uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + } +*/ + } + return UC_ERR_OK; +} + +UNICORN_EXPORT +uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) +{ + MemoryRegion *mr; + unsigned int i; + struct uc_struct* uc = (struct uc_struct *)handle; + + if (handle == 0) + // invalid handle + return UC_ERR_UCH; + + if (size == 0) + // nothing to unmap + return UC_ERR_OK; + + // address must be aligned to 4KB + if ((address & (4*1024 - 1)) != 0) + return UC_ERR_MAP; + + // size must be multiple of 4KB + if ((size & (4*1024 - 1)) != 0) + return UC_ERR_MAP; + + //check that user's entire requested block is mapped + if (!check_mem_area(uc, address, size)) + return UC_ERR_MAP; + + //Now we know entire region is mapped, so change permissions + //check trivial case first + mr = memory_mapping(uc, address); + if (address == mr->addr && size == int128_get64(mr->size)) { + //regions exactly matches an existing region just unmap it + uc->memory_unmap(uc, mr); + for (i = 0; i < uc->mapped_block_count; i++) { + if (uc->mapped_blocks[i] == mr) { + uc->mapped_block_count--; + //shift remainder of array down over deleted pointer + memcpy(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i)); + break; + } + } + return UC_ERR_OK; + } + else { + //ouch, we are going to need to subdivide blocks + } + + return UC_ERR_MAP; } MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) From 942de0f2ae34a5134c976859fc330d86ddf0c124 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 00:22:18 -0700 Subject: [PATCH 04/23] implemented basic block splitting, uc_mem_unmap should work for sub=blocks or across contiguous blocks --- qemu/memory.c | 2 +- uc.c | 153 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 138 insertions(+), 17 deletions(-) mode change 100644 => 100755 qemu/memory.c mode change 100644 => 100755 uc.c diff --git a/qemu/memory.c b/qemu/memory.c old mode 100644 new mode 100755 index f44d32c2..5e9cc9b0 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -47,7 +47,7 @@ MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, ui void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { - targer_ulong addr; + target_ulong addr; //make sure all pages associated with the MemoryRegion are flushed for (addr = mr->addr; addr < mr->end; addr += 0x1000) { tlb_flush_page(uc->current_cpu, addr); diff --git a/uc.c b/uc.c old mode 100644 new mode 100755 index 454d1a04..6dc2786d --- a/uc.c +++ b/uc.c @@ -31,6 +31,13 @@ #include "qemu/include/hw/boards.h" +//keep this a power of two! +#define UC_BLOCK_SIZE 0x1000 +#define UC_ALIGN_MASK (UC_BLOCK_SIZE - 1) + +static uint8_t *copy_region(uch uc, MemoryRegion *mr); +static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete); + UNICORN_EXPORT unsigned int uc_version(unsigned int *major, unsigned int *minor) { @@ -622,12 +629,12 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) // invalid memory mapping return UC_ERR_MAP; - // address must be aligned to 4KB - if ((address & (4*1024 - 1)) != 0) + // address must be aligned to UC_BLOCK_SIZE + if ((address & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; - // size must be multiple of 4KB - if ((size & (4*1024 - 1)) != 0) + // size must be multiple of UC_BLOCK_SIZE + if ((size & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; // check for only valid permissions @@ -647,6 +654,111 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_OK; } +//create a backup copy of the indicated MemoryRegion +//generally used in prepartion for splitting a MemoryRegion +static uint8_t *copy_region(uch handle, MemoryRegion *mr) +{ + uint8_t *block = (uint8_t *)malloc(int128_get64(mr->size)); + if (block != NULL) { + uc_err err = uc_mem_read(handle, mr->addr, block, int128_get64(mr->size)); + if (err != UC_ERR_OK) { + free(block); + block = NULL; + } + } + return block; +} + +/* +Split the given MemoryRegion at the indicated address for the indicated size +this may result in the create of up to 3 spanning sections. If the delete +parameter is true, the no new section will be created to replace the indicate +range. This functions exists to support uc_mem_protect and uc_mem_unmap. + +This is a static function and callers have already done some preliminary +parameter validation. +*/ +//TODO: investigate whether qemu region manipulation functions already offer this capability +static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete) +{ + uint8_t *backup; + uint32_t perms; + uint64_t begin, end, chunk_end; + size_t l_size, m_size, r_size; + chunk_end = address + size; + if (address <= mr->addr && chunk_end >= mr->end) { + //trivial case, if we are deleting, just unmap + if (do_delete) + return uc_mem_unmap(handle, mr->addr, int128_get64(mr->size)) == UC_ERR_OK; + return true; + } + + if (size == 0) + //trivial case + return true; + + if (address >= mr->end || chunk_end <= mr->addr) + //impossible case + return false; + + backup = copy_region(handle, mr); + if (backup == NULL) + return false; + + //save the essential information required for the split before mr gets deleted + perms = mr->perms; + begin = mr->addr; + end = mr->end; + + if (uc_mem_unmap(handle, mr->addr, int128_get64(mr->size)) != UC_ERR_OK) + goto error; + + /* overlapping cases + * |------mr------| + * case 1 |---size--| + * case 2 |--size--| + * case 3 |---size--| + */ + + //adjust some things + if (address < begin) + address = begin; + if (chunk_end > end) + chunk_end = end; + + //compute sub region sizes + l_size = (size_t)(address - begin); + r_size = (size_t)(end - chunk_end); + m_size = (size_t)(chunk_end - address); + + //If there are error in any of the below operations, things are too far gone + //at that point to recover. Could try to remap orignal region, but these smaller + //allocation just failed so no guarantee that we can recover the original + //allocation at this point + if (l_size > 0) { + if (uc_mem_map(handle, begin, l_size, perms) != UC_ERR_OK) + goto error; + if (uc_mem_write(handle, begin, backup, l_size) != UC_ERR_OK) + goto error; + } + if (m_size > 0 && !do_delete) { + if (uc_mem_map(handle, address, m_size, perms) != UC_ERR_OK) + goto error; + if (uc_mem_write(handle, address, backup + l_size, m_size) != UC_ERR_OK) + goto error; + } + if (r_size > 0) { + if (uc_mem_map(handle, chunk_end, r_size, perms) != UC_ERR_OK) + goto error; + if (uc_mem_write(handle, chunk_end, backup + l_size + m_size, r_size) != UC_ERR_OK) + goto error; + } + return true; +error: + free(backup); + return false; +} + UNICORN_EXPORT uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) { @@ -661,12 +773,12 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) // invalid memory mapping return UC_ERR_MAP; - // address must be aligned to 4KB - if ((address & (4*1024 - 1)) != 0) + // address must be aligned to UC_BLOCK_SIZE + if ((address & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; - // size must be multiple of 4KB - if ((size & (4*1024 - 1)) != 0) + // size must be multiple of UC_BLOCK_SIZE + if ((size & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; // check for only valid permissions @@ -730,6 +842,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) MemoryRegion *mr; unsigned int i; struct uc_struct* uc = (struct uc_struct *)handle; + size_t count, len; if (handle == 0) // invalid handle @@ -739,23 +852,24 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) // nothing to unmap return UC_ERR_OK; - // address must be aligned to 4KB - if ((address & (4*1024 - 1)) != 0) + // address must be aligned to UC_BLOCK_SIZE + if ((address & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; - // size must be multiple of 4KB - if ((size & (4*1024 - 1)) != 0) + // size must be multiple of UC_BLOCK_SIZE + if ((size & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; //check that user's entire requested block is mapped if (!check_mem_area(uc, address, size)) return UC_ERR_MAP; - //Now we know entire region is mapped, so change permissions + //Now we know entire region is mapped, so begin the delete //check trivial case first mr = memory_mapping(uc, address); if (address == mr->addr && size == int128_get64(mr->size)) { //regions exactly matches an existing region just unmap it + //this termiantes a possible recursion between this function and split_region uc->memory_unmap(uc, mr); for (i = 0; i < uc->mapped_block_count; i++) { if (uc->mapped_blocks[i] == mr) { @@ -765,13 +879,20 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) break; } } - return UC_ERR_OK; } else { //ouch, we are going to need to subdivide blocks + count = 0; + while(count < size) { + MemoryRegion *mr = memory_mapping(uc, address); + len = MIN(size - count, mr->end - address); + if (!split_region(handle, mr, address, len, true)) + return UC_ERR_MAP; + count += len; + address += len; + } } - - return UC_ERR_MAP; + return UC_ERR_OK; } MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) From 0a60fa4c8a6e83a3e1ed8e302241160ab596886a Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 00:22:49 -0700 Subject: [PATCH 05/23] fix perms --- qemu/memory.c | 0 uc.c | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 qemu/memory.c mode change 100755 => 100644 uc.c diff --git a/qemu/memory.c b/qemu/memory.c old mode 100755 new mode 100644 diff --git a/uc.c b/uc.c old mode 100755 new mode 100644 From 394461b941e4597a8ae04830587b6a4499ddb5cd Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 14:01:07 -0700 Subject: [PATCH 06/23] section splitting complete for uc_mem_protect --- samples/Makefile | 4 +- samples/mem_protect.c | 323 ++++++++++++++++++++++++++++++++++++++++++ samples/mem_unmap.c | 313 ++++++++++++++++++++++++++++++++++++++++ uc.c | 71 ++++------ 4 files changed, 665 insertions(+), 46 deletions(-) create mode 100755 samples/mem_protect.c create mode 100755 samples/mem_unmap.c diff --git a/samples/Makefile b/samples/Makefile index 1ebb891f..72d1ca45 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -97,6 +97,8 @@ endif ifneq (,$(findstring x86,$(UNICORN_ARCHS))) SOURCES += sample_x86.c SOURCES += shellcode.c +SOURCES += mem_unmap.c +SOURCES += mem_protect.c endif ifneq (,$(findstring m68k,$(UNICORN_ARCHS))) SOURCES += sample_m68k.c @@ -111,7 +113,7 @@ all: $(BINARY) clean: rm -rf *.o $(OBJS_ELF) $(BINARY) $(SAMPLEDIR)/*.exe $(SAMPLEDIR)/*.static $(OBJDIR)/lib$(LIBNAME)* $(OBJDIR)/$(LIBNAME)* rm -rf libunicorn*.so libunicorn*.lib libunicorn*.dylib unicorn*.dll unicorn*.lib - rm -rf sample_x86 sample_arm sample_arm64 sample_mips sample_sparc sample_ppc sample_m68k shellcode + rm -rf sample_x86 sample_arm sample_arm64 sample_mips sample_sparc sample_ppc sample_m68k shellcode mem_unmap mem_protect $(BINARY): $(OBJS) diff --git a/samples/mem_protect.c b/samples/mem_protect.c new file mode 100755 index 00000000..4afb88c1 --- /dev/null +++ b/samples/mem_protect.c @@ -0,0 +1,323 @@ +/* + +uc_mem_protect demo / unit test + +Copyright(c) 2015 Chris Eagle + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + +#include + +unsigned char PROGRAM[] = + "\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20" + "\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90" + "\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00" + "\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xc7" + "\x05\x00\xf8\x3f\x00\x47\x47\x47\x47\xc7\x05\x00\x18\x40\x00\x48" + "\x48\x48\x48\xf4"; +// total size: 84 bytes + +/* +bits 32 + +; assumes code section at 0x100000 +; assumes data section at 0x200000, initially rw +; assumes data section at 0x300000, initially rw +; assumes data section at 0x400000, initially rw + +; with installed hooks unmaps or maps on each nop + + mov dword [0x200000], 0x41414141 + nop ; mark it RO + mov dword [0x200000], 0x42424242 + + mov dword [0x300000], 0x43434343 + nop ; mark it RO + mov dword [0x300000], 0x44444444 + + mov dword [0x400000], 0x45454545 + nop ; mark it RO + mov dword [0x400000], 0x46464646 + mov dword [0x3ff800], 0x47474747 ; make sure surrounding areas remained RW + mov dword [0x401800], 0x48484848 ; make sure surrounding areas remained RW + + hlt ; tell hook function we are done +*/ + +int test_num = 0; +uint32_t tests[] = { + 0x41414141, + 0x43434343, + 0x45454545 +}; + +static int log_num = 1; + +#define CODE_SECTION 0x100000 +#define CODE_SIZE 0x1000 + +// callback for tracing instruction +static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) +{ + uint8_t opcode; + uint32_t testval; + if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); + switch (opcode) { + case 0x90: //nop + printf("# Handling NOP\n"); + if (uc_mem_read(handle, 0x200000 + test_num * 0x100000, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } + else { + printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + printf("# uc_mem_read for test %d\n", test_num); + + if (testval == tests[test_num]) { + printf("ok %d - passed test %d\n", log_num++, test_num); + } + else { + printf("not ok %d - failed test %d\n", log_num++, test_num); + printf("# Expected: 0x%x\n",tests[test_num]); + printf("# Received: 0x%x\n", testval); + } + } + if (uc_mem_protect(handle, 0x200000 + test_num * 0x100000, 0x1000, UC_PROT_READ) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } + else { + printf("ok %d - uc_mem_protect success\n", log_num++); + } + test_num++; + break; + case 0xf4: //hlt + printf("# Handling HLT\n"); + if (uc_emu_stop(handle) != UC_ERR_OK) { + printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + _exit(1); + } + else { + printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); + } + break; + default: //all others + printf("# Handling OTHER\n"); + break; + } +} + +// callback for tracing memory access (READ or WRITE) +static void hook_mem_write(uch handle, uc_mem_type type, + uint64_t addr, int size, int64_t value, void *user_data) +{ + printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); +} + +// callback for tracing invalid memory access (READ or WRITE) +static bool hook_mem_invalid(uch handle, uc_mem_type type, + uint64_t addr, int size, int64_t value, void *user_data) +{ + uint32_t testval; + switch(type) { + default: + printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + return false; + case UC_MEM_WRITE_NW: + printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + + if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr); + } + else { + printf("ok %d - uc_mem_read success after mem_protect at test %d\n", log_num++, test_num - 1); + } + + if (uc_mem_protect(handle, addr & ~0xfff, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + else { + printf("ok %d - uc_mem_protect success\n", log_num++); + } + return true; + } +} + +int main(int argc, char **argv, char **envp) +{ + uch handle, trace1, trace2; + uc_err err; + uint32_t addr, testval; + int32_t buf1[1024], buf2[1024], readbuf[1024]; + int i; + + //don't really care about quality of randomness + srand(time(NULL)); + for (i = 0; i < 1024; i++) { + buf1[i] = rand(); + buf2[i] = rand(); + } + + printf("# Memory protect test\n"); + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); + if (err) { + printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); + return 1; + } + else { + printf("ok %d - uc_open() success\n", log_num++); + } + + uc_mem_map(handle, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(handle, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); + + // fill in sections that shouldn't get touched + if (uc_mem_write(handle, 0x3ff000, (uint8_t*)buf1, 4096)) { + printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - Random buffer 1 written to memory\n", log_num++); + } + + if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { + printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - Random buffer 2 written to memory\n", log_num++); + } + + // write machine code to be emulated to memory + if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { + printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - Program written to memory\n", log_num++); + } + + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); + return 3; + } + else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } + + // intercept memory write events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); + return 4; + } + else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } + + // intercept invalid memory events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); + return 4; + } + else { + printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + } + + // emulate machine code until told to stop by hook_code + printf("# BEGIN execution\n"); + err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 100); + if (err != UC_ERR_OK) { + printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); + return 5; + } + else { + printf("ok %d - uc_emu_start complete\n", log_num++); + } + printf("# END execution\n"); + + //read from the remapped memory + testval = 0x42424242; + for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { + uint32_t val; + if (uc_mem_read(handle, addr, (uint8_t*)&val, sizeof(val)) != UC_ERR_OK) { + printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr); + } + else { + printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr); + } + if (val != testval) { + printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval); + } + else { + printf("ok %d - Correct value retrieved\n", log_num++); + } + testval += 0x02020202; + } + + //account for the two mods made by the machine code + buf1[512] = 0x47474747; + buf2[512] = 0x48484848; + + //make sure that random blocks didn't get nuked + // fill in sections that shouldn't get touched + if (uc_mem_read(handle, 0x3ff000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); + } + else { + printf("ok %d - Random buffer 1 read from memory\n", log_num++); + if (memcmp(buf1, readbuf, 4096)) { + printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); + } + else { + printf("ok %d - Random buffer 1 contents are correct\n", log_num++); + } + } + + if (uc_mem_read(handle, 0x401000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); + } + else { + printf("ok %d - Random buffer 2 read from memory\n", log_num++); + if (memcmp(buf2, readbuf, 4096)) { + printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); + } + else { + printf("ok %d - Random buffer 2 contents are correct\n", log_num++); + } + } + + if (uc_close(&handle) == UC_ERR_OK) { + printf("ok %d - uc_close complete\n", log_num++); + } + else { + printf("not ok %d - uc_close complete\n", log_num++); + } + + return 0; +} diff --git a/samples/mem_unmap.c b/samples/mem_unmap.c new file mode 100755 index 00000000..60f3039a --- /dev/null +++ b/samples/mem_unmap.c @@ -0,0 +1,313 @@ +/* + +uc_mem_unmap demo / unit test + +Copyright(c) 2015 Chris Eagle + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + +#include + +unsigned char PROGRAM[] = + "\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20" + "\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90" + "\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00" + "\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xf4"; +// total size: 64 bytes + +/* +; assumes code section at 0x100000 +; assumes data section at 0x200000, initially rw +; assumes data section at 0x300000, initially rw +; assumes data section at 0x400000, initially rw + +; with installed hooks unmaps or maps on each nop + + mov dword [0x200000], 0x41414141 + nop ; unmap it + mov dword [0x200000], 0x42424242 + + mov dword [0x300000], 0x43434343 + nop ; unmap it + mov dword [0x300000], 0x44444444 + + mov dword [0x400000], 0x45454545 + nop ; unmap it + mov dword [0x400000], 0x46464646 + + hlt ; tell hook function we are done +*/ + +int test_num = 0; +uint32_t tests[] = { + 0x41414141, + 0x43434343, + 0x45454545 +}; + +static int log_num = 1; + +#define CODE_SECTION 0x100000 +#define CODE_SIZE 0x1000 + +// callback for tracing instruction +static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) +{ + uint8_t opcode; + uint32_t testval; + if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); + switch (opcode) { + case 0x90: //nop + printf("# Handling NOP\n"); + if (uc_mem_read(handle, 0x200000 + test_num * 0x100000, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } + else { + printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + printf("# uc_mem_read for test %d\n", test_num); + + if (testval == tests[test_num]) { + printf("ok %d - passed test %d\n", log_num++, test_num); + } + else { + printf("not ok %d - failed test %d\n", log_num++, test_num); + printf("# Expected: 0x%x\n",tests[test_num]); + printf("# Received: 0x%x\n", testval); + } + } + if (uc_mem_unmap(handle, 0x200000 + test_num * 0x100000, 0x1000) != UC_ERR_OK) { + printf("not ok %d - uc_mem_unmap fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } + else { + printf("ok %d - uc_mem_unmap success\n", log_num++); + } + test_num++; + break; + case 0xf4: //hlt + printf("# Handling HLT\n"); + if (uc_emu_stop(handle) != UC_ERR_OK) { + printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + _exit(1); + } + else { + printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); + } + break; + default: //all others + printf("# Handling OTHER\n"); + break; + } +} + +// callback for tracing memory access (READ or WRITE) +static void hook_mem_write(uch handle, uc_mem_type type, + uint64_t addr, int size, int64_t value, void *user_data) +{ + printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); +} + +// callback for tracing invalid memory access (READ or WRITE) +static bool hook_mem_invalid(uch handle, uc_mem_type type, + uint64_t addr, int size, int64_t value, void *user_data) +{ + uint32_t testval; + switch(type) { + default: + printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + return false; + case UC_MEM_WRITE: + printf("# write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + + if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr); + } + else { + printf("not ok %d - uc_mem_read success after unmap at test %d\n", log_num++, test_num - 1); + } + + if (uc_mem_map(handle, addr & ~0xfff, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + printf("not ok %d - uc_mem_map fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + else { + printf("ok %d - uc_mem_map success\n", log_num++); + } + return true; + } +} + +int main(int argc, char **argv, char **envp) +{ + uch handle, trace1, trace2; + uc_err err; + uint32_t addr, testval; + int32_t buf1[1024], buf2[1024], readbuf[1024]; + int i; + + //don't really care about quality of randomness + srand(time(NULL)); + for (i = 0; i < 1024; i++) { + buf1[i] = rand(); + buf2[i] = rand(); + } + + printf("# Memory unmapping test\n"); + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); + if (err) { + printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); + return 1; + } + else { + printf("ok %d - uc_open() success\n", log_num++); + } + + uc_mem_map(handle, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(handle, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); + + // fill in sections that shouldn't get touched + if (uc_mem_write(handle, 0x3ff000, (uint8_t*)buf1, 4096)) { + printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - Random buffer 1 written to memory\n", log_num++); + } + + if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { + printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - Random buffer 2 written to memory\n", log_num++); + } + + // write machine code to be emulated to memory + if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { + printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - Program written to memory\n", log_num++); + } + + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); + return 3; + } + else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } + + // intercept memory write events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); + return 4; + } + else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } + + // intercept invalid memory events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); + return 4; + } + else { + printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + } + + // emulate machine code until told to stop by hook_code + printf("# BEGIN execution\n"); + err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 100); + if (err != UC_ERR_OK) { + printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); + return 5; + } + else { + printf("ok %d - uc_emu_start complete\n", log_num++); + } + printf("# END execution\n"); + + //read from the remapped memory + testval = 0x42424242; + for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { + uint32_t val; + if (uc_mem_read(handle, addr, (uint8_t*)&val, sizeof(val)) != UC_ERR_OK) { + printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr); + } + else { + printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr); + } + if (val != testval) { + printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval); + } + else { + printf("ok %d - Correct value retrieved\n", log_num++); + } + testval += 0x02020202; + } + + //make sure that random blocks didn't get nuked + // fill in sections that shouldn't get touched + if (uc_mem_read(handle, 0x3ff000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); + } + else { + printf("ok %d - Random buffer 1 read from memory\n", log_num++); + if (memcmp(buf1, readbuf, 4096)) { + printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); + } + else { + printf("ok %d - Random buffer 1 contents are correct\n", log_num++); + } + } + + if (uc_mem_read(handle, 0x401000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); + } + else { + printf("ok %d - Random buffer 2 read from memory\n", log_num++); + if (memcmp(buf2, readbuf, 4096)) { + printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); + } + else { + printf("ok %d - Random buffer 2 contents are correct\n", log_num++); + } + } + + if (uc_close(&handle) == UC_ERR_OK) { + printf("ok %d - uc_close complete\n", log_num++); + } + else { + printf("not ok %d - uc_close complete\n", log_num++); + } + + return 0; +} diff --git a/uc.c b/uc.c index 6dc2786d..13b395cf 100644 --- a/uc.c +++ b/uc.c @@ -638,7 +638,7 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_MAP; // check for only valid permissions - if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)) != 0) + if ((perms & ~UC_PROT_ALL) != 0) return UC_ERR_MAP; if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow @@ -782,7 +782,7 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_MAP; // check for only valid permissions - if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC)) != 0) + if ((perms & ~UC_PROT_ALL) != 0) return UC_ERR_MAP; //check that user's entire requested block is mapped @@ -790,50 +790,32 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_MAP; //Now we know entire region is mapped, so change permissions - //check trivial case first + //If request exactly matches a region we don't need to split mr = memory_mapping(uc, address); - if (address == mr->addr && size == int128_get64(mr->size)) { - //regions exactly matches an existing region just change perms - mr->perms = perms; - uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); - } - else { + if (address != mr->addr || size != int128_get64(mr->size)) { //ouch, we are going to need to subdivide blocks -/* - address = start; - size = block_size; - while (size > 0) { - MemoryRegion *mr = memory_mapping(uc, address); - uint64_t region_size = int128_get64(mr->size); - if (address > mr->addr) { - //in case start address is not aligned with start of region - region_size -= address - mr->addr; - //TODO Learn how to split regions - //In this case some proper subset of the region is having it's permissions changed - //need to split region and add new portions into uc->mapped_blocks list - //In this case, there is a portion of the region with original perms: mr->addr..start - //and a portion getting new perms: start..start+block_size - - //split the block and stay in the loop - } - if (size < int128_get64(mr->size)) { - //TODO Learn how to split regions - //In this case some proper subset of the region is having it's permissions changed - //need to split region and add new portions into uc->mapped_blocks list - //In this case, there is a portion of the region with new perms: start..start+block_size - //and a portion getting new perms: mr->addr+size..mr->addr+mr->size - - //split the block and break - break; - } - size -= int128_get64(mr->size); - address += int128_get64(mr->size); - mr->perms = perms; - uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + uint64_t addr = address; + size_t count = 0, len; + while(count < size) { + MemoryRegion *mr = memory_mapping(uc, addr); + len = MIN(size - count, mr->end - addr); + if (!split_region(handle, mr, addr, len, false)) + return UC_ERR_MAP; + count += len; + addr += len; } -*/ - } - return UC_ERR_OK; + //Grab a pointer to the newly split MemoryRegion + mr = memory_mapping(uc, address); + if (mr == NULL) { + //this should never happern if splitting succeeded + return UC_ERR_MAP; + } + } + //regions exactly matches an existing region just change perms + mr->perms = perms; + uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + + return UC_ERR_OK; } UNICORN_EXPORT @@ -842,7 +824,6 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) MemoryRegion *mr; unsigned int i; struct uc_struct* uc = (struct uc_struct *)handle; - size_t count, len; if (handle == 0) // invalid handle @@ -882,7 +863,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) } else { //ouch, we are going to need to subdivide blocks - count = 0; + size_t count = 0, len; while(count < size) { MemoryRegion *mr = memory_mapping(uc, address); len = MIN(size - count, mr->end - address); From 05b645abd5922f3fe54ab92e8e66735d0e472b4a Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 14:01:46 -0700 Subject: [PATCH 07/23] fix perms --- samples/mem_protect.c | 0 samples/mem_unmap.c | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 samples/mem_protect.c mode change 100755 => 100644 samples/mem_unmap.c diff --git a/samples/mem_protect.c b/samples/mem_protect.c old mode 100755 new mode 100644 diff --git a/samples/mem_unmap.c b/samples/mem_unmap.c old mode 100755 new mode 100644 From 9e4e96ff47b540583a9869ad54b794316698985a Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 19:50:18 -0700 Subject: [PATCH 08/23] final updates for uc_mem_unmap, uc_mem_protect, and support ro UC_PROT_EXEC permission --- qemu/cputlb.c | 5 + samples/Makefile | 4 +- samples/mem_exec.c | 296 ++++++++++++++++++++++++++++++++++++++++++ samples/mem_protect.c | 18 +-- samples/mem_unmap.c | 18 +-- 5 files changed, 322 insertions(+), 19 deletions(-) create mode 100644 samples/mem_exec.c diff --git a/qemu/cputlb.c b/qemu/cputlb.c index cde8e30e..ed120082 100644 --- a/qemu/cputlb.c +++ b/qemu/cputlb.c @@ -299,6 +299,11 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code != (addr & TARGET_PAGE_MASK))) { cpu_ldub_code(env1, addr); + //check for NX related error from softmmu + if (env1->invalid_error == UC_ERR_MEM_READ) { + env1->invalid_error = UC_ERR_CODE_INVALID; + return -1; + } } pd = env1->iotlb[mmu_idx][page_index] & ~TARGET_PAGE_MASK; mr = iotlb_to_region(cpu->as, pd); diff --git a/samples/Makefile b/samples/Makefile index 72d1ca45..f6345ae8 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -99,6 +99,7 @@ SOURCES += sample_x86.c SOURCES += shellcode.c SOURCES += mem_unmap.c SOURCES += mem_protect.c +SOURCES += mem_exec.c endif ifneq (,$(findstring m68k,$(UNICORN_ARCHS))) SOURCES += sample_m68k.c @@ -113,7 +114,8 @@ all: $(BINARY) clean: rm -rf *.o $(OBJS_ELF) $(BINARY) $(SAMPLEDIR)/*.exe $(SAMPLEDIR)/*.static $(OBJDIR)/lib$(LIBNAME)* $(OBJDIR)/$(LIBNAME)* rm -rf libunicorn*.so libunicorn*.lib libunicorn*.dylib unicorn*.dll unicorn*.lib - rm -rf sample_x86 sample_arm sample_arm64 sample_mips sample_sparc sample_ppc sample_m68k shellcode mem_unmap mem_protect + rm -rf sample_x86 sample_arm sample_arm64 sample_mips sample_sparc sample_ppc sample_m68k \ + shellcode mem_unmap mem_protect mem_exec $(BINARY): $(OBJS) diff --git a/samples/mem_exec.c b/samples/mem_exec.c new file mode 100644 index 00000000..b83ea7d0 --- /dev/null +++ b/samples/mem_exec.c @@ -0,0 +1,296 @@ +/* + +Executable memory regions demo / unit test + +Copyright(c) 2015 Chris Eagle + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 2 as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include + +#include + +unsigned char PROGRAM[] = + "\xeb\x45\x5e\x81\xe6\x00\xf0\xff\xff\x40\x40\x40\x40\x40\x40\x40" + "\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40" + "\x40\x40\x40\x40\x40\x40\x40\x89\xf7\x81\xc7\x00\x00\x10\x00\xb9" + "\x4c\x00\x00\x00\x81\xff\x00\x00\x40\x00\x75\x01\xf4\xf3\xa4\x81" + "\xe7\x00\xf0\xff\xff\xff\xe7\xe8\xb6\xff\xff\xff"; +// total size: 76 bytes + +/* +bits 32 + +; assumes r-x section at 0x100000 +; assumes rw- section at 0x200000 +; assumes r-- section at 0x300000 +; also needs an initialized stack + +start: + jmp bottom +top: + pop esi + and esi, ~0xfff + times 30 inc eax + mov edi, esi + add edi, 0x100000 + mov ecx, end - start + rep movsb + and edi, ~0xfff + cmp edi, 0x400000 + jnz next_block + hlt +next_block: + jmp edi +bottom: + call top +end: +*/ + +int test_num = 0; +uint32_t tests[] = { + 0x41414141, + 0x43434343, + 0x45454545 +}; + +static int log_num = 1; + +#define CODE_SECTION 0x100000 +#define CODE_SIZE 0x1000 + +// callback for tracing instruction +static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) +{ + uint8_t opcode; + if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } +// printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); + switch (opcode) { + case 0xf4: //hlt + printf("# Handling HLT\n"); + if (uc_emu_stop(handle) != UC_ERR_OK) { + printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + _exit(-1); + } + else { + printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); + } + break; + default: //all others +// printf("# Handling OTHER\n"); + break; + } +} + +// callback for tracing memory access (READ or WRITE) +static void hook_mem_write(uch handle, uc_mem_type type, + uint64_t addr, int size, int64_t value, void *user_data) +{ + printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); +} + +// callback for tracing invalid memory access (READ or WRITE) +static bool hook_mem_invalid(uch handle, uc_mem_type type, + uint64_t addr, int size, int64_t value, void *user_data) +{ + switch(type) { + default: + printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + return false; + case UC_MEM_NX: + printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); + + //make page executable + if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_EXEC) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail for address: 0x%" PRIx64 "\n", log_num++, addr); + } + else { + printf("ok %d - uc_mem_protect success at 0x%" PRIx64 "\n", log_num++, addr); + } + return true; + case UC_MEM_WRITE_NW: + printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + + if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + else { + printf("ok %d - uc_mem_protect success\n", log_num++); + } + return true; + } +} + +int main(int argc, char **argv, char **envp) +{ + uch handle, trace1, trace2; + uc_err err; + uint32_t esp, eip; + int32_t buf1[1024], buf2[1024], readbuf[1024]; + int i; + + //don't really care about quality of randomness + srand(time(NULL)); + for (i = 0; i < 1024; i++) { + buf1[i] = rand(); + buf2[i] = rand(); + } + + printf("# Memory protect test\n"); + + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); + if (err) { + printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); + return 1; + } + else { + printf("ok %d - uc_open() success\n", log_num++); + } + + uc_mem_map(handle, 0x100000, 0x1000, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(handle, 0x1ff000, 0x2000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x300000, 0x2000, UC_PROT_READ); + uc_mem_map(handle, 0xf00000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + + esp = 0xf00000 + 0x1000; + + // Setup stack pointer + if (uc_reg_write(handle, UC_X86_REG_ESP, &esp)) { + printf("not ok %d - Failed to set esp. quit!\n", log_num++); + return 2; + } + else { + printf("ok %d - ESP set\n", log_num++); + } + + // fill in sections that shouldn't get touched + if (uc_mem_write(handle, 0x1ff000, (uint8_t*)buf1, 4096)) { + printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); + return 3; + } + else { + printf("ok %d - Random buffer 1 written to memory\n", log_num++); + } + + if (uc_mem_write(handle, 0x301000, (uint8_t*)buf2, 4096)) { + printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); + return 4; + } + else { + printf("ok %d - Random buffer 2 written to memory\n", log_num++); + } + + // write machine code to be emulated to memory + if (uc_mem_write(handle, 0x100000, PROGRAM, sizeof(PROGRAM))) { + printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); + return 5; + } + else { + printf("ok %d - Program written to memory\n", log_num++); + } + + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); + return 6; + } + else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } + + // intercept memory write events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); + return 7; + } + else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } + + // intercept invalid memory events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); + return 8; + } + else { + printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + } + + // emulate machine code until told to stop by hook_code + printf("# BEGIN execution\n"); + err = uc_emu_start(handle, 0x100000, 0x400000, 0, 0); + if (err != UC_ERR_OK) { + printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); + return 9; + } + else { + printf("ok %d - uc_emu_start complete\n", log_num++); + } + printf("# END execution\n"); + + // get ending EIP + if (uc_reg_read(handle, UC_X86_REG_EIP, &eip)) { + printf("not ok %d - Failed to read eip.\n", log_num++); + } + else { + printf("ok %d - Ending EIP 0x%x\n", log_num++, eip); + } + + //make sure that random blocks didn't get nuked + // fill in sections that shouldn't get touched + if (uc_mem_read(handle, 0x1ff000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); + } + else { + printf("ok %d - Random buffer 1 read from memory\n", log_num++); + if (memcmp(buf1, readbuf, 4096)) { + printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); + } + else { + printf("ok %d - Random buffer 1 contents are correct\n", log_num++); + } + } + + if (uc_mem_read(handle, 0x301000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); + } + else { + printf("ok %d - Random buffer 2 read from memory\n", log_num++); + if (memcmp(buf2, readbuf, 4096)) { + printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); + } + else { + printf("ok %d - Random buffer 2 contents are correct\n", log_num++); + } + } + + if (uc_close(&handle) == UC_ERR_OK) { + printf("ok %d - uc_close complete\n", log_num++); + } + else { + printf("not ok %d - uc_close complete\n", log_num++); + } + + return 0; +} diff --git a/samples/mem_protect.c b/samples/mem_protect.c index 4afb88c1..025f9953 100644 --- a/samples/mem_protect.c +++ b/samples/mem_protect.c @@ -117,7 +117,7 @@ static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) printf("# Handling HLT\n"); if (uc_emu_stop(handle) != UC_ERR_OK) { printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - _exit(1); + _exit(-1); } else { printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); @@ -155,7 +155,7 @@ static bool hook_mem_invalid(uch handle, uc_mem_type type, printf("ok %d - uc_mem_read success after mem_protect at test %d\n", log_num++, test_num - 1); } - if (uc_mem_protect(handle, addr & ~0xfff, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); } else { @@ -208,7 +208,7 @@ int main(int argc, char **argv, char **envp) if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); - return 2; + return 3; } else { printf("ok %d - Random buffer 2 written to memory\n", log_num++); @@ -217,7 +217,7 @@ int main(int argc, char **argv, char **envp) // write machine code to be emulated to memory if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); - return 2; + return 4; } else { printf("ok %d - Program written to memory\n", log_num++); @@ -225,7 +225,7 @@ int main(int argc, char **argv, char **envp) if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); - return 3; + return 5; } else { printf("ok %d - UC_HOOK_CODE installed\n", log_num++); @@ -234,7 +234,7 @@ int main(int argc, char **argv, char **envp) // intercept memory write events if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); - return 4; + return 6; } else { printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); @@ -243,7 +243,7 @@ int main(int argc, char **argv, char **envp) // intercept invalid memory events if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); - return 4; + return 7; } else { printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); @@ -251,10 +251,10 @@ int main(int argc, char **argv, char **envp) // emulate machine code until told to stop by hook_code printf("# BEGIN execution\n"); - err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 100); + err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0); if (err != UC_ERR_OK) { printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); - return 5; + return 8; } else { printf("ok %d - uc_emu_start complete\n", log_num++); diff --git a/samples/mem_unmap.c b/samples/mem_unmap.c index 60f3039a..6f93673d 100644 --- a/samples/mem_unmap.c +++ b/samples/mem_unmap.c @@ -111,7 +111,7 @@ static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) printf("# Handling HLT\n"); if (uc_emu_stop(handle) != UC_ERR_OK) { printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - _exit(1); + _exit(-1); } else { printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); @@ -149,7 +149,7 @@ static bool hook_mem_invalid(uch handle, uc_mem_type type, printf("not ok %d - uc_mem_read success after unmap at test %d\n", log_num++, test_num - 1); } - if (uc_mem_map(handle, addr & ~0xfff, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + if (uc_mem_map(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { printf("not ok %d - uc_mem_map fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); } else { @@ -202,7 +202,7 @@ int main(int argc, char **argv, char **envp) if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); - return 2; + return 3; } else { printf("ok %d - Random buffer 2 written to memory\n", log_num++); @@ -211,7 +211,7 @@ int main(int argc, char **argv, char **envp) // write machine code to be emulated to memory if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); - return 2; + return 4; } else { printf("ok %d - Program written to memory\n", log_num++); @@ -219,7 +219,7 @@ int main(int argc, char **argv, char **envp) if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); - return 3; + return 5; } else { printf("ok %d - UC_HOOK_CODE installed\n", log_num++); @@ -228,7 +228,7 @@ int main(int argc, char **argv, char **envp) // intercept memory write events if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); - return 4; + return 6; } else { printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); @@ -237,7 +237,7 @@ int main(int argc, char **argv, char **envp) // intercept invalid memory events if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); - return 4; + return 7; } else { printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); @@ -245,10 +245,10 @@ int main(int argc, char **argv, char **envp) // emulate machine code until told to stop by hook_code printf("# BEGIN execution\n"); - err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 100); + err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0); if (err != UC_ERR_OK) { printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); - return 5; + return 8; } else { printf("ok %d - uc_emu_start complete\n", log_num++); From 24dde77ec2392223a81ab02ece87188f8ee4ade5 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 20:38:38 -0700 Subject: [PATCH 09/23] fix uc_mem_type comments --- include/unicorn/unicorn.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index fee75297..e4b5e359 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -149,9 +149,9 @@ 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_NX, // read from non-readable + UC_MEM_WRITE_NW, // write to non-writable memory + UC_MEM_READ_NR, // read from non-readable memory + UC_MEM_NX, // fetch from non-executable memory } uc_mem_type; // All type of hooks for uc_hook_add() API. From 410e317e9265412a649b6df5f3b04a8b44f37228 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Sun, 30 Aug 2015 21:24:14 -0700 Subject: [PATCH 10/23] dont use explicit page size, use TARGET_PAGE_SIZE --- qemu/memory.c | 2 +- uc.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qemu/memory.c b/qemu/memory.c index 5e9cc9b0..f5b70f35 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -49,7 +49,7 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { target_ulong addr; //make sure all pages associated with the MemoryRegion are flushed - for (addr = mr->addr; addr < mr->end; addr += 0x1000) { + for (addr = mr->addr; addr < mr->end; addr += TARGET_PAGE_SIZE) { tlb_flush_page(uc->current_cpu, addr); } mr->enabled = false; diff --git a/uc.c b/uc.c index 13b395cf..ad2a7325 100644 --- a/uc.c +++ b/uc.c @@ -32,8 +32,8 @@ #include "qemu/include/hw/boards.h" //keep this a power of two! -#define UC_BLOCK_SIZE 0x1000 -#define UC_ALIGN_MASK (UC_BLOCK_SIZE - 1) +#define UC_PAGE_SIZE 0x1000 +#define UC_ALIGN_MASK (UC_PAGE_SIZE - 1) static uint8_t *copy_region(uch uc, MemoryRegion *mr); static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete); @@ -629,11 +629,11 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) // invalid memory mapping return UC_ERR_MAP; - // address must be aligned to UC_BLOCK_SIZE + // address must be aligned to UC_PAGE_SIZE if ((address & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; - // size must be multiple of UC_BLOCK_SIZE + // size must be multiple of UC_PAGE_SIZE if ((size & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; @@ -773,11 +773,11 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) // invalid memory mapping return UC_ERR_MAP; - // address must be aligned to UC_BLOCK_SIZE + // address must be aligned to UC_PAGE_SIZE if ((address & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; - // size must be multiple of UC_BLOCK_SIZE + // size must be multiple of UC_PAGE_SIZE if ((size & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; @@ -833,11 +833,11 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) // nothing to unmap return UC_ERR_OK; - // address must be aligned to UC_BLOCK_SIZE + // address must be aligned to UC_PAGE_SIZE if ((address & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; - // size must be multiple of UC_BLOCK_SIZE + // size must be multiple of UC_PAGE_SIZE if ((size & UC_ALIGN_MASK) != 0) return UC_ERR_MAP; From b27e9879321606997851b86890e2c6b30a909da8 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Mon, 31 Aug 2015 01:00:44 -0700 Subject: [PATCH 11/23] Add target_page_size member to uc_struct to track TARGET_PAGE_SIZE --- include/uc_priv.h | 3 +++ qemu/memory.c | 2 +- qemu/unicorn_common.h | 3 +++ uc.c | 28 ++++++++++++---------------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/include/uc_priv.h b/include/uc_priv.h index 695b5fc6..aff7a7d7 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -175,6 +175,9 @@ struct uc_struct { bool block_full; MemoryRegion **mapped_blocks; uint32_t mapped_block_count; + + uint32_t target_page_size; + uint32_t target_page_align; }; #include "qemu_macro.h" diff --git a/qemu/memory.c b/qemu/memory.c index f5b70f35..e04d59b7 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -49,7 +49,7 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { target_ulong addr; //make sure all pages associated with the MemoryRegion are flushed - for (addr = mr->addr; addr < mr->end; addr += TARGET_PAGE_SIZE) { + for (addr = mr->addr; addr < mr->end; addr += uc->target_page_size) { tlb_flush_page(uc->current_cpu, addr); } mr->enabled = false; diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 23ef0acb..5ba74fac 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -76,6 +76,9 @@ static inline void uc_common_init(struct uc_struct* uc) uc->memory_unmap = memory_unmap; uc->readonly_mem = memory_region_set_readonly; + uc->target_page_size = TARGET_PAGE_SIZE; + uc->target_page_align = TARGET_PAGE_SIZE - 1; + if (!uc->release) uc->release = release_common; } diff --git a/uc.c b/uc.c index ad2a7325..2dddb39c 100644 --- a/uc.c +++ b/uc.c @@ -31,10 +31,6 @@ #include "qemu/include/hw/boards.h" -//keep this a power of two! -#define UC_PAGE_SIZE 0x1000 -#define UC_ALIGN_MASK (UC_PAGE_SIZE - 1) - static uint8_t *copy_region(uch uc, MemoryRegion *mr); static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete); @@ -629,12 +625,12 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) // invalid memory mapping return UC_ERR_MAP; - // address must be aligned to UC_PAGE_SIZE - if ((address & UC_ALIGN_MASK) != 0) + // address must be aligned to uc->target_page_size + if ((address & uc->target_page_align) != 0) return UC_ERR_MAP; - // size must be multiple of UC_PAGE_SIZE - if ((size & UC_ALIGN_MASK) != 0) + // size must be multiple of uc->target_page_size + if ((size & uc->target_page_align) != 0) return UC_ERR_MAP; // check for only valid permissions @@ -773,12 +769,12 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) // invalid memory mapping return UC_ERR_MAP; - // address must be aligned to UC_PAGE_SIZE - if ((address & UC_ALIGN_MASK) != 0) + // address must be aligned to uc->target_page_size + if ((address & uc->target_page_align) != 0) return UC_ERR_MAP; - // size must be multiple of UC_PAGE_SIZE - if ((size & UC_ALIGN_MASK) != 0) + // size must be multiple of uc->target_page_size + if ((size & uc->target_page_align) != 0) return UC_ERR_MAP; // check for only valid permissions @@ -833,12 +829,12 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) // nothing to unmap return UC_ERR_OK; - // address must be aligned to UC_PAGE_SIZE - if ((address & UC_ALIGN_MASK) != 0) + // address must be aligned to uc->target_page_size + if ((address & uc->target_page_align) != 0) return UC_ERR_MAP; - // size must be multiple of UC_PAGE_SIZE - if ((size & UC_ALIGN_MASK) != 0) + // size must be multiple of uc->target_page_size + if ((size & uc->target_page_align) != 0) return UC_ERR_MAP; //check that user's entire requested block is mapped From 658e399776954f046689c5409486437af5e9bc3d Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Mon, 31 Aug 2015 19:08:48 -0700 Subject: [PATCH 12/23] clean up mem_protect related constants --- include/unicorn/unicorn.h | 5 +++-- qemu/softmmu_template.h | 16 ++++++++-------- uc.c | 10 ++++++---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index e4b5e359..962598a8 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -116,8 +116,9 @@ 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_PROT_WRITE, // Quit emulation due to UC_PROT_WRITE violation: uc_emu_start() + UC_ERR_PROT_READ, // Quit emulation due to UC_PROT_READ violation: uc_emu_start() + UC_ERR_PROT_EXEC, // Quit emulation due to UC_PROT_EXEC violation: uc_emu_start() } uc_err; diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 8c181824..a448a787 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -191,8 +191,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_MEM_READ; - // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); + env->invalid_error = UC_ERR_PROT_EXEC; + // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; } @@ -233,7 +233,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_MEM_READ_NR; + env->invalid_error = UC_ERR_PROT_READ; // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -350,8 +350,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_MEM_READ; - // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); + env->invalid_error = UC_ERR_PROT_EXEC; + // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; } @@ -392,7 +392,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_MEM_READ_NR; + env->invalid_error = UC_ERR_PROT_READ; // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -571,7 +571,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_MEM_WRITE_NW; + env->invalid_error = UC_ERR_PROT_WRITE; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return; @@ -705,7 +705,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_MEM_WRITE_NW; + env->invalid_error = UC_ERR_PROT_WRITE; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return; diff --git a/uc.c b/uc.c index 2dddb39c..85624a36 100644 --- a/uc.c +++ b/uc.c @@ -92,10 +92,12 @@ 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)"; + case UC_ERR_PROT_WRITE: + return "Write to non-writable memory (UC_ERR_PROT_WRITE)"; + case UC_ERR_PROT_READ: + return "Read from non-readable memory (UC_ERR_PROT_READ)"; + case UC_ERR_PROT_EXEC: + return "Fetch from non-executable memory (UC_ERR_PROT_EXEC)"; } } From 2c4f3769d443969be07556c27f394d84fc9e2bb6 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Tue, 1 Sep 2015 12:10:09 -0700 Subject: [PATCH 13/23] clean up mem_protect related constants and error codes --- include/unicorn/unicorn.h | 12 ++++++------ qemu/softmmu_template.h | 24 ++++++++++++------------ samples/mem_exec.c | 4 ++-- samples/mem_protect.c | 2 +- uc.c | 12 ++++++------ 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 962598a8..9a39c8a6 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -116,9 +116,9 @@ 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_PROT_WRITE, // Quit emulation due to UC_PROT_WRITE violation: uc_emu_start() - UC_ERR_PROT_READ, // Quit emulation due to UC_PROT_READ violation: uc_emu_start() - UC_ERR_PROT_EXEC, // Quit emulation due to UC_PROT_EXEC violation: uc_emu_start() + UC_ERR_WRITE_PROT, // Quit emulation due to UC_PROT_WRITE violation: uc_emu_start() + UC_ERR_READ_PROT, // Quit emulation due to UC_PROT_READ violation: uc_emu_start() + UC_ERR_EXEC_PROT, // Quit emulation due to UC_PROT_EXEC violation: uc_emu_start() } uc_err; @@ -150,9 +150,9 @@ 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 memory - UC_MEM_READ_NR, // read from non-readable memory - UC_MEM_NX, // fetch from non-executable memory + UC_MEM_WRITE_PROT, // write to write protected memory + UC_MEM_READ_PROT, // read from read protected memory + UC_MEM_EXEC_PROT, // fetch from non-executable memory } uc_mem_type; // All type of hooks for uc_hook_add() API. diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index a448a787..de47e769 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -185,13 +185,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { //non-executable if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - (uch)uc, UC_MEM_NX, addr, DATA_SIZE, 0, + (uch)uc, UC_MEM_EXEC_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_PROT_EXEC; + env->invalid_error = UC_ERR_EXEC_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -227,13 +227,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on non-readable memory if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - (uch)uc, UC_MEM_READ_NR, addr, DATA_SIZE, 0, + (uch)uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_PROT_READ; + env->invalid_error = UC_ERR_READ_PROT; // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -344,13 +344,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on fetch from NX if (mr != NULL && !(mr->perms & UC_PROT_EXEC)) { //non-executable if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - (uch)uc, UC_MEM_NX, addr, DATA_SIZE, 0, + (uch)uc, UC_MEM_EXEC_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_PROT_EXEC; + env->invalid_error = UC_ERR_EXEC_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -386,13 +386,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on non-readable memory if (mr != NULL && !(mr->perms & UC_PROT_READ)) { //non-readable if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - (uch)uc, UC_MEM_READ_NR, addr, DATA_SIZE, 0, + (uch)uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_PROT_READ; + env->invalid_error = UC_ERR_READ_PROT; // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -565,13 +565,13 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - (uch)uc, UC_MEM_WRITE_NW, addr, DATA_SIZE, (int64_t)val, + (uch)uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_PROT_WRITE; + env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return; @@ -699,13 +699,13 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, // Unicorn: callback on non-writable memory if (mr != NULL && !(mr->perms & UC_PROT_WRITE)) { //non-writable if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - (uch)uc, UC_MEM_WRITE_NW, addr, DATA_SIZE, (int64_t)val, + (uch)uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_PROT_WRITE; + env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return; diff --git a/samples/mem_exec.c b/samples/mem_exec.c index b83ea7d0..171022a7 100644 --- a/samples/mem_exec.c +++ b/samples/mem_exec.c @@ -118,7 +118,7 @@ static bool hook_mem_invalid(uch handle, uc_mem_type type, default: printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); return false; - case UC_MEM_NX: + case UC_MEM_EXEC_PROT: printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); //make page executable @@ -129,7 +129,7 @@ static bool hook_mem_invalid(uch handle, uc_mem_type type, printf("ok %d - uc_mem_protect success at 0x%" PRIx64 "\n", log_num++, addr); } return true; - case UC_MEM_WRITE_NW: + case UC_MEM_WRITE_PROT: printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { diff --git a/samples/mem_protect.c b/samples/mem_protect.c index 025f9953..5d852ff5 100644 --- a/samples/mem_protect.c +++ b/samples/mem_protect.c @@ -145,7 +145,7 @@ static bool hook_mem_invalid(uch handle, uc_mem_type type, default: printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); return false; - case UC_MEM_WRITE_NW: + case UC_MEM_WRITE_PROT: printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { diff --git a/uc.c b/uc.c index 85624a36..177c8cf3 100644 --- a/uc.c +++ b/uc.c @@ -92,12 +92,12 @@ 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_PROT_WRITE: - return "Write to non-writable memory (UC_ERR_PROT_WRITE)"; - case UC_ERR_PROT_READ: - return "Read from non-readable memory (UC_ERR_PROT_READ)"; - case UC_ERR_PROT_EXEC: - return "Fetch from non-executable memory (UC_ERR_PROT_EXEC)"; + case UC_ERR_WRITE_PROT: + return "Write to write-protected memory (UC_ERR_WRITE_PROT)"; + case UC_ERR_READ_PROT: + return "Read from non-readable memory (UC_ERR_READ_PROT)"; + case UC_ERR_EXEC_PROT: + return "Fetch from non-executable memory (UC_ERR_EXEC_PROT)"; } } From ad877e6af059c77980675a914c41a0d8a4d43a21 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Tue, 1 Sep 2015 13:40:19 -0700 Subject: [PATCH 14/23] Add error value UC_ERR_INVAL and rename UC_ERR_OOM to UC_ERR_NOMEM to provide more error specificity --- include/unicorn/unicorn.h | 28 ++++++++++---------- uc.c | 54 ++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 9a39c8a6..cbac0506 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -104,7 +104,7 @@ typedef enum uc_mode { // These are values returned by uc_errno() typedef enum uc_err { UC_ERR_OK = 0, // No error: everything was fine - UC_ERR_OOM, // Out-Of-Memory error: uc_open(), uc_emulate() + UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate() UC_ERR_ARCH, // Unsupported architecture: uc_open() UC_ERR_HANDLE, // Invalid handle UC_ERR_UCH, // Invalid handle (uch) @@ -119,6 +119,7 @@ typedef enum uc_err { UC_ERR_WRITE_PROT, // Quit emulation due to UC_PROT_WRITE violation: uc_emu_start() UC_ERR_READ_PROT, // Quit emulation due to UC_PROT_READ violation: uc_emu_start() UC_ERR_EXEC_PROT, // Quit emulation due to UC_PROT_EXEC violation: uc_emu_start() + UC_ERR_INVAL, // Inavalid argument provided to uc_xxx function (See specific function API) } uc_err; @@ -405,15 +406,15 @@ typedef enum uc_prot { @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. + This address must be aligned to 4KB, or this will return with UC_ERR_INVAL 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. + This size must be multiple of 4KB, or this will return with UC_ERR_INVAL error. @perms: Permissions for the newly mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, - or this will return with UC_ERR_MAP error. + or this will return with UC_ERR_INVAL error. - @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + @return UC_ERR_OK on success, UC_ERR_NOMEM if no memory is available to satisfy the + request, or other value on failure (refer to uc_err enum for detailed error). */ UNICORN_EXPORT uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); @@ -424,15 +425,16 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); @handle: handle returned by uc_open() @address: starting address of the memory region to be modified. - This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. + This address must be aligned to 4KB, or this will return with UC_ERR_INVAL error. @size: size of the memory region to be modified. - This size must be multiple of 4KB, or this will return with UC_ERR_MAP error. + This size must be multiple of 4KB, or this will return with UC_ERR_INVAL error. @perms: New permissions for the mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, - or this will return with UC_ERR_MAP error. + or this will return with UC_ERR_INVAL error. - @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum - for detailed error). + @return UC_ERR_OK on success, UC_ERR_HANDLE for an invalid handle, UC_ERR_INVAL + for invalid perms or unaligned address or size, UC_ERR_NOMEM if entire region + is not mapped. */ UNICORN_EXPORT uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms); @@ -443,9 +445,9 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) @handle: handle returned by uc_open() @address: starting address of the memory region to be unmapped. - This address must be aligned to 4KB, or this will return with UC_ERR_MAP error. + This address must be aligned to 4KB, or this will return with UC_ERR_INVAL error. @size: size of the memory region to be modified. - This size must be multiple of 4KB, or this will return with UC_ERR_MAP error. + This size must be multiple of 4KB, or this will return with UC_ERR_INVAL error. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). diff --git a/uc.c b/uc.c index 177c8cf3..32a9af70 100644 --- a/uc.c +++ b/uc.c @@ -68,8 +68,8 @@ const char *uc_strerror(uc_err code) return "Unknown error code"; case UC_ERR_OK: return "OK (UC_ERR_OK)"; - case UC_ERR_OOM: - return "Out of memory (UC_ERR_OOM)"; + case UC_ERR_NOMEM: + return "No memory available or memory not present (UC_ERR_NOMEM)"; case UC_ERR_ARCH: return "Invalid/unsupported architecture(UC_ERR_ARCH)"; case UC_ERR_HANDLE: @@ -98,6 +98,8 @@ const char *uc_strerror(uc_err code) return "Read from non-readable memory (UC_ERR_READ_PROT)"; case UC_ERR_EXEC_PROT: return "Fetch from non-executable memory (UC_ERR_EXEC_PROT)"; + case UC_ERR_INVAL: + return "Invalid argumet (UC_ERR_INVAL)"; } } @@ -143,7 +145,7 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uch *handle) uc = calloc(1, sizeof(*uc)); if (!uc) { // memory insufficient - return UC_ERR_OOM; + return UC_ERR_NOMEM; } uc->errnum = UC_ERR_OK; @@ -590,7 +592,7 @@ static int _hook_code(uch handle, int type, uint64_t begin, uint64_t end, i = hook_add(handle, type, begin, end, callback, user_data); if (i == 0) - return UC_ERR_OOM; // FIXME + return UC_ERR_NOMEM; // FIXME *h2 = i; @@ -606,7 +608,7 @@ static uc_err _hook_mem_access(uch handle, uc_mem_type type, i = hook_add(handle, type, begin, end, callback, user_data); if (i == 0) - return UC_ERR_OOM; // FIXME + return UC_ERR_NOMEM; // FIXME *h2 = i; @@ -625,24 +627,24 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) if (size == 0) // invalid memory mapping - return UC_ERR_MAP; + return UC_ERR_INVAL; // address must be aligned to uc->target_page_size if ((address & uc->target_page_align) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; // size must be multiple of uc->target_page_size if ((size & uc->target_page_align) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; // check for only valid permissions if ((perms & ~UC_PROT_ALL) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow regions = (MemoryRegion**)realloc(uc->mapped_blocks, sizeof(MemoryRegion*) * (uc->mapped_block_count + MEM_BLOCK_INCR)); if (regions == NULL) { - return UC_ERR_OOM; + return UC_ERR_NOMEM; } uc->mapped_blocks = regions; } @@ -768,24 +770,24 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_UCH; if (size == 0) - // invalid memory mapping - return UC_ERR_MAP; + // trivial case, no change + return UC_ERR_OK; // address must be aligned to uc->target_page_size if ((address & uc->target_page_align) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; // size must be multiple of uc->target_page_size if ((size & uc->target_page_align) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; // check for only valid permissions if ((perms & ~UC_PROT_ALL) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; //check that user's entire requested block is mapped if (!check_mem_area(uc, address, size)) - return UC_ERR_MAP; + return UC_ERR_NOMEM; //Now we know entire region is mapped, so change permissions //If request exactly matches a region we don't need to split @@ -798,7 +800,7 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) MemoryRegion *mr = memory_mapping(uc, addr); len = MIN(size - count, mr->end - addr); if (!split_region(handle, mr, addr, len, false)) - return UC_ERR_MAP; + return UC_ERR_NOMEM; count += len; addr += len; } @@ -806,7 +808,7 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) mr = memory_mapping(uc, address); if (mr == NULL) { //this should never happern if splitting succeeded - return UC_ERR_MAP; + return UC_ERR_NOMEM; } } //regions exactly matches an existing region just change perms @@ -833,7 +835,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) // address must be aligned to uc->target_page_size if ((address & uc->target_page_align) != 0) - return UC_ERR_MAP; + return UC_ERR_INVAL; // size must be multiple of uc->target_page_size if ((size & uc->target_page_align) != 0) @@ -841,7 +843,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) //check that user's entire requested block is mapped if (!check_mem_area(uc, address, size)) - return UC_ERR_MAP; + return UC_ERR_NOMEM; //Now we know entire region is mapped, so begin the delete //check trivial case first @@ -866,7 +868,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) MemoryRegion *mr = memory_mapping(uc, address); len = MIN(size - count, mr->end - address); if (!split_region(handle, mr, address, len, true)) - return UC_ERR_MAP; + return UC_ERR_NOMEM; count += len; address += len; } @@ -902,7 +904,7 @@ static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback, uc->hook_mem_idx = i; return UC_ERR_OK; } else - return UC_ERR_OOM; + return UC_ERR_NOMEM; } @@ -921,7 +923,7 @@ static uc_err _hook_intr(struct uc_struct* uc, void *callback, uc->hook_intr_idx = i; return UC_ERR_OK; } else - return UC_ERR_OOM; + return UC_ERR_NOMEM; } @@ -945,7 +947,7 @@ static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callb uc->hook_out_idx = i; return UC_ERR_OK; } else - return UC_ERR_OOM; + return UC_ERR_NOMEM; case UC_X86_INS_IN: // FIXME: only one event handler at the same time i = hook_find_new(uc); @@ -956,7 +958,7 @@ static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callb uc->hook_in_idx = i; return UC_ERR_OK; } else - return UC_ERR_OOM; + return UC_ERR_NOMEM; case UC_X86_INS_SYSCALL: case UC_X86_INS_SYSENTER: // FIXME: only one event handler at the same time @@ -968,7 +970,7 @@ static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callb uc->hook_syscall_idx = i; return UC_ERR_OK; } else - return UC_ERR_OOM; + return UC_ERR_NOMEM; } break; } From fe51b9a9aee15e524c4576f3d71bad71eca9fb58 Mon Sep 17 00:00:00 2001 From: Sean Heelan Date: Thu, 3 Sep 2015 00:48:20 +0700 Subject: [PATCH 15/23] Regression test for issue #98 This is a problem with sign extension during uc_reg_write --- regress/reg_write_sign_extension.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 regress/reg_write_sign_extension.py diff --git a/regress/reg_write_sign_extension.py b/regress/reg_write_sign_extension.py new file mode 100644 index 00000000..69347c0f --- /dev/null +++ b/regress/reg_write_sign_extension.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +"""See https://github.com/unicorn-engine/unicorn/issues/98""" + +import unicorn +ADDR = 0xffaabbcc + +def hook_mem_invalid(mu, access, address, size, value, user_data): + print ">>> Expected value: 0x%x, actual value: 0x%x" % (ADDR, address) + assert(address == ADDR) + mu.mem_map(address & 0xfffff000, 4 * 1024) + mu.mem_write(address, b'\xcc') + return True + +mu = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32) +mu.reg_write(unicorn.x86_const.UC_X86_REG_EBX, ADDR) + +mu.mem_map(0x10000000, 1024 * 4) +# jmp ebx +mu.mem_write(0x10000000, b'\xff\xe3') + +mu.hook_add(unicorn.UC_HOOK_MEM_INVALID, hook_mem_invalid) +mu.emu_start(0x10000000, 0x10000000 + 2, count=1) From f230de876ead025835009a371c23454fc0241e70 Mon Sep 17 00:00:00 2001 From: Ryan Hileman Date: Wed, 2 Sep 2015 19:07:11 -0700 Subject: [PATCH 16/23] fix go binding hook arguments --- bindings/go/unicorn/hook.c | 8 ++++++-- bindings/go/unicorn/hook.go | 25 ++++++++++++++++++++----- bindings/go/unicorn/hook.h | 3 ++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/bindings/go/unicorn/hook.c b/bindings/go/unicorn/hook.c index a06f9901..f24dd1f1 100644 --- a/bindings/go/unicorn/hook.c +++ b/bindings/go/unicorn/hook.c @@ -1,8 +1,12 @@ #include #include "_cgo_export.h" -uc_err uc_hook_add2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, int extra) { - return uc_hook_add(handle, h2, type, callback, user_data, extra); +uc_err uc_hook_add_i1(uch handle, uch *h2, uc_hook_t type, void *callback, void *user, int arg1) { + return uc_hook_add(handle, h2, type, callback, user, arg1); +} + +uc_err uc_hook_add_u2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user, uint64_t arg1, uint64_t arg2) { + return uc_hook_add(handle, h2, type, callback, user, arg1, arg2); } void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user) { diff --git a/bindings/go/unicorn/hook.go b/bindings/go/unicorn/hook.go index dcab893a..e6e01617 100644 --- a/bindings/go/unicorn/hook.go +++ b/bindings/go/unicorn/hook.go @@ -60,21 +60,26 @@ func hookX86Syscall(handle C.uch, user unsafe.Pointer) { var hookRetain = make(map[C.uch]*HookData) -func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) { +func (u *Uc) HookAdd(htype int, cb interface{}, extra ...uint64) (C.uch, error) { var callback unsafe.Pointer - var extra C.int + var iarg1 C.int + var uarg1, uarg2 C.uint64_t + rangeMode := false switch htype { case UC_HOOK_BLOCK, UC_HOOK_CODE: + rangeMode = true callback = C.hookCode_cgo case UC_HOOK_MEM_INVALID: + rangeMode = true callback = C.hookMemInvalid_cgo case UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ_WRITE: + rangeMode = true callback = C.hookMemAccess_cgo case UC_HOOK_INTR: callback = C.hookInterrupt_cgo case UC_HOOK_INSN: - extra = C.int(insn[0]) - switch extra { + iarg1 = C.int(extra[0]) + switch iarg1 { case UC_X86_INS_IN: callback = C.hookX86In_cgo case UC_X86_INS_OUT: @@ -89,7 +94,17 @@ func (u *Uc) HookAdd(htype int, cb interface{}, insn ...int) (C.uch, error) { } var h2 C.uch data := &HookData{u, cb} - C.uc_hook_add2(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(data), extra) + if rangeMode { + if len(extra) == 2 { + uarg1 = C.uint64_t(extra[0]) + uarg2 = C.uint64_t(extra[1]) + } else { + uarg1, uarg2 = 1, 0 + } + C.uc_hook_add_u2(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(data), uarg1, uarg2) + } else { + C.uc_hook_add_i1(u.Handle, &h2, C.uc_hook_t(htype), callback, unsafe.Pointer(data), iarg1) + } hookRetain[h2] = data return h2, nil } diff --git a/bindings/go/unicorn/hook.h b/bindings/go/unicorn/hook.h index c8c22267..b35fca27 100644 --- a/bindings/go/unicorn/hook.h +++ b/bindings/go/unicorn/hook.h @@ -1,4 +1,5 @@ -uc_err uc_hook_add2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, int extra); +uc_err uc_hook_add_i1(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, int arg1); +uc_err uc_hook_add_u2(uch handle, uch *h2, uc_hook_t type, void *callback, void *user_data, uint64_t arg1, uint64_t arg2); void hookCode_cgo(uch handle, uint64_t addr, uint32_t size, void *user); bool hookMemInvalid_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user); void hookMemAccess_cgo(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user); From 46cc510dfe95c87c63cfdad222b4121e813bdf80 Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Thu, 3 Sep 2015 18:02:25 +0800 Subject: [PATCH 17/23] chmod +x regress/reg_write_sign_extension.py --- regress/reg_write_sign_extension.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 regress/reg_write_sign_extension.py diff --git a/regress/reg_write_sign_extension.py b/regress/reg_write_sign_extension.py old mode 100644 new mode 100755 From 9f9d57e84f72e871dc96521c8d263f7474a9dc6e Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Thu, 3 Sep 2015 18:16:49 +0800 Subject: [PATCH 18/23] cleaning & indentation --- qemu/unicorn_common.h | 2 +- regress/ro_mem_test.c | 2 +- samples/mem_exec.c | 423 +++++++++++++++++++-------------------- samples/mem_protect.c | 446 ++++++++++++++++++++---------------------- samples/mem_unmap.c | 433 +++++++++++++++++++--------------------- uc.c | 21 +- 6 files changed, 633 insertions(+), 694 deletions(-) diff --git a/qemu/unicorn_common.h b/qemu/unicorn_common.h index 5ba74fac..adfb5f05 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -77,7 +77,7 @@ static inline void uc_common_init(struct uc_struct* uc) uc->readonly_mem = memory_region_set_readonly; uc->target_page_size = TARGET_PAGE_SIZE; - uc->target_page_align = TARGET_PAGE_SIZE - 1; + uc->target_page_align = TARGET_PAGE_SIZE - 1; if (!uc->release) uc->release = release_common; diff --git a/regress/ro_mem_test.c b/regress/ro_mem_test.c index 52534cd4..38fd913b 100644 --- a/regress/ro_mem_test.c +++ b/regress/ro_mem_test.c @@ -160,7 +160,7 @@ int main(int argc, char **argv, char **envp) uint32_t eax = 0x40002C; uc_reg_write(handle, UC_X86_REG_EAX, &eax); //resume execution at the mov dword [eax], 0x87654321 - //to test an aligned write as well + //to test an aligned write as well 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", diff --git a/samples/mem_exec.c b/samples/mem_exec.c index 171022a7..370fd1ea 100644 --- a/samples/mem_exec.c +++ b/samples/mem_exec.c @@ -1,23 +1,22 @@ /* + Executable memory regions demo / unit test -Executable memory regions demo / unit test + Copyright(c) 2015 Chris Eagle -Copyright(c) 2015 Chris Eagle + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -version 2 as published by the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -*/ + */ #define __STDC_FORMAT_MACROS #include @@ -30,47 +29,47 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include unsigned char PROGRAM[] = - "\xeb\x45\x5e\x81\xe6\x00\xf0\xff\xff\x40\x40\x40\x40\x40\x40\x40" - "\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40" - "\x40\x40\x40\x40\x40\x40\x40\x89\xf7\x81\xc7\x00\x00\x10\x00\xb9" - "\x4c\x00\x00\x00\x81\xff\x00\x00\x40\x00\x75\x01\xf4\xf3\xa4\x81" - "\xe7\x00\xf0\xff\xff\xff\xe7\xe8\xb6\xff\xff\xff"; -// total size: 76 bytes + "\xeb\x45\x5e\x81\xe6\x00\xf0\xff\xff\x40\x40\x40\x40\x40\x40\x40" + "\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40" + "\x40\x40\x40\x40\x40\x40\x40\x89\xf7\x81\xc7\x00\x00\x10\x00\xb9" + "\x4c\x00\x00\x00\x81\xff\x00\x00\x40\x00\x75\x01\xf4\xf3\xa4\x81" + "\xe7\x00\xf0\xff\xff\xff\xe7\xe8\xb6\xff\xff\xff"; + // total size: 76 bytes /* -bits 32 + bits 32 -; assumes r-x section at 0x100000 -; assumes rw- section at 0x200000 -; assumes r-- section at 0x300000 -; also needs an initialized stack + ; assumes r-x section at 0x100000 + ; assumes rw- section at 0x200000 + ; assumes r-- section at 0x300000 + ; also needs an initialized stack start: - jmp bottom +jmp bottom top: - pop esi - and esi, ~0xfff - times 30 inc eax - mov edi, esi - add edi, 0x100000 - mov ecx, end - start - rep movsb - and edi, ~0xfff - cmp edi, 0x400000 - jnz next_block - hlt +pop esi +and esi, ~0xfff +times 30 inc eax +mov edi, esi +add edi, 0x100000 +mov ecx, end - start +rep movsb +and edi, ~0xfff +cmp edi, 0x400000 +jnz next_block +hlt next_block: - jmp edi +jmp edi bottom: - call top +call top end: -*/ + */ int test_num = 0; uint32_t tests[] = { - 0x41414141, - 0x43434343, - 0x45454545 + 0x41414141, + 0x43434343, + 0x45454545 }; static int log_num = 1; @@ -81,216 +80,200 @@ static int log_num = 1; // callback for tracing instruction static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) { - uint8_t opcode; - if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { - printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - } -// printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); - switch (opcode) { - case 0xf4: //hlt - printf("# Handling HLT\n"); - if (uc_emu_stop(handle) != UC_ERR_OK) { - printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - _exit(-1); - } - else { - printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); - } - break; - default: //all others -// printf("# Handling OTHER\n"); - break; - } + uint8_t opcode; + + if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + + // printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); + switch (opcode) { + case 0xf4: //hlt + printf("# Handling HLT\n"); + if (uc_emu_stop(handle) != UC_ERR_OK) { + printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + _exit(-1); + } else { + printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); + } + break; + default: //all others + // printf("# Handling OTHER\n"); + break; + } } // callback for tracing memory access (READ or WRITE) static void hook_mem_write(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data) { - printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); } // callback for tracing invalid memory access (READ or WRITE) static bool hook_mem_invalid(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data) { - switch(type) { - default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); - return false; - case UC_MEM_EXEC_PROT: - printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); + switch(type) { + default: + printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + return false; + case UC_MEM_EXEC_PROT: + printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); - //make page executable - if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_EXEC) != UC_ERR_OK) { - printf("not ok %d - uc_mem_protect fail for address: 0x%" PRIx64 "\n", log_num++, addr); - } - else { - printf("ok %d - uc_mem_protect success at 0x%" PRIx64 "\n", log_num++, addr); - } - return true; - case UC_MEM_WRITE_PROT: - printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + //make page executable + if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_EXEC) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail for address: 0x%" PRIx64 "\n", log_num++, addr); + } else { + printf("ok %d - uc_mem_protect success at 0x%" PRIx64 "\n", log_num++, addr); + } + return true; + case UC_MEM_WRITE_PROT: + printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); - if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { - printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - } - else { - printf("ok %d - uc_mem_protect success\n", log_num++); - } - return true; - } + if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } else { + printf("ok %d - uc_mem_protect success\n", log_num++); + } + return true; + } } int main(int argc, char **argv, char **envp) { - uch handle, trace1, trace2; - uc_err err; - uint32_t esp, eip; - int32_t buf1[1024], buf2[1024], readbuf[1024]; - int i; - - //don't really care about quality of randomness - srand(time(NULL)); - for (i = 0; i < 1024; i++) { - buf1[i] = rand(); - buf2[i] = rand(); - } + uch handle, trace1, trace2; + uc_err err; + uint32_t esp, eip; + int32_t buf1[1024], buf2[1024], readbuf[1024]; + int i; - printf("# Memory protect test\n"); + //don't really care about quality of randomness + srand(time(NULL)); + for (i = 0; i < 1024; i++) { + buf1[i] = rand(); + buf2[i] = rand(); + } - // Initialize emulator in X86-32bit mode - err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); - if (err) { - printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); - return 1; - } - else { - printf("ok %d - uc_open() success\n", log_num++); - } + printf("# Memory protect test\n"); - uc_mem_map(handle, 0x100000, 0x1000, UC_PROT_READ | UC_PROT_EXEC); - uc_mem_map(handle, 0x1ff000, 0x2000, UC_PROT_READ | UC_PROT_WRITE); - uc_mem_map(handle, 0x300000, 0x2000, UC_PROT_READ); - uc_mem_map(handle, 0xf00000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); + if (err) { + printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); + return 1; + } else { + printf("ok %d - uc_open() success\n", log_num++); + } - esp = 0xf00000 + 0x1000; + uc_mem_map(handle, 0x100000, 0x1000, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(handle, 0x1ff000, 0x2000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x300000, 0x2000, UC_PROT_READ); + uc_mem_map(handle, 0xf00000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); - // Setup stack pointer - if (uc_reg_write(handle, UC_X86_REG_ESP, &esp)) { - printf("not ok %d - Failed to set esp. quit!\n", log_num++); - return 2; - } - else { - printf("ok %d - ESP set\n", log_num++); - } + esp = 0xf00000 + 0x1000; - // fill in sections that shouldn't get touched - if (uc_mem_write(handle, 0x1ff000, (uint8_t*)buf1, 4096)) { - printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); - return 3; - } - else { - printf("ok %d - Random buffer 1 written to memory\n", log_num++); - } + // Setup stack pointer + if (uc_reg_write(handle, UC_X86_REG_ESP, &esp)) { + printf("not ok %d - Failed to set esp. quit!\n", log_num++); + return 2; + } else { + printf("ok %d - ESP set\n", log_num++); + } - if (uc_mem_write(handle, 0x301000, (uint8_t*)buf2, 4096)) { - printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); - return 4; - } - else { - printf("ok %d - Random buffer 2 written to memory\n", log_num++); - } + // fill in sections that shouldn't get touched + if (uc_mem_write(handle, 0x1ff000, (uint8_t*)buf1, 4096)) { + printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); + return 3; + } else { + printf("ok %d - Random buffer 1 written to memory\n", log_num++); + } - // write machine code to be emulated to memory - if (uc_mem_write(handle, 0x100000, PROGRAM, sizeof(PROGRAM))) { - printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); - return 5; - } - else { - printf("ok %d - Program written to memory\n", log_num++); - } + if (uc_mem_write(handle, 0x301000, (uint8_t*)buf2, 4096)) { + printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); + return 4; + } else { + printf("ok %d - Random buffer 2 written to memory\n", log_num++); + } - if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); - return 6; - } - else { - printf("ok %d - UC_HOOK_CODE installed\n", log_num++); - } + // write machine code to be emulated to memory + if (uc_mem_write(handle, 0x100000, PROGRAM, sizeof(PROGRAM))) { + printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); + return 5; + } else { + printf("ok %d - Program written to memory\n", log_num++); + } - // intercept memory write events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); - return 7; - } - else { - printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); - } + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); + return 6; + } else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } - // intercept invalid memory events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); - return 8; - } - else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); - } + // intercept memory write events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); + return 7; + } else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } - // emulate machine code until told to stop by hook_code - printf("# BEGIN execution\n"); - err = uc_emu_start(handle, 0x100000, 0x400000, 0, 0); - if (err != UC_ERR_OK) { - printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); - return 9; - } - else { - printf("ok %d - uc_emu_start complete\n", log_num++); - } - printf("# END execution\n"); + // intercept invalid memory events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); + return 8; + } else { + printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + } - // get ending EIP - if (uc_reg_read(handle, UC_X86_REG_EIP, &eip)) { - printf("not ok %d - Failed to read eip.\n", log_num++); - } - else { - printf("ok %d - Ending EIP 0x%x\n", log_num++, eip); - } + // emulate machine code until told to stop by hook_code + printf("# BEGIN execution\n"); + err = uc_emu_start(handle, 0x100000, 0x400000, 0, 0); + if (err != UC_ERR_OK) { + printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); + return 9; + } else { + printf("ok %d - uc_emu_start complete\n", log_num++); + } + printf("# END execution\n"); - //make sure that random blocks didn't get nuked - // fill in sections that shouldn't get touched - if (uc_mem_read(handle, 0x1ff000, (uint8_t*)readbuf, 4096)) { - printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); - } - else { - printf("ok %d - Random buffer 1 read from memory\n", log_num++); - if (memcmp(buf1, readbuf, 4096)) { - printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); - } - else { - printf("ok %d - Random buffer 1 contents are correct\n", log_num++); - } - } + // get ending EIP + if (uc_reg_read(handle, UC_X86_REG_EIP, &eip)) { + printf("not ok %d - Failed to read eip.\n", log_num++); + } else { + printf("ok %d - Ending EIP 0x%x\n", log_num++, eip); + } - if (uc_mem_read(handle, 0x301000, (uint8_t*)readbuf, 4096)) { - printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); - } - else { - printf("ok %d - Random buffer 2 read from memory\n", log_num++); - if (memcmp(buf2, readbuf, 4096)) { - printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); - } - else { - printf("ok %d - Random buffer 2 contents are correct\n", log_num++); - } - } + //make sure that random blocks didn't get nuked + // fill in sections that shouldn't get touched + if (uc_mem_read(handle, 0x1ff000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); + } else { + printf("ok %d - Random buffer 1 read from memory\n", log_num++); + if (memcmp(buf1, readbuf, 4096)) { + printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); + } else { + printf("ok %d - Random buffer 1 contents are correct\n", log_num++); + } + } - if (uc_close(&handle) == UC_ERR_OK) { - printf("ok %d - uc_close complete\n", log_num++); - } - else { - printf("not ok %d - uc_close complete\n", log_num++); - } + if (uc_mem_read(handle, 0x301000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); + } else { + printf("ok %d - Random buffer 2 read from memory\n", log_num++); + if (memcmp(buf2, readbuf, 4096)) { + printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); + } else { + printf("ok %d - Random buffer 2 contents are correct\n", log_num++); + } + } - return 0; + if (uc_close(&handle) == UC_ERR_OK) { + printf("ok %d - uc_close complete\n", log_num++); + } else { + printf("not ok %d - uc_close complete\n", log_num++); + } + + return 0; } diff --git a/samples/mem_protect.c b/samples/mem_protect.c index 5d852ff5..5739563e 100644 --- a/samples/mem_protect.c +++ b/samples/mem_protect.c @@ -1,23 +1,22 @@ /* + uc_mem_protect demo / unit test -uc_mem_protect demo / unit test + Copyright(c) 2015 Chris Eagle -Copyright(c) 2015 Chris Eagle + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -version 2 as published by the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -*/ + */ #define __STDC_FORMAT_MACROS #include @@ -30,23 +29,23 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include unsigned char PROGRAM[] = - "\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20" - "\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90" - "\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00" - "\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xc7" - "\x05\x00\xf8\x3f\x00\x47\x47\x47\x47\xc7\x05\x00\x18\x40\x00\x48" - "\x48\x48\x48\xf4"; -// total size: 84 bytes + "\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20" + "\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90" + "\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00" + "\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xc7" + "\x05\x00\xf8\x3f\x00\x47\x47\x47\x47\xc7\x05\x00\x18\x40\x00\x48" + "\x48\x48\x48\xf4"; + // total size: 84 bytes /* -bits 32 + bits 32 -; assumes code section at 0x100000 -; assumes data section at 0x200000, initially rw -; assumes data section at 0x300000, initially rw -; assumes data section at 0x400000, initially rw + ; assumes code section at 0x100000 + ; assumes data section at 0x200000, initially rw + ; assumes data section at 0x300000, initially rw + ; assumes data section at 0x400000, initially rw -; with installed hooks unmaps or maps on each nop + ; with installed hooks unmaps or maps on each nop mov dword [0x200000], 0x41414141 nop ; mark it RO @@ -63,13 +62,13 @@ bits 32 mov dword [0x401800], 0x48484848 ; make sure surrounding areas remained RW hlt ; tell hook function we are done -*/ + */ int test_num = 0; uint32_t tests[] = { - 0x41414141, - 0x43434343, - 0x45454545 + 0x41414141, + 0x43434343, + 0x45454545 }; static int log_num = 1; @@ -80,244 +79,223 @@ static int log_num = 1; // callback for tracing instruction static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) { - uint8_t opcode; - uint32_t testval; - if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { - printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - } - printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); - switch (opcode) { - case 0x90: //nop - printf("# Handling NOP\n"); - if (uc_mem_read(handle, 0x200000 + test_num * 0x100000, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { - printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); - } - else { - printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); - printf("# uc_mem_read for test %d\n", test_num); + uint8_t opcode; + uint32_t testval; + if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); + switch (opcode) { + case 0x90: //nop + printf("# Handling NOP\n"); + if (uc_mem_read(handle, 0x200000 + test_num * 0x100000, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } else { + printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + printf("# uc_mem_read for test %d\n", test_num); - if (testval == tests[test_num]) { - printf("ok %d - passed test %d\n", log_num++, test_num); + if (testval == tests[test_num]) { + printf("ok %d - passed test %d\n", log_num++, test_num); + } else { + printf("not ok %d - failed test %d\n", log_num++, test_num); + printf("# Expected: 0x%x\n",tests[test_num]); + printf("# Received: 0x%x\n", testval); + } } - else { - printf("not ok %d - failed test %d\n", log_num++, test_num); - printf("# Expected: 0x%x\n",tests[test_num]); - printf("# Received: 0x%x\n", testval); + if (uc_mem_protect(handle, 0x200000 + test_num * 0x100000, 0x1000, UC_PROT_READ) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } else { + printf("ok %d - uc_mem_protect success\n", log_num++); } - } - if (uc_mem_protect(handle, 0x200000 + test_num * 0x100000, 0x1000, UC_PROT_READ) != UC_ERR_OK) { - printf("not ok %d - uc_mem_protect fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); - } - else { - printf("ok %d - uc_mem_protect success\n", log_num++); - } - test_num++; - break; - case 0xf4: //hlt - printf("# Handling HLT\n"); - if (uc_emu_stop(handle) != UC_ERR_OK) { - printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - _exit(-1); - } - else { - printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); - } - break; - default: //all others - printf("# Handling OTHER\n"); - break; - } + test_num++; + break; + case 0xf4: //hlt + printf("# Handling HLT\n"); + if (uc_emu_stop(handle) != UC_ERR_OK) { + printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + _exit(-1); + } else { + printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); + } + break; + default: //all others + printf("# Handling OTHER\n"); + break; + } } // callback for tracing memory access (READ or WRITE) static void hook_mem_write(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data) { - printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); } // callback for tracing invalid memory access (READ or WRITE) static bool hook_mem_invalid(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data) { - uint32_t testval; - switch(type) { - default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); - return false; - case UC_MEM_WRITE_PROT: - printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + uint32_t testval; + switch(type) { + default: + printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + return false; + case UC_MEM_WRITE_PROT: + printf("# write to non-writeable memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); - if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { - printf("not ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr); - } - else { - printf("ok %d - uc_mem_read success after mem_protect at test %d\n", log_num++, test_num - 1); - } + if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr); + } else { + printf("ok %d - uc_mem_read success after mem_protect at test %d\n", log_num++, test_num - 1); + } - if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { - printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - } - else { - printf("ok %d - uc_mem_protect success\n", log_num++); - } - return true; - } + if (uc_mem_protect(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + printf("not ok %d - uc_mem_protect fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } else { + printf("ok %d - uc_mem_protect success\n", log_num++); + } + return true; + } } int main(int argc, char **argv, char **envp) { - uch handle, trace1, trace2; - uc_err err; - uint32_t addr, testval; - int32_t buf1[1024], buf2[1024], readbuf[1024]; - int i; - - //don't really care about quality of randomness - srand(time(NULL)); - for (i = 0; i < 1024; i++) { - buf1[i] = rand(); - buf2[i] = rand(); - } + uch handle, trace1, trace2; + uc_err err; + uint32_t addr, testval; + int32_t buf1[1024], buf2[1024], readbuf[1024]; + int i; - printf("# Memory protect test\n"); + //don't really care about quality of randomness + srand(time(NULL)); + for (i = 0; i < 1024; i++) { + buf1[i] = rand(); + buf2[i] = rand(); + } - // Initialize emulator in X86-32bit mode - err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); - if (err) { - printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); - return 1; - } - else { - printf("ok %d - uc_open() success\n", log_num++); - } + printf("# Memory protect test\n"); - uc_mem_map(handle, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); - uc_mem_map(handle, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); - uc_mem_map(handle, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); - uc_mem_map(handle, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); + if (err) { + printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); + return 1; + } else { + printf("ok %d - uc_open() success\n", log_num++); + } - // fill in sections that shouldn't get touched - if (uc_mem_write(handle, 0x3ff000, (uint8_t*)buf1, 4096)) { - printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); - return 2; - } - else { - printf("ok %d - Random buffer 1 written to memory\n", log_num++); - } + uc_mem_map(handle, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(handle, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); - if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { - printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); - return 3; - } - else { - printf("ok %d - Random buffer 2 written to memory\n", log_num++); - } + // fill in sections that shouldn't get touched + if (uc_mem_write(handle, 0x3ff000, (uint8_t*)buf1, 4096)) { + printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); + return 2; + } else { + printf("ok %d - Random buffer 1 written to memory\n", log_num++); + } - // write machine code to be emulated to memory - if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { - printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); - return 4; - } - else { - printf("ok %d - Program written to memory\n", log_num++); - } + if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { + printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); + return 3; + } else { + printf("ok %d - Random buffer 2 written to memory\n", log_num++); + } - if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); - return 5; - } - else { - printf("ok %d - UC_HOOK_CODE installed\n", log_num++); - } + // write machine code to be emulated to memory + if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { + printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); + return 4; + } else { + printf("ok %d - Program written to memory\n", log_num++); + } - // intercept memory write events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); - return 6; - } - else { - printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); - } + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); + return 5; + } else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } - // intercept invalid memory events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); - return 7; - } - else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); - } + // intercept memory write events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); + return 6; + } else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } - // emulate machine code until told to stop by hook_code - printf("# BEGIN execution\n"); - err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0); - if (err != UC_ERR_OK) { - printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); - return 8; - } - else { - printf("ok %d - uc_emu_start complete\n", log_num++); - } - printf("# END execution\n"); + // intercept invalid memory events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); + return 7; + } else { + printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + } - //read from the remapped memory - testval = 0x42424242; - for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { - uint32_t val; - if (uc_mem_read(handle, addr, (uint8_t*)&val, sizeof(val)) != UC_ERR_OK) { - printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr); - } - else { - printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr); - } - if (val != testval) { - printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval); - } - else { - printf("ok %d - Correct value retrieved\n", log_num++); - } - testval += 0x02020202; - } + // emulate machine code until told to stop by hook_code + printf("# BEGIN execution\n"); + err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0); + if (err != UC_ERR_OK) { + printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); + return 8; + } else { + printf("ok %d - uc_emu_start complete\n", log_num++); + } + printf("# END execution\n"); - //account for the two mods made by the machine code - buf1[512] = 0x47474747; - buf2[512] = 0x48484848; + //read from the remapped memory + testval = 0x42424242; + for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { + uint32_t val; + if (uc_mem_read(handle, addr, (uint8_t*)&val, sizeof(val)) != UC_ERR_OK) { + printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr); + } else { + printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr); + } + if (val != testval) { + printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval); + } else { + printf("ok %d - Correct value retrieved\n", log_num++); + } + testval += 0x02020202; + } - //make sure that random blocks didn't get nuked - // fill in sections that shouldn't get touched - if (uc_mem_read(handle, 0x3ff000, (uint8_t*)readbuf, 4096)) { - printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); - } - else { - printf("ok %d - Random buffer 1 read from memory\n", log_num++); - if (memcmp(buf1, readbuf, 4096)) { - printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); - } - else { - printf("ok %d - Random buffer 1 contents are correct\n", log_num++); - } - } + //account for the two mods made by the machine code + buf1[512] = 0x47474747; + buf2[512] = 0x48484848; - if (uc_mem_read(handle, 0x401000, (uint8_t*)readbuf, 4096)) { - printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); - } - else { - printf("ok %d - Random buffer 2 read from memory\n", log_num++); - if (memcmp(buf2, readbuf, 4096)) { - printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); - } - else { - printf("ok %d - Random buffer 2 contents are correct\n", log_num++); - } - } + //make sure that random blocks didn't get nuked + // fill in sections that shouldn't get touched + if (uc_mem_read(handle, 0x3ff000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); + } else { + printf("ok %d - Random buffer 1 read from memory\n", log_num++); + if (memcmp(buf1, readbuf, 4096)) { + printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); + } else { + printf("ok %d - Random buffer 1 contents are correct\n", log_num++); + } + } - if (uc_close(&handle) == UC_ERR_OK) { - printf("ok %d - uc_close complete\n", log_num++); - } - else { - printf("not ok %d - uc_close complete\n", log_num++); - } + if (uc_mem_read(handle, 0x401000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); + } else { + printf("ok %d - Random buffer 2 read from memory\n", log_num++); + if (memcmp(buf2, readbuf, 4096)) { + printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); + } else { + printf("ok %d - Random buffer 2 contents are correct\n", log_num++); + } + } - return 0; + if (uc_close(&handle) == UC_ERR_OK) { + printf("ok %d - uc_close complete\n", log_num++); + } else { + printf("not ok %d - uc_close complete\n", log_num++); + } + + return 0; } diff --git a/samples/mem_unmap.c b/samples/mem_unmap.c index 6f93673d..b31bf220 100644 --- a/samples/mem_unmap.c +++ b/samples/mem_unmap.c @@ -1,23 +1,23 @@ /* -uc_mem_unmap demo / unit test + uc_mem_unmap demo / unit test -Copyright(c) 2015 Chris Eagle + Copyright(c) 2015 Chris Eagle -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -version 2 as published by the Free Software Foundation. + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + */ #define __STDC_FORMAT_MACROS #include @@ -30,19 +30,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include unsigned char PROGRAM[] = - "\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20" - "\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90" - "\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00" - "\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xf4"; -// total size: 64 bytes + "\xc7\x05\x00\x00\x20\x00\x41\x41\x41\x41\x90\xc7\x05\x00\x00\x20" + "\x00\x42\x42\x42\x42\xc7\x05\x00\x00\x30\x00\x43\x43\x43\x43\x90" + "\xc7\x05\x00\x00\x30\x00\x44\x44\x44\x44\xc7\x05\x00\x00\x40\x00" + "\x45\x45\x45\x45\x90\xc7\x05\x00\x00\x40\x00\x46\x46\x46\x46\xf4"; + // total size: 64 bytes /* -; assumes code section at 0x100000 -; assumes data section at 0x200000, initially rw -; assumes data section at 0x300000, initially rw -; assumes data section at 0x400000, initially rw + ; assumes code section at 0x100000 + ; assumes data section at 0x200000, initially rw + ; assumes data section at 0x300000, initially rw + ; assumes data section at 0x400000, initially rw -; with installed hooks unmaps or maps on each nop + ; with installed hooks unmaps or maps on each nop mov dword [0x200000], 0x41414141 nop ; unmap it @@ -57,13 +57,13 @@ unsigned char PROGRAM[] = mov dword [0x400000], 0x46464646 hlt ; tell hook function we are done -*/ + */ int test_num = 0; uint32_t tests[] = { - 0x41414141, - 0x43434343, - 0x45454545 + 0x41414141, + 0x43434343, + 0x45454545 }; static int log_num = 1; @@ -74,240 +74,219 @@ static int log_num = 1; // callback for tracing instruction static void hook_code(uch handle, uint64_t addr, uint32_t size, void *user_data) { - uint8_t opcode; - uint32_t testval; - if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { - printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - } - printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); - switch (opcode) { - case 0x90: //nop - printf("# Handling NOP\n"); - if (uc_mem_read(handle, 0x200000 + test_num * 0x100000, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { - printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); - } - else { - printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); - printf("# uc_mem_read for test %d\n", test_num); + uint8_t opcode; + uint32_t testval; + if (uc_mem_read(handle, addr, &opcode, 1) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } + printf("ok %d - uc_mem_read for opcode at address 0x%" PRIx64 "\n", log_num++, addr); + switch (opcode) { + case 0x90: //nop + printf("# Handling NOP\n"); + if (uc_mem_read(handle, 0x200000 + test_num * 0x100000, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("not ok %d - uc_mem_read fail for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } else { + printf("ok %d - good uc_mem_read for address: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + printf("# uc_mem_read for test %d\n", test_num); - if (testval == tests[test_num]) { - printf("ok %d - passed test %d\n", log_num++, test_num); + if (testval == tests[test_num]) { + printf("ok %d - passed test %d\n", log_num++, test_num); + } else { + printf("not ok %d - failed test %d\n", log_num++, test_num); + printf("# Expected: 0x%x\n",tests[test_num]); + printf("# Received: 0x%x\n", testval); + } } - else { - printf("not ok %d - failed test %d\n", log_num++, test_num); - printf("# Expected: 0x%x\n",tests[test_num]); - printf("# Received: 0x%x\n", testval); + if (uc_mem_unmap(handle, 0x200000 + test_num * 0x100000, 0x1000) != UC_ERR_OK) { + printf("not ok %d - uc_mem_unmap fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); + } else { + printf("ok %d - uc_mem_unmap success\n", log_num++); } - } - if (uc_mem_unmap(handle, 0x200000 + test_num * 0x100000, 0x1000) != UC_ERR_OK) { - printf("not ok %d - uc_mem_unmap fail during hook_code callback, addr: 0x%x\n", log_num++, 0x200000 + test_num * 0x100000); - } - else { - printf("ok %d - uc_mem_unmap success\n", log_num++); - } - test_num++; - break; - case 0xf4: //hlt - printf("# Handling HLT\n"); - if (uc_emu_stop(handle) != UC_ERR_OK) { - printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - _exit(-1); - } - else { - printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); - } - break; - default: //all others - printf("# Handling OTHER\n"); - break; - } + test_num++; + break; + case 0xf4: //hlt + printf("# Handling HLT\n"); + if (uc_emu_stop(handle) != UC_ERR_OK) { + printf("not ok %d - uc_emu_stop fail during hook_code callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + _exit(-1); + } else { + printf("ok %d - hlt encountered, uc_emu_stop called\n", log_num++); + } + break; + default: //all others + printf("# Handling OTHER\n"); + break; + } } // callback for tracing memory access (READ or WRITE) static void hook_mem_write(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data) { - printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + printf("# write to memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); } // callback for tracing invalid memory access (READ or WRITE) static bool hook_mem_invalid(uch handle, uc_mem_type type, uint64_t addr, int size, int64_t value, void *user_data) { - uint32_t testval; - switch(type) { - default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); - return false; - case UC_MEM_WRITE: - printf("# write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); + uint32_t testval; + switch(type) { + default: + printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + return false; + case UC_MEM_WRITE: + printf("# write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); - if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { - printf("ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr); - } - else { - printf("not ok %d - uc_mem_read success after unmap at test %d\n", log_num++, test_num - 1); - } + if (uc_mem_read(handle, addr, (uint8_t*)&testval, sizeof(testval)) != UC_ERR_OK) { + printf("ok %d - uc_mem_read fail for address: 0x%" PRIx64 "\n", log_num++, addr); + } else { + printf("not ok %d - uc_mem_read success after unmap at test %d\n", log_num++, test_num - 1); + } - if (uc_mem_map(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { - printf("not ok %d - uc_mem_map fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); - } - else { - printf("ok %d - uc_mem_map success\n", log_num++); - } - return true; - } + if (uc_mem_map(handle, addr & ~0xfffL, 0x1000, UC_PROT_READ | UC_PROT_WRITE) != UC_ERR_OK) { + printf("not ok %d - uc_mem_map fail during hook_mem_invalid callback, addr: 0x%" PRIx64 "\n", log_num++, addr); + } else { + printf("ok %d - uc_mem_map success\n", log_num++); + } + return true; + } } int main(int argc, char **argv, char **envp) { - uch handle, trace1, trace2; - uc_err err; - uint32_t addr, testval; - int32_t buf1[1024], buf2[1024], readbuf[1024]; - int i; - - //don't really care about quality of randomness - srand(time(NULL)); - for (i = 0; i < 1024; i++) { - buf1[i] = rand(); - buf2[i] = rand(); - } + uch handle, trace1, trace2; + uc_err err; + uint32_t addr, testval; + int32_t buf1[1024], buf2[1024], readbuf[1024]; + int i; - printf("# Memory unmapping test\n"); + //don't really care about quality of randomness + srand(time(NULL)); + for (i = 0; i < 1024; i++) { + buf1[i] = rand(); + buf2[i] = rand(); + } - // Initialize emulator in X86-32bit mode - err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); - if (err) { - printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); - return 1; - } - else { - printf("ok %d - uc_open() success\n", log_num++); - } + printf("# Memory unmapping test\n"); - uc_mem_map(handle, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); - uc_mem_map(handle, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); - uc_mem_map(handle, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); - uc_mem_map(handle, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); + // Initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &handle); + if (err) { + printf("not ok %d - Failed on uc_open() with error returned: %u\n", log_num++, err); + return 1; + } else { + printf("ok %d - uc_open() success\n", log_num++); + } - // fill in sections that shouldn't get touched - if (uc_mem_write(handle, 0x3ff000, (uint8_t*)buf1, 4096)) { - printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); - return 2; - } - else { - printf("ok %d - Random buffer 1 written to memory\n", log_num++); - } + uc_mem_map(handle, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(handle, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(handle, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); - if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { - printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); - return 3; - } - else { - printf("ok %d - Random buffer 2 written to memory\n", log_num++); - } + // fill in sections that shouldn't get touched + if (uc_mem_write(handle, 0x3ff000, (uint8_t*)buf1, 4096)) { + printf("not ok %d - Failed to write random buffer 1 to memory, quit!\n", log_num++); + return 2; + } else { + printf("ok %d - Random buffer 1 written to memory\n", log_num++); + } - // write machine code to be emulated to memory - if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { - printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); - return 4; - } - else { - printf("ok %d - Program written to memory\n", log_num++); - } + if (uc_mem_write(handle, 0x401000, (uint8_t*)buf2, 4096)) { + printf("not ok %d - Failed to write random buffer 2 to memory, quit!\n", log_num++); + return 3; + } else { + printf("ok %d - Random buffer 2 written to memory\n", log_num++); + } - if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); - return 5; - } - else { - printf("ok %d - UC_HOOK_CODE installed\n", log_num++); - } + // write machine code to be emulated to memory + if (uc_mem_write(handle, CODE_SECTION, PROGRAM, sizeof(PROGRAM))) { + printf("not ok %d - Failed to write emulation code to memory, quit!\n", log_num++); + return 4; + } else { + printf("ok %d - Program written to memory\n", log_num++); + } - // intercept memory write events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); - return 6; - } - else { - printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); - } + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); + return 5; + } else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } - // intercept invalid memory events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { - printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); - return 7; - } - else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); - } + // intercept memory write events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); + return 6; + } else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } - // emulate machine code until told to stop by hook_code - printf("# BEGIN execution\n"); - err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0); - if (err != UC_ERR_OK) { - printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); - return 8; - } - else { - printf("ok %d - uc_emu_start complete\n", log_num++); - } - printf("# END execution\n"); + // intercept invalid memory events + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID handler\n", log_num++); + return 7; + } else { + printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + } - //read from the remapped memory - testval = 0x42424242; - for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { - uint32_t val; - if (uc_mem_read(handle, addr, (uint8_t*)&val, sizeof(val)) != UC_ERR_OK) { - printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr); - } - else { - printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr); - } - if (val != testval) { - printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval); - } - else { - printf("ok %d - Correct value retrieved\n", log_num++); - } - testval += 0x02020202; - } + // emulate machine code until told to stop by hook_code + printf("# BEGIN execution\n"); + err = uc_emu_start(handle, CODE_SECTION, CODE_SECTION + CODE_SIZE, 0, 0); + if (err != UC_ERR_OK) { + printf("not ok %d - Failure on uc_emu_start() with error %u:%s\n", log_num++, err, uc_strerror(err)); + return 8; + } else { + printf("ok %d - uc_emu_start complete\n", log_num++); + } + printf("# END execution\n"); - //make sure that random blocks didn't get nuked - // fill in sections that shouldn't get touched - if (uc_mem_read(handle, 0x3ff000, (uint8_t*)readbuf, 4096)) { - printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); - } - else { - printf("ok %d - Random buffer 1 read from memory\n", log_num++); - if (memcmp(buf1, readbuf, 4096)) { - printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); - } - else { - printf("ok %d - Random buffer 1 contents are correct\n", log_num++); - } - } + //read from the remapped memory + testval = 0x42424242; + for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { + uint32_t val; + if (uc_mem_read(handle, addr, (uint8_t*)&val, sizeof(val)) != UC_ERR_OK) { + printf("not ok %d - Failed uc_mem_read for address 0x%x\n", log_num++, addr); + } else { + printf("ok %d - Good uc_mem_read from 0x%x\n", log_num++, addr); + } + if (val != testval) { + printf("not ok %d - Read 0x%x, expected 0x%x\n", log_num++, val, testval); + } else { + printf("ok %d - Correct value retrieved\n", log_num++); + } + testval += 0x02020202; + } - if (uc_mem_read(handle, 0x401000, (uint8_t*)readbuf, 4096)) { - printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); - } - else { - printf("ok %d - Random buffer 2 read from memory\n", log_num++); - if (memcmp(buf2, readbuf, 4096)) { - printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); - } - else { - printf("ok %d - Random buffer 2 contents are correct\n", log_num++); - } - } + //make sure that random blocks didn't get nuked + // fill in sections that shouldn't get touched + if (uc_mem_read(handle, 0x3ff000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 1 from memory\n", log_num++); + } else { + printf("ok %d - Random buffer 1 read from memory\n", log_num++); + if (memcmp(buf1, readbuf, 4096)) { + printf("not ok %d - Random buffer 1 contents are incorrect\n", log_num++); + } else { + printf("ok %d - Random buffer 1 contents are correct\n", log_num++); + } + } - if (uc_close(&handle) == UC_ERR_OK) { - printf("ok %d - uc_close complete\n", log_num++); - } - else { - printf("not ok %d - uc_close complete\n", log_num++); - } + if (uc_mem_read(handle, 0x401000, (uint8_t*)readbuf, 4096)) { + printf("not ok %d - Failed to read random buffer 2 from memory\n", log_num++); + } else { + printf("ok %d - Random buffer 2 read from memory\n", log_num++); + if (memcmp(buf2, readbuf, 4096)) { + printf("not ok %d - Random buffer 2 contents are incorrect\n", log_num++); + } else { + printf("ok %d - Random buffer 2 contents are correct\n", log_num++); + } + } - return 0; + if (uc_close(&handle) == UC_ERR_OK) { + printf("ok %d - uc_close complete\n", log_num++); + } else { + printf("not ok %d - uc_close complete\n", log_num++); + } + + return 0; } diff --git a/uc.c b/uc.c index 622de423..3db40db3 100644 --- a/uc.c +++ b/uc.c @@ -670,14 +670,14 @@ static uint8_t *copy_region(uch handle, MemoryRegion *mr) } /* -Split the given MemoryRegion at the indicated address for the indicated size -this may result in the create of up to 3 spanning sections. If the delete -parameter is true, the no new section will be created to replace the indicate -range. This functions exists to support uc_mem_protect and uc_mem_unmap. + Split the given MemoryRegion at the indicated address for the indicated size + this may result in the create of up to 3 spanning sections. If the delete + parameter is true, the no new section will be created to replace the indicate + range. This functions exists to support uc_mem_protect and uc_mem_unmap. -This is a static function and callers have already done some preliminary -parameter validation. -*/ + This is a static function and callers have already done some preliminary + parameter validation. + */ //TODO: investigate whether qemu region manipulation functions already offer this capability static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete) { @@ -709,7 +709,7 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t perms = mr->perms; begin = mr->addr; end = mr->end; - + if (uc_mem_unmap(handle, mr->addr, int128_get64(mr->size)) != UC_ERR_OK) goto error; @@ -784,7 +784,7 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) // check for only valid permissions if ((perms & ~UC_PROT_ALL) != 0) return UC_ERR_INVAL; - + //check that user's entire requested block is mapped if (!check_mem_area(uc, address, size)) return UC_ERR_NOMEM; @@ -860,8 +860,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) break; } } - } - else { + } else { //ouch, we are going to need to subdivide blocks size_t count = 0, len; while(count < size) { From 8a6fe6dc9d8791f166e404c992b974849d30ce0f Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Thu, 3 Sep 2015 18:43:29 +0800 Subject: [PATCH 19/23] update .gitignore --- .gitignore | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index fade14b3..f4d49d9e 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,16 @@ shellcode.static sample_m68k sample_m68k.exe sample_m68k.static +mem_exec +mem_exec.exe +mem_exec.static +mem_protect +mem_protect.exe +mem_protect.static +mem_unmap +mem_unmap.exe +mem_unmap.static + libunicorn*.dll libunicorn*.so From 6ca85a72ed6d131b40d22dac1c2ff69d41158a98 Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Fri, 4 Sep 2015 01:02:38 +0800 Subject: [PATCH 20/23] simplify uc_mem_protect() & uc_mem_unmap() --- include/unicorn/unicorn.h | 39 +++++++------ qemu/softmmu_template.h | 9 +-- uc.c | 115 ++++++++++++++++++++------------------ 3 files changed, 84 insertions(+), 79 deletions(-) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index cbac0506..e9136f14 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -401,8 +401,7 @@ typedef enum uc_prot { /* Map memory in for 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 | UC_PROT_EXEC. + 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. @@ -413,12 +412,28 @@ typedef enum uc_prot { This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, or this will return with UC_ERR_INVAL error. - @return UC_ERR_OK on success, UC_ERR_NOMEM if no memory is available to satisfy the - request, or other value on failure (refer to uc_err enum for detailed 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(uch handle, uint64_t address, size_t size, uint32_t perms); +/* + Unmap a region of emulation memory. + This API deletes a memory mapping from the emulation memory space. + + @handle: handle returned by uc_open() + @address: starting address of the memory region to be unmapped. + This address must be aligned to 4KB, or this will return with UC_ERR_INVAL error. + @size: size of the memory region to be modified. + This size must be multiple of 4KB, or this will return with UC_ERR_INVAL 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_unmap(uch handle, uint64_t address, size_t size); + /* Set memory permissions for emulation memory. This API changes permissions on an existing memory region. @@ -439,22 +454,6 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms); UNICORN_EXPORT uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms); -/* - Unmap a region of emulation memory. - This API deletes a memory mapping from the emulation memory space. - - @handle: handle returned by uc_open() - @address: starting address of the memory region to be unmapped. - This address must be aligned to 4KB, or this will return with UC_ERR_INVAL error. - @size: size of the memory region to be modified. - This size must be multiple of 4KB, or this will return with UC_ERR_INVAL 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_unmap(uch handle, uint64_t address, size_t size); - #ifdef __cplusplus } #endif diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 3695c64e..de169bee 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -188,8 +188,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, (uch)uc, UC_MEM_EXEC_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; - } - else { + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_EXEC_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); @@ -347,8 +346,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, (uch)uc, UC_MEM_EXEC_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; - } - else { + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_EXEC_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); @@ -389,8 +387,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, (uch)uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; - } - else { + } else { env->invalid_addr = addr; env->invalid_error = UC_ERR_READ_PROT; // printf("***** Invalid memory read (non-readable) at " TARGET_FMT_lx "\n", addr); diff --git a/uc.c b/uc.c index 3db40db3..099af362 100644 --- a/uc.c +++ b/uc.c @@ -32,7 +32,7 @@ #include "qemu/include/hw/boards.h" static uint8_t *copy_region(uch uc, MemoryRegion *mr); -static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete); +static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size); UNICORN_EXPORT unsigned int uc_version(unsigned int *major, unsigned int *minor) @@ -654,8 +654,8 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_OK; } -//create a backup copy of the indicated MemoryRegion -//generally used in prepartion for splitting a MemoryRegion +// Create a backup copy of the indicated MemoryRegion. +// Generally used in prepartion for splitting a MemoryRegion. static uint8_t *copy_region(uch handle, MemoryRegion *mr) { uint8_t *block = (uint8_t *)malloc(int128_get64(mr->size)); @@ -666,6 +666,7 @@ static uint8_t *copy_region(uch handle, MemoryRegion *mr) block = NULL; } } + return block; } @@ -678,18 +679,18 @@ static uint8_t *copy_region(uch handle, MemoryRegion *mr) This is a static function and callers have already done some preliminary parameter validation. */ -//TODO: investigate whether qemu region manipulation functions already offer this capability -static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete) +// TODO: investigate whether qemu region manipulation functions already offered +// this capability +static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, + size_t size) { uint8_t *backup; uint32_t perms; uint64_t begin, end, chunk_end; size_t l_size, m_size, r_size; + chunk_end = address + size; if (address <= mr->addr && chunk_end >= mr->end) { - //trivial case, if we are deleting, just unmap - if (do_delete) - return uc_mem_unmap(handle, mr->addr, int128_get64(mr->size)) == UC_ERR_OK; return true; } @@ -731,17 +732,17 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t r_size = (size_t)(end - chunk_end); m_size = (size_t)(chunk_end - address); - //If there are error in any of the below operations, things are too far gone - //at that point to recover. Could try to remap orignal region, but these smaller - //allocation just failed so no guarantee that we can recover the original - //allocation at this point + // If there are error in any of the below operations, things are too far gone + // at that point to recover. Could try to remap orignal region, but these smaller + // allocation just failed so no guarantee that we can recover the original + // allocation at this point if (l_size > 0) { if (uc_mem_map(handle, begin, l_size, perms) != UC_ERR_OK) goto error; if (uc_mem_write(handle, begin, backup, l_size) != UC_ERR_OK) goto error; } - if (m_size > 0 && !do_delete) { + if (m_size > 0) { if (uc_mem_map(handle, address, m_size, perms) != UC_ERR_OK) goto error; if (uc_mem_write(handle, address, backup + l_size, m_size) != UC_ERR_OK) @@ -764,6 +765,8 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) { struct uc_struct* uc = (struct uc_struct *)handle; MemoryRegion *mr; + uint64_t addr = address; + size_t count, len; if (handle == 0) // invalid handle @@ -789,31 +792,30 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) if (!check_mem_area(uc, address, size)) return UC_ERR_NOMEM; - //Now we know entire region is mapped, so change permissions - //If request exactly matches a region we don't need to split - mr = memory_mapping(uc, address); - if (address != mr->addr || size != int128_get64(mr->size)) { - //ouch, we are going to need to subdivide blocks - uint64_t addr = address; - size_t count = 0, len; - while(count < size) { - MemoryRegion *mr = memory_mapping(uc, addr); - len = MIN(size - count, mr->end - addr); - if (!split_region(handle, mr, addr, len, false)) - return UC_ERR_NOMEM; - count += len; - addr += len; - } - //Grab a pointer to the newly split MemoryRegion - mr = memory_mapping(uc, address); - if (mr == NULL) { - //this should never happern if splitting succeeded + // Now we know entire region is mapped, so change permissions + // We may need to split regions if this area spans adjacent regions + addr = address; + count = 0; + while(count < size) { + mr = memory_mapping(uc, addr); + len = MIN(size - count, mr->end - addr); + if (!split_region(handle, mr, addr, len)) return UC_ERR_NOMEM; - } + count += len; + addr += len; + } + + // Now iterate all the regions to set permission + addr = address; + count = 0; + while(count < size) { + mr = memory_mapping(uc, addr); + len = MIN(size - count, mr->end - addr); + mr->perms = perms; + uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + count += len; + addr += len; } - //regions exactly matches an existing region just change perms - mr->perms = perms; - uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); return UC_ERR_OK; } @@ -824,6 +826,8 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) MemoryRegion *mr; unsigned int i; struct uc_struct* uc = (struct uc_struct *)handle; + uint64_t addr; + size_t count, len; if (handle == 0) // invalid handle @@ -845,12 +849,25 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) if (!check_mem_area(uc, address, size)) return UC_ERR_NOMEM; - //Now we know entire region is mapped, so begin the delete - //check trivial case first - mr = memory_mapping(uc, address); - if (address == mr->addr && size == int128_get64(mr->size)) { - //regions exactly matches an existing region just unmap it - //this termiantes a possible recursion between this function and split_region + // Now we know entire region is mapped, so change permissions + // We may need to split regions if this area spans adjacent regions + addr = address; + count = 0; + while(count < size) { + mr = memory_mapping(uc, addr); + len = MIN(size - count, mr->end - addr); + if (!split_region(handle, mr, addr, len)) + return UC_ERR_NOMEM; + count += len; + addr += len; + } + + // Now iterate all the regions to set permission + addr = address; + count = 0; + while(count < size) { + mr = memory_mapping(uc, addr); + len = MIN(size - count, mr->end - addr); uc->memory_unmap(uc, mr); for (i = 0; i < uc->mapped_block_count; i++) { if (uc->mapped_blocks[i] == mr) { @@ -860,18 +877,10 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) break; } } - } else { - //ouch, we are going to need to subdivide blocks - size_t count = 0, len; - while(count < size) { - MemoryRegion *mr = memory_mapping(uc, address); - len = MIN(size - count, mr->end - address); - if (!split_region(handle, mr, address, len, true)) - return UC_ERR_NOMEM; - count += len; - address += len; - } + count += len; + addr += len; } + return UC_ERR_OK; } From 2da46caef72905ae4c3b5beaf7f21ca76ad543e6 Mon Sep 17 00:00:00 2001 From: Chris Eagle Date: Thu, 3 Sep 2015 12:26:36 -0700 Subject: [PATCH 21/23] smooth out split_region related code --- qemu/memory.c | 11 +++++++++ samples/mem_exec.c | 4 +-- samples/mem_protect.c | 4 +-- samples/mem_unmap.c | 4 +-- uc.c | 57 ++++++++++++++----------------------------- 5 files changed, 35 insertions(+), 45 deletions(-) diff --git a/qemu/memory.c b/qemu/memory.c index e04d59b7..be7933d5 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -47,6 +47,7 @@ MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, ui void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) { + int i; target_ulong addr; //make sure all pages associated with the MemoryRegion are flushed for (addr = mr->addr; addr < mr->end; addr += uc->target_page_size) { @@ -54,6 +55,16 @@ void memory_unmap(struct uc_struct *uc, MemoryRegion *mr) } mr->enabled = false; memory_region_del_subregion(get_system_memory(uc), mr); + + for (i = 0; i < uc->mapped_block_count; i++) { + if (uc->mapped_blocks[i] == mr) { + uc->mapped_block_count--; + //shift remainder of array down over deleted pointer + memcpy(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i)); + break; + } + } + g_free(mr); } diff --git a/samples/mem_exec.c b/samples/mem_exec.c index 370fd1ea..19153b46 100644 --- a/samples/mem_exec.c +++ b/samples/mem_exec.c @@ -204,7 +204,7 @@ int main(int argc, char **argv, char **envp) printf("ok %d - Program written to memory\n", log_num++); } - if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); return 6; } else { @@ -212,7 +212,7 @@ int main(int argc, char **argv, char **envp) } // intercept memory write events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); return 7; } else { diff --git a/samples/mem_protect.c b/samples/mem_protect.c index 5739563e..d9644d51 100644 --- a/samples/mem_protect.c +++ b/samples/mem_protect.c @@ -212,7 +212,7 @@ int main(int argc, char **argv, char **envp) printf("ok %d - Program written to memory\n", log_num++); } - if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); return 5; } else { @@ -220,7 +220,7 @@ int main(int argc, char **argv, char **envp) } // intercept memory write events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); return 6; } else { diff --git a/samples/mem_unmap.c b/samples/mem_unmap.c index b31bf220..e35b95ba 100644 --- a/samples/mem_unmap.c +++ b/samples/mem_unmap.c @@ -207,7 +207,7 @@ int main(int argc, char **argv, char **envp) printf("ok %d - Program written to memory\n", log_num++); } - if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, 1, 0) != UC_ERR_OK) { + if (uc_hook_add(handle, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_CODE handler\n", log_num++); return 5; } else { @@ -215,7 +215,7 @@ int main(int argc, char **argv, char **envp) } // intercept memory write events - if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL) != UC_ERR_OK) { + if (uc_hook_add(handle, &trace1, UC_HOOK_MEM_WRITE, hook_mem_write, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK) { printf("not ok %d - Failed to install UC_HOOK_MEM_WRITE handler\n", log_num++); return 6; } else { diff --git a/uc.c b/uc.c index 099af362..eaf1219a 100644 --- a/uc.c +++ b/uc.c @@ -32,7 +32,7 @@ #include "qemu/include/hw/boards.h" static uint8_t *copy_region(uch uc, MemoryRegion *mr); -static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size); +static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t size, bool do_delete); UNICORN_EXPORT unsigned int uc_version(unsigned int *major, unsigned int *minor) @@ -678,11 +678,15 @@ static uint8_t *copy_region(uch handle, MemoryRegion *mr) This is a static function and callers have already done some preliminary parameter validation. + + The do_delete argument indicates that we are being called to support + uc_mem_unmap. In this case we save some time by choosing NOT to remap + the areas that are intended to get unmapped */ // TODO: investigate whether qemu region manipulation functions already offered // this capability static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, - size_t size) + size_t size, bool do_delete) { uint8_t *backup; uint32_t perms; @@ -690,9 +694,8 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t l_size, m_size, r_size; chunk_end = address + size; - if (address <= mr->addr && chunk_end >= mr->end) { + if (address <= mr->addr && chunk_end >= mr->end) return true; - } if (size == 0) //trivial case @@ -742,7 +745,7 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, if (uc_mem_write(handle, begin, backup, l_size) != UC_ERR_OK) goto error; } - if (m_size > 0) { + if (m_size > 0 && !do_delete) { if (uc_mem_map(handle, address, m_size, perms) != UC_ERR_OK) goto error; if (uc_mem_write(handle, address, backup + l_size, m_size) != UC_ERR_OK) @@ -788,7 +791,7 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) if ((perms & ~UC_PROT_ALL) != 0) return UC_ERR_INVAL; - //check that user's entire requested block is mapped + // check that user's entire requested block is mapped if (!check_mem_area(uc, address, size)) return UC_ERR_NOMEM; @@ -799,24 +802,16 @@ uc_err uc_mem_protect(uch handle, uint64_t address, size_t size, uint32_t perms) while(count < size) { mr = memory_mapping(uc, addr); len = MIN(size - count, mr->end - addr); - if (!split_region(handle, mr, addr, len)) + if (!split_region(handle, mr, addr, len, false)) return UC_ERR_NOMEM; - count += len; - addr += len; - } - // Now iterate all the regions to set permission - addr = address; - count = 0; - while(count < size) { mr = memory_mapping(uc, addr); - len = MIN(size - count, mr->end - addr); mr->perms = perms; uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + count += len; addr += len; } - return UC_ERR_OK; } @@ -824,7 +819,6 @@ UNICORN_EXPORT uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) { MemoryRegion *mr; - unsigned int i; struct uc_struct* uc = (struct uc_struct *)handle; uint64_t addr; size_t count, len; @@ -845,42 +839,27 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) if ((size & uc->target_page_align) != 0) return UC_ERR_MAP; - //check that user's entire requested block is mapped + // check that user's entire requested block is mapped if (!check_mem_area(uc, address, size)) return UC_ERR_NOMEM; - // Now we know entire region is mapped, so change permissions + // Now we know entire region is mapped, so do the unmap // We may need to split regions if this area spans adjacent regions addr = address; count = 0; while(count < size) { mr = memory_mapping(uc, addr); len = MIN(size - count, mr->end - addr); - if (!split_region(handle, mr, addr, len)) + if (!split_region(handle, mr, addr, len, true)) return UC_ERR_NOMEM; - count += len; - addr += len; - } - - // Now iterate all the regions to set permission - addr = address; - count = 0; - while(count < size) { + // if we can retieve the mapping, then no splitting took place + // so unmap here mr = memory_mapping(uc, addr); - len = MIN(size - count, mr->end - addr); - uc->memory_unmap(uc, mr); - for (i = 0; i < uc->mapped_block_count; i++) { - if (uc->mapped_blocks[i] == mr) { - uc->mapped_block_count--; - //shift remainder of array down over deleted pointer - memcpy(&uc->mapped_blocks[i], &uc->mapped_blocks[i + 1], sizeof(MemoryRegion*) * (uc->mapped_block_count - i)); - break; - } - } + if (mr != NULL) + uc->memory_unmap(uc, mr); count += len; addr += len; } - return UC_ERR_OK; } From e54519c09ff37011b043214713882398e7cb1c54 Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Fri, 4 Sep 2015 09:20:13 +0800 Subject: [PATCH 22/23] cleanup --- uc.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/uc.c b/uc.c index eaf1219a..f9bbf846 100644 --- a/uc.c +++ b/uc.c @@ -642,7 +642,8 @@ uc_err uc_mem_map(uch handle, uint64_t address, size_t size, uint32_t perms) return UC_ERR_INVAL; if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow - regions = (MemoryRegion**)realloc(uc->mapped_blocks, sizeof(MemoryRegion*) * (uc->mapped_block_count + MEM_BLOCK_INCR)); + regions = (MemoryRegion**)realloc(uc->mapped_blocks, + sizeof(MemoryRegion*) * (uc->mapped_block_count + MEM_BLOCK_INCR)); if (regions == NULL) { return UC_ERR_NOMEM; } @@ -694,26 +695,30 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, size_t l_size, m_size, r_size; chunk_end = address + size; + + // if this region belongs to area [address, address+size], + // then there is no work to do. if (address <= mr->addr && chunk_end >= mr->end) return true; if (size == 0) - //trivial case + // trivial case return true; if (address >= mr->end || chunk_end <= mr->addr) - //impossible case + // impossible case return false; backup = copy_region(handle, mr); if (backup == NULL) return false; - //save the essential information required for the split before mr gets deleted + // save the essential information required for the split before mr gets deleted perms = mr->perms; begin = mr->addr; end = mr->end; + // unmap this region first, then do split it later if (uc_mem_unmap(handle, mr->addr, int128_get64(mr->size)) != UC_ERR_OK) goto error; @@ -724,13 +729,13 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, * case 3 |---size--| */ - //adjust some things + // adjust some things if (address < begin) address = begin; if (chunk_end > end) chunk_end = end; - //compute sub region sizes + // compute sub region sizes l_size = (size_t)(address - begin); r_size = (size_t)(end - chunk_end); m_size = (size_t)(chunk_end - address); @@ -745,19 +750,23 @@ static bool split_region(uch handle, MemoryRegion *mr, uint64_t address, if (uc_mem_write(handle, begin, backup, l_size) != UC_ERR_OK) goto error; } + if (m_size > 0 && !do_delete) { if (uc_mem_map(handle, address, m_size, perms) != UC_ERR_OK) goto error; if (uc_mem_write(handle, address, backup + l_size, m_size) != UC_ERR_OK) goto error; } + if (r_size > 0) { if (uc_mem_map(handle, chunk_end, r_size, perms) != UC_ERR_OK) goto error; if (uc_mem_write(handle, chunk_end, backup + l_size + m_size, r_size) != UC_ERR_OK) goto error; } + return true; + error: free(backup); return false; @@ -852,7 +861,7 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size) len = MIN(size - count, mr->end - addr); if (!split_region(handle, mr, addr, len, true)) return UC_ERR_NOMEM; - // if we can retieve the mapping, then no splitting took place + // if we can retrieve the mapping, then no splitting took place // so unmap here mr = memory_mapping(uc, addr); if (mr != NULL) From 0962c4822b99bfa6215a3381ef68f4943ca71663 Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Fri, 4 Sep 2015 09:43:31 +0800 Subject: [PATCH 23/23] cleanup & update bindings' constants --- bindings/go/unicorn/unicorn_const.go | 16 ++++++++++------ bindings/python/unicorn/unicorn_const.py | 16 ++++++++++------ include/unicorn/unicorn.h | 5 ++--- samples/sample_sparc.c | 2 +- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/bindings/go/unicorn/unicorn_const.go b/bindings/go/unicorn/unicorn_const.go index 427b34cd..e5ab6863 100644 --- a/bindings/go/unicorn/unicorn_const.go +++ b/bindings/go/unicorn/unicorn_const.go @@ -34,7 +34,7 @@ const ( UC_MODE_MIPS64 = 8 UC_ERR_OK = 0 - UC_ERR_OOM = 1 + UC_ERR_NOMEM = 1 UC_ERR_ARCH = 2 UC_ERR_HANDLE = 3 UC_ERR_UCH = 4 @@ -46,13 +46,16 @@ const ( UC_ERR_HOOK = 10 UC_ERR_INSN_INVALID = 11 UC_ERR_MAP = 12 - UC_ERR_MEM_WRITE_NW = 13 - UC_ERR_MEM_READ_NR = 14 + UC_ERR_WRITE_PROT = 13 + UC_ERR_READ_PROT = 14 + UC_ERR_EXEC_PROT = 15 + UC_ERR_INVAL = 16 UC_MEM_READ = 16 UC_MEM_WRITE = 17 UC_MEM_READ_WRITE = 18 - UC_MEM_WRITE_NW = 19 - UC_MEM_READ_NR = 20 + UC_MEM_WRITE_PROT = 19 + UC_MEM_READ_PROT = 20 + UC_MEM_EXEC_PROT = 21 UC_HOOK_INTR = 32 UC_HOOK_INSN = 33 UC_HOOK_CODE = 34 @@ -65,5 +68,6 @@ const ( UC_PROT_NONE = 0 UC_PROT_READ = 1 UC_PROT_WRITE = 2 - UC_PROT_ALL = 3 + UC_PROT_EXEC = 4 + UC_PROT_ALL = 7 ) \ No newline at end of file diff --git a/bindings/python/unicorn/unicorn_const.py b/bindings/python/unicorn/unicorn_const.py index f2c8f0db..0f2bf718 100644 --- a/bindings/python/unicorn/unicorn_const.py +++ b/bindings/python/unicorn/unicorn_const.py @@ -32,7 +32,7 @@ UC_MODE_MIPS32 = 4 UC_MODE_MIPS64 = 8 UC_ERR_OK = 0 -UC_ERR_OOM = 1 +UC_ERR_NOMEM = 1 UC_ERR_ARCH = 2 UC_ERR_HANDLE = 3 UC_ERR_UCH = 4 @@ -44,13 +44,16 @@ UC_ERR_CODE_INVALID = 9 UC_ERR_HOOK = 10 UC_ERR_INSN_INVALID = 11 UC_ERR_MAP = 12 -UC_ERR_MEM_WRITE_NW = 13 -UC_ERR_MEM_READ_NR = 14 +UC_ERR_WRITE_PROT = 13 +UC_ERR_READ_PROT = 14 +UC_ERR_EXEC_PROT = 15 +UC_ERR_INVAL = 16 UC_MEM_READ = 16 UC_MEM_WRITE = 17 UC_MEM_READ_WRITE = 18 -UC_MEM_WRITE_NW = 19 -UC_MEM_READ_NR = 20 +UC_MEM_WRITE_PROT = 19 +UC_MEM_READ_PROT = 20 +UC_MEM_EXEC_PROT = 21 UC_HOOK_INTR = 32 UC_HOOK_INSN = 33 UC_HOOK_CODE = 34 @@ -63,4 +66,5 @@ UC_HOOK_MEM_READ_WRITE = 39 UC_PROT_NONE = 0 UC_PROT_READ = 1 UC_PROT_WRITE = 2 -UC_PROT_ALL = 3 +UC_PROT_EXEC = 4 +UC_PROT_ALL = 7 diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index e9136f14..c6e5015c 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -447,9 +447,8 @@ uc_err uc_mem_unmap(uch handle, uint64_t address, size_t size); This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, or this will return with UC_ERR_INVAL error. - @return UC_ERR_OK on success, UC_ERR_HANDLE for an invalid handle, UC_ERR_INVAL - for invalid perms or unaligned address or size, UC_ERR_NOMEM if entire region - is not mapped. + @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_protect(uch handle, uint64_t address, size_t size, uint32_t perms); diff --git a/samples/sample_sparc.c b/samples/sample_sparc.c index c7f2971a..ee81aad9 100644 --- a/samples/sample_sparc.c +++ b/samples/sample_sparc.c @@ -64,7 +64,7 @@ static void test_sparc(void) // emulate machine code in infinite time (last param = 0), or when // finishing all the code. - err = uc_emu_start(handle, ADDRESS, ADDRESS + sizeof(SPARC_CODE) -1, 0, 0); + err = uc_emu_start(handle, ADDRESS, ADDRESS + sizeof(SPARC_CODE) - 1, 0, 0); if (err) { printf("Failed on uc_emu_start() with error returned: %u (%s)\n", err, uc_strerror(err));