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 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.py b/bindings/python/unicorn/unicorn.py index ae672227..95f927dc 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -60,37 +60,41 @@ def _setup_prototype(lib, fname, restype, *argtypes): getattr(lib, fname).restype = restype getattr(lib, fname).argtypes = argtypes -_setup_prototype(_uc, "uc_version", ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) +ucerr = ctypes.c_int +ucengine = ctypes.c_void_p +uc_hook_h = ctypes.c_size_t + +_setup_prototype(_uc, "uc_version", ucerr, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) _setup_prototype(_uc, "uc_arch_supported", ctypes.c_bool, ctypes.c_int) -_setup_prototype(_uc, "uc_open", ctypes.c_int, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ctypes.c_size_t)) -_setup_prototype(_uc, "uc_close", ctypes.c_int, ctypes.POINTER(ctypes.c_size_t)) -_setup_prototype(_uc, "uc_strerror", ctypes.c_char_p, ctypes.c_int) -_setup_prototype(_uc, "uc_errno", ctypes.c_int, ctypes.c_size_t) -_setup_prototype(_uc, "uc_reg_read", ctypes.c_int, ctypes.c_size_t, ctypes.c_int, ctypes.c_void_p) -_setup_prototype(_uc, "uc_reg_write", ctypes.c_int, ctypes.c_size_t, ctypes.c_int, ctypes.c_void_p) -_setup_prototype(_uc, "uc_mem_read", ctypes.c_int, ctypes.c_size_t, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) -_setup_prototype(_uc, "uc_mem_write", ctypes.c_int, ctypes.c_size_t, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) -_setup_prototype(_uc, "uc_emu_start", ctypes.c_int, ctypes.c_size_t, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) -_setup_prototype(_uc, "uc_emu_stop", ctypes.c_int, ctypes.c_size_t) -_setup_prototype(_uc, "uc_hook_del", ctypes.c_int, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)) -_setup_prototype(_uc, "uc_mem_map", ctypes.c_int, ctypes.c_size_t, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) +_setup_prototype(_uc, "uc_open", ucerr, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(ucengine)) +_setup_prototype(_uc, "uc_close", ucerr, ucengine) +_setup_prototype(_uc, "uc_strerror", ctypes.c_char_p, ucerr) +_setup_prototype(_uc, "uc_errno", ucerr, ucengine) +_setup_prototype(_uc, "uc_reg_read", ucerr, ucengine, ctypes.c_int, ctypes.c_void_p) +_setup_prototype(_uc, "uc_reg_write", ucerr, ucengine, ctypes.c_int, ctypes.c_void_p) +_setup_prototype(_uc, "uc_mem_read", ucerr, ucengine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) +_setup_prototype(_uc, "uc_mem_write", ucerr, ucengine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) +_setup_prototype(_uc, "uc_emu_start", ucerr, ucengine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) +_setup_prototype(_uc, "uc_emu_stop", ucerr, ucengine) +_setup_prototype(_uc, "uc_hook_del", ucerr, ucengine, uc_hook_h) +_setup_prototype(_uc, "uc_mem_map", ucerr, ucengine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) # uc_hook_add is special due to variable number of arguments _uc.uc_hook_add = getattr(_uc, "uc_hook_add") _uc.uc_hook_add.restype = ctypes.c_int -UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) -UC_HOOK_MEM_INVALID_CB = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_size_t, ctypes.c_int, \ +UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, ucengine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) +UC_HOOK_MEM_INVALID_CB = ctypes.CFUNCTYPE(ctypes.c_bool, ucengine, ctypes.c_int, \ ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p) -UC_HOOK_MEM_ACCESS_CB = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_int, \ +UC_HOOK_MEM_ACCESS_CB = ctypes.CFUNCTYPE(None, ucengine, ctypes.c_int, \ ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p) -UC_HOOK_INTR_CB = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_uint32, \ +UC_HOOK_INTR_CB = ctypes.CFUNCTYPE(None, ucengine, ctypes.c_uint32, \ ctypes.c_void_p) -UC_HOOK_INSN_IN_CB = ctypes.CFUNCTYPE(ctypes.c_uint32, ctypes.c_size_t, ctypes.c_uint32, \ +UC_HOOK_INSN_IN_CB = ctypes.CFUNCTYPE(ctypes.c_uint32, ucengine, ctypes.c_uint32, \ ctypes.c_int, ctypes.c_void_p) -UC_HOOK_INSN_OUT_CB = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_uint32, \ +UC_HOOK_INSN_OUT_CB = ctypes.CFUNCTYPE(None, ucengine, ctypes.c_uint32, \ ctypes.c_int, ctypes.c_uint32, ctypes.c_void_p) -UC_HOOK_INSN_SYSCALL_CB = ctypes.CFUNCTYPE(None, ctypes.c_size_t, ctypes.c_void_p) +UC_HOOK_INSN_SYSCALL_CB = ctypes.CFUNCTYPE(None, ucengine, ctypes.c_void_p) # access to error code via @errno of UcError @@ -130,7 +134,7 @@ class Uc(object): raise UcError(UC_ERR_VERSION) self._arch, self._mode = arch, mode - self._uch = ctypes.c_size_t() + self._uch = ctypes.c_void_p() status = _uc.uc_open(arch, mode, ctypes.byref(self._uch)) if status != UC_ERR_OK: self._uch = None @@ -144,7 +148,8 @@ class Uc(object): def __del__(self): if self._uch: try: - status = _uc.uc_close(ctypes.byref(self._uch)) + status = _uc.uc_close(self._uch) + self._uch = None if status != UC_ERR_OK: raise UcError(status) except: # _uc might be pulled from under our feet @@ -251,7 +256,7 @@ class Uc(object): # add a hook def hook_add(self, htype, callback, user_data=None, arg1=1, arg2=0): - _h2 = ctypes.c_size_t() + _h2 = uc_hook_h() # save callback & user_data self._callback_count += 1 @@ -296,8 +301,8 @@ class Uc(object): # delete a hook def hook_del(self, h): - _h = ctypes.c_size_t(h) - status = _uc.uc_hook_del(self._uch, ctypes.byref(_h)) + _h = uc_hook_h(h) + status = _uc.uc_hook_del(self._uch, _h) if status != UC_ERR_OK: raise UcError(status) h = 0 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/hook.c b/hook.c index 850c9564..109a20d2 100644 --- a/hook.c +++ b/hook.c @@ -91,7 +91,7 @@ size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, vo } // return 0 on success, -1 on failure -uc_err hook_del(struct uc_struct *uc, uc_hook_h hh) +uc_err hook_del(struct uc_struct *uc, uchook hh) { if (hh == uc->hook_block_idx) { uc->hook_block_idx = 0; diff --git a/include/hook.h b/include/hook.h index 8c095b28..08e9e9b0 100644 --- a/include/hook.h +++ b/include/hook.h @@ -8,7 +8,7 @@ size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, void *callback, void *user_data); // return 0 on success, -1 on failure -uc_err hook_del(struct uc_struct *uc, uc_hook_h hh); +uc_err hook_del(struct uc_struct *uc, uchook hh); // return NULL on failure struct hook_struct *hook_find(struct uc_struct *uc, int type, uint64_t address); diff --git a/include/uc_priv.h b/include/uc_priv.h index 195d719c..dc4fcfd6 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; @@ -172,8 +175,9 @@ struct uc_struct { bool block_full; MemoryRegion **mapped_blocks; uint32_t mapped_block_count; - void *qemu_thread_data; // to support cross compile to Windows (qemu-thread-win32.c) + uint32_t target_page_size; + uint32_t target_page_align; }; #include "qemu_macro.h" diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 40a32e0e..1aae5c11 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -22,7 +22,7 @@ extern "C" { struct uc_struct; typedef struct uc_struct ucengine; -typedef size_t uc_hook_h; +typedef size_t uchook; #include "m68k.h" #include "x86.h" @@ -106,7 +106,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_MODE, // Invalid/unsupported mode: uc_open() @@ -117,8 +117,10 @@ 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_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; @@ -150,8 +152,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_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. @@ -372,7 +375,7 @@ uc_err uc_emu_stop(ucengine *uc); for detailed error). */ UNICORN_EXPORT -uc_err uc_hook_add(ucengine *uc, uc_hook_h *hh, uc_hook_t type, void *callback, void *user_data, ...); +uc_err uc_hook_add(ucengine *uc, uchook *hh, uc_hook_t type, void *callback, void *user_data, ...); /* Unregister (remove) a hook callback. @@ -387,13 +390,14 @@ uc_err uc_hook_add(ucengine *uc, uc_hook_h *hh, uc_hook_t type, void *callback, for detailed error). */ UNICORN_EXPORT -uc_err uc_hook_del(ucengine *uc, uc_hook_h hh); +uc_err uc_hook_del(ucengine *uc, uchook hh); typedef enum uc_prot { UC_PROT_NONE = 0, UC_PROT_READ = 1, UC_PROT_WRITE = 2, - UC_PROT_ALL = 3, + UC_PROT_EXEC = 4, + UC_PROT_ALL = 7, } uc_prot; /* @@ -402,12 +406,12 @@ typedef enum uc_prot { @uc: 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, - or this will return with UC_ERR_MAP error. See uc_prot type above. + 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, or other value on failure (refer to uc_err enum for detailed error). @@ -415,6 +419,41 @@ typedef enum uc_prot { UNICORN_EXPORT uc_err uc_mem_map(ucengine *uc, 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(ucengine *uc, uint64_t address, size_t size); + +/* + Set memory permissions for emulation memory. + This API changes permissions on an existing memory region. + + @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_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. + @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_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_protect(ucengine *uc, uint64_t address, size_t size, uint32_t perms); + #ifdef __cplusplus } #endif 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/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/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 60f86ae4..be7933d5 100644 --- a/qemu/memory.c +++ b/qemu/memory.c @@ -45,6 +45,29 @@ MemoryRegion *memory_map(struct uc_struct *uc, ram_addr_t begin, size_t size, ui return ram; } +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) { + tlb_flush_page(uc->current_cpu, addr); + } + 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); +} + 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/softmmu_template.h b/qemu/softmmu_template.h index bda85dce..3c851686 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -181,6 +181,23 @@ 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)( + 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_EXEC_PROT; + // printf("***** Invalid fetch (non-executable) 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(env->uc, UC_HOOK_MEM_READ, addr); @@ -206,20 +223,16 @@ 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)( - 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)( + 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_MEM_READ_NR; + 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; @@ -326,6 +339,23 @@ 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)( + 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_EXEC_PROT; + // printf("***** Invalid fetch (non-executable) 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(env->uc, UC_HOOK_MEM_READ, addr); @@ -351,20 +381,15 @@ 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)( - 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)( + 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_MEM_READ_NR; + 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; @@ -534,20 +559,16 @@ 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)( - 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)( + 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_MEM_WRITE_NW; + env->invalid_error = UC_ERR_WRITE_PROT; // printf("***** Invalid memory write (ro) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return; @@ -672,20 +693,16 @@ 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)( - 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)( + 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_MEM_WRITE_NW; + 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/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..adfb5f05 100644 --- a/qemu/unicorn_common.h +++ b/qemu/unicorn_common.h @@ -73,8 +73,12 @@ 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; + 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/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/regress/block_test.c b/regress/block_test.c index 06a0fa21..7aaf1b93 100644 --- a/regress/block_test.c +++ b/regress/block_test.c @@ -62,7 +62,7 @@ int main() { } fprintf(stderr, "ok %d - uc_mem_write\n", count++); - uc_hook_h h1, h2; + uchook h1, h2; err = uc_hook_add(uc, &h1, UC_HOOK_BLOCK, cb_hookblock, NULL, (uint64_t)1, (uint64_t)0); if (err != UC_ERR_OK) { diff --git a/regress/nr_mem_test.c b/regress/nr_mem_test.c index 0e70829b..b804dd79 100644 --- a/regress/nr_mem_test.c +++ b/regress/nr_mem_test.c @@ -55,7 +55,7 @@ static bool hook_mem_invalid(ucengine *uc, uc_mem_type type, int main(int argc, char **argv, char **envp) { ucengine *uc; - uc_hook_h trace1, trace2; + uchook trace1, trace2; uc_err err; uint32_t eax, ebx; diff --git a/regress/reg_write_sign_extension.py b/regress/reg_write_sign_extension.py old mode 100644 new mode 100755 diff --git a/regress/rep_movsb.c b/regress/rep_movsb.c index 97b00c15..0d91cbef 100644 --- a/regress/rep_movsb.c +++ b/regress/rep_movsb.c @@ -90,7 +90,7 @@ static void hook_mem_write(ucengine *uc, uc_mem_type type, int main(int argc, char **argv, char **envp) { ucengine *uc; - uc_hook_h trace1, trace2; + uchook trace1, trace2; uc_err err; uint8_t buf1[100], readbuf[100]; diff --git a/regress/ro_mem_test.c b/regress/ro_mem_test.c index 51b94b2f..49db748e 100644 --- a/regress/ro_mem_test.c +++ b/regress/ro_mem_test.c @@ -95,7 +95,7 @@ static bool hook_mem_invalid(ucengine *uc, uc_mem_type type, int main(int argc, char **argv, char **envp) { ucengine *uc; - uc_hook_h trace1, trace2; + uchook trace1, trace2; uc_err err; uint8_t bytes[8]; uint32_t esp; @@ -157,8 +157,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(uc, UC_X86_REG_EAX, &eax); + //resume execution at the mov dword [eax], 0x87654321 + //to test an aligned write as well err = uc_emu_start(uc, 0x400015, 0x400000 + sizeof(PROGRAM), 0, 2); if (err) { printf("Expected failure on uc_emu_start() with error returned %u: %s\n", diff --git a/regress/sigill.c b/regress/sigill.c index ad7af47f..b7317888 100644 --- a/regress/sigill.c +++ b/regress/sigill.c @@ -21,7 +21,7 @@ int main() int size; uint8_t *buf; ucengine *uc; - uc_hook_h uh_trap; + uchook uh_trap; uc_err err = uc_open (UC_ARCH_X86, UC_MODE_64, &uc); if (err) { fprintf (stderr, "Cannot initialize unicorn\n"); diff --git a/regress/timeout_segfault.c b/regress/timeout_segfault.c index 2632a51f..73070978 100644 --- a/regress/timeout_segfault.c +++ b/regress/timeout_segfault.c @@ -38,7 +38,7 @@ static void test_arm(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r0 = 0x1234; // R0 register int r2 = 0x6789; // R1 register @@ -94,7 +94,7 @@ static void test_thumb(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int sp = 0x1234; // R0 register diff --git a/samples/Makefile b/samples/Makefile index 1ebb891f..f6345ae8 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -97,6 +97,9 @@ endif ifneq (,$(findstring x86,$(UNICORN_ARCHS))) 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 @@ -111,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 + 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..3b63012c --- /dev/null +++ b/samples/mem_exec.c @@ -0,0 +1,280 @@ +/* + 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(ucengine *uc, uint64_t addr, uint32_t size, void *user_data) +{ + uint8_t opcode; + + if (uc_mem_read(uc, 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(uc) != 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(ucengine *uc, 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(ucengine *uc, 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); + + //make page executable + if (uc_mem_protect(uc, 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(uc, 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) +{ + ucengine *uc; + uchook 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, &uc); + 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(uc, 0x100000, 0x1000, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(uc, 0x1ff000, 0x2000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(uc, 0x300000, 0x2000, UC_PROT_READ); + uc_mem_map(uc, 0xf00000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + + esp = 0xf00000 + 0x1000; + + // Setup stack pointer + if (uc_reg_write(uc, 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(uc, 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(uc, 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(uc, 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(uc, &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 ucr\n", log_num++); + return 6; + } else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } + + // intercept memory write events + if (uc_hook_add(uc, &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 ucr\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(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\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(uc, 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(uc, 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(uc, 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(uc, 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(uc) == 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 new file mode 100644 index 00000000..dfabd0fb --- /dev/null +++ b/samples/mem_protect.c @@ -0,0 +1,302 @@ +/* + 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(ucengine *uc, uint64_t addr, uint32_t size, void *user_data) +{ + uint8_t opcode; + uint32_t testval; + if (uc_mem_read(uc, 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(uc, 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(uc, 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(uc) != 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(ucengine *uc, 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(ucengine *uc, 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); + + if (uc_mem_read(uc, 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(uc, 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) +{ + ucengine *uc; + uchook 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, &uc); + 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(uc, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(uc, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(uc, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(uc, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); + + // fill in sections that shouldn't get touched + if (uc_mem_write(uc, 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(uc, 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++); + } + + // write machine code to be emulated to memory + if (uc_mem_write(uc, 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_hook_add(uc, &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 ucr\n", log_num++); + return 5; + } else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } + + // intercept memory write events + if (uc_hook_add(uc, &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 ucr\n", log_num++); + return 6; + } else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } + + // intercept invalid memory events + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++); + return 7; + } 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(uc, 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"); + + //read from the remapped memory + testval = 0x42424242; + for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { + uint32_t val; + if (uc_mem_read(uc, 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(uc, 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(uc, 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(uc) == 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 100644 index 00000000..0bf61911 --- /dev/null +++ b/samples/mem_unmap.c @@ -0,0 +1,293 @@ +/* + + 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(ucengine *uc, uint64_t addr, uint32_t size, void *user_data) +{ + uint8_t opcode; + uint32_t testval; + if (uc_mem_read(uc, 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(uc, 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(uc, 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(uc) != 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(ucengine *uc, 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(ucengine *uc, 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(uc, 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(uc, 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) +{ + ucengine *uc; + uchook 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, &uc); + 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(uc, CODE_SECTION, CODE_SIZE, UC_PROT_READ | UC_PROT_EXEC); + uc_mem_map(uc, 0x200000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(uc, 0x300000, 0x1000, UC_PROT_READ | UC_PROT_WRITE); + uc_mem_map(uc, 0x3ff000, 0x3000, UC_PROT_READ | UC_PROT_WRITE); + + // fill in sections that shouldn't get touched + if (uc_mem_write(uc, 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(uc, 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++); + } + + // write machine code to be emulated to memory + if (uc_mem_write(uc, 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_hook_add(uc, &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 ucr\n", log_num++); + return 5; + } else { + printf("ok %d - UC_HOOK_CODE installed\n", log_num++); + } + + // intercept memory write events + if (uc_hook_add(uc, &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 ucr\n", log_num++); + return 6; + } else { + printf("ok %d - UC_HOOK_MEM_WRITE installed\n", log_num++); + } + + // intercept invalid memory events + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install UC_HOOK_MEM_INVALID ucr\n", log_num++); + return 7; + } 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(uc, 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"); + + //read from the remapped memory + testval = 0x42424242; + for (addr = 0x200000; addr <= 0x400000; addr += 0x100000) { + uint32_t val; + if (uc_mem_read(uc, 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(uc, 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(uc, 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(uc) == 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/sample_arm.c b/samples/sample_arm.c index 38e8590a..81ec691b 100644 --- a/samples/sample_arm.c +++ b/samples/sample_arm.c @@ -29,7 +29,7 @@ static void test_arm(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r0 = 0x1234; // R0 register int r2 = 0x6789; // R1 register @@ -85,7 +85,7 @@ static void test_thumb(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int sp = 0x1234; // R0 register diff --git a/samples/sample_arm64.c b/samples/sample_arm64.c index 61a58f20..3541b2f6 100644 --- a/samples/sample_arm64.c +++ b/samples/sample_arm64.c @@ -28,7 +28,7 @@ static void test_arm64(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int64_t x11 = 0x1234; // X11 register int64_t x13 = 0x6789; // X13 register diff --git a/samples/sample_m68k.c b/samples/sample_m68k.c index 049041cb..b8fef353 100644 --- a/samples/sample_m68k.c +++ b/samples/sample_m68k.c @@ -25,7 +25,7 @@ static void hook_code(ucengine *uc, uint64_t address, uint32_t size, void *user_ static void test_m68k(void) { ucengine *uc; - uc_hook_h trace1, trace2; + uchook trace1, trace2; uc_err err; int d0 = 0x0000; // d0 data register diff --git a/samples/sample_mips.c b/samples/sample_mips.c index 0806f60e..e604563c 100644 --- a/samples/sample_mips.c +++ b/samples/sample_mips.c @@ -29,7 +29,7 @@ static void test_mips_eb(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r1 = 0x6789; // R1 register @@ -78,7 +78,7 @@ static void test_mips_el(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r1 = 0x6789; // R1 register diff --git a/samples/sample_sparc.c b/samples/sample_sparc.c index 36536680..540b7d36 100644 --- a/samples/sample_sparc.c +++ b/samples/sample_sparc.c @@ -29,7 +29,7 @@ static void test_sparc(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int g1 = 0x1230; // G1 register int g2 = 0x6789; // G2 register @@ -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(uc, ADDRESS, ADDRESS + sizeof(SPARC_CODE) -1, 0, 0); + err = uc_emu_start(uc, 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)); diff --git a/samples/sample_x86.c b/samples/sample_x86.c index 9bdd8232..aecad6b5 100644 --- a/samples/sample_x86.c +++ b/samples/sample_x86.c @@ -171,7 +171,7 @@ static void test_i386(void) ucengine *uc; uc_err err; uint32_t tmp; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register @@ -232,7 +232,7 @@ static void test_i386_jump(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; printf("===================================\n"); printf("Emulate i386 code with jump\n"); @@ -328,7 +328,7 @@ static void test_i386_invalid_mem_read(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register @@ -385,7 +385,7 @@ static void test_i386_invalid_mem_write(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2, trace3; + uchook trace1, trace2, trace3; uint32_t tmp; int r_ecx = 0x1234; // ECX register @@ -457,7 +457,7 @@ static void test_i386_jump_invalid(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r_ecx = 0x1234; // ECX register int r_edx = 0x7890; // EDX register @@ -513,7 +513,7 @@ static void test_i386_inout(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2, trace3, trace4; + uchook trace1, trace2, trace3, trace4; int r_eax = 0x1234; // EAX register int r_ecx = 0x6789; // ECX register @@ -574,7 +574,7 @@ static void test_x86_64(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2, trace3, trace4; + uchook trace1, trace2, trace3, trace4; int64_t rax = 0x71f3029efd49d41d; int64_t rbx = 0xd87b45277f133ddb; @@ -689,7 +689,7 @@ static void test_x86_64(void) static void test_x86_64_syscall(void) { ucengine *uc; - uc_hook_h trace1; + uchook trace1; uc_err err; int64_t rax = 0x100; diff --git a/samples/shellcode.c b/samples/shellcode.c index f185db97..31f72ffc 100644 --- a/samples/shellcode.c +++ b/samples/shellcode.c @@ -90,7 +90,7 @@ static void test_i386(void) { ucengine *uc; uc_err err; - uc_hook_h trace1, trace2; + uchook trace1, trace2; int r_esp = ADDRESS + 0x200000; // ESP register diff --git a/uc.c b/uc.c index cc25918a..210616bf 100644 --- a/uc.c +++ b/uc.c @@ -31,6 +31,7 @@ #include "qemu/include/hw/boards.h" + UNICORN_EXPORT unsigned int uc_version(unsigned int *major, unsigned int *minor) { @@ -58,8 +59,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: @@ -80,10 +81,14 @@ 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_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)"; + case UC_ERR_INVAL: + return "Invalid argumet (UC_ERR_INVAL)"; } } @@ -129,7 +134,7 @@ uc_err uc_open(uc_arch arch, uc_mode mode, ucengine **result) uc = calloc(1, sizeof(*uc)); if (!uc) { // memory insufficient - return UC_ERR_OOM; + return UC_ERR_NOMEM; } uc->errnum = UC_ERR_OK; @@ -516,13 +521,13 @@ uc_err uc_emu_stop(ucengine *uc) static int _hook_code(ucengine *uc, int type, uint64_t begin, uint64_t end, - void *callback, void *user_data, uc_hook_h *hh) + void *callback, void *user_data, uchook *hh) { int i; i = hook_add(uc, type, begin, end, callback, user_data); if (i == 0) - return UC_ERR_OOM; // FIXME + return UC_ERR_NOMEM; // FIXME *hh = i; @@ -532,13 +537,13 @@ static int _hook_code(ucengine *uc, int type, uint64_t begin, uint64_t end, static uc_err _hook_mem_access(ucengine *uc, uc_hook_t type, uint64_t begin, uint64_t end, - void *callback, void *user_data, uc_hook_h *hh) + void *callback, void *user_data, uchook *hh) { int i; i = hook_add(uc, type, begin, end, callback, user_data); if (i == 0) - return UC_ERR_OOM; // FIXME + return UC_ERR_NOMEM; // FIXME *hh = i; @@ -552,24 +557,25 @@ uc_err uc_mem_map(ucengine *uc, 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 4KB - if ((address & (4*1024 - 1)) != 0) - return UC_ERR_MAP; + // address must be aligned to uc->target_page_size + if ((address & uc->target_page_align) != 0) + return UC_ERR_INVAL; - // size must be multiple of 4KB - if ((size & (4*1024 - 1)) != 0) - return UC_ERR_MAP; + // size must be multiple of uc->target_page_size + if ((size & uc->target_page_align) != 0) + return UC_ERR_INVAL; // check for only valid permissions - if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE)) != 0) - return UC_ERR_MAP; + if ((perms & ~UC_PROT_ALL) != 0) + 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_OOM; + return UC_ERR_NOMEM; } uc->mapped_blocks = regions; } @@ -579,7 +585,214 @@ uc_err uc_mem_map(ucengine *uc, uint64_t address, size_t size, uint32_t perms) return UC_ERR_OK; } -MemoryRegion *memory_mapping(ucengine* uc, uint64_t address) +// Create a backup copy of the indicated MemoryRegion. +// Generally used in prepartion for splitting a MemoryRegion. +static uint8_t *copy_region(struct uc_struct *uc, MemoryRegion *mr) +{ + uint8_t *block = (uint8_t *)malloc(int128_get64(mr->size)); + if (block != NULL) { + uc_err err = uc_mem_read(uc, 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. + + 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(struct uc_struct *uc, 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 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 + return true; + + if (address >= mr->end || chunk_end <= mr->addr) + // impossible case + return false; + + backup = copy_region(uc, 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; + + // unmap this region first, then do split it later + if (uc_mem_unmap(uc, 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(uc, begin, l_size, perms) != UC_ERR_OK) + goto error; + if (uc_mem_write(uc, begin, backup, l_size) != UC_ERR_OK) + goto error; + } + + if (m_size > 0 && !do_delete) { + if (uc_mem_map(uc, address, m_size, perms) != UC_ERR_OK) + goto error; + if (uc_mem_write(uc, address, backup + l_size, m_size) != UC_ERR_OK) + goto error; + } + + if (r_size > 0) { + if (uc_mem_map(uc, chunk_end, r_size, perms) != UC_ERR_OK) + goto error; + if (uc_mem_write(uc, 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(struct uc_struct *uc, uint64_t address, size_t size, uint32_t perms) +{ + MemoryRegion *mr; + uint64_t addr = address; + size_t count, len; + + if (size == 0) + // 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_INVAL; + + // size must be multiple of uc->target_page_size + if ((size & uc->target_page_align) != 0) + return UC_ERR_INVAL; + + // 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; + + // 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(uc, mr, addr, len, false)) + return UC_ERR_NOMEM; + + mr = memory_mapping(uc, addr); + mr->perms = perms; + uc->readonly_mem(mr, (perms & UC_PROT_WRITE) == 0); + + count += len; + addr += len; + } + return UC_ERR_OK; +} + +UNICORN_EXPORT +uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size) +{ + MemoryRegion *mr; + uint64_t addr; + size_t count, len; + + if (size == 0) + // nothing to unmap + return UC_ERR_OK; + + // address must be aligned to uc->target_page_size + if ((address & uc->target_page_align) != 0) + return UC_ERR_INVAL; + + // 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 + if (!check_mem_area(uc, address, size)) + return UC_ERR_NOMEM; + + // 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(uc, mr, addr, len, true)) + return UC_ERR_NOMEM; + // if we can retrieve the mapping, then no splitting took place + // so unmap here + mr = memory_mapping(uc, addr); + if (mr != NULL) + uc->memory_unmap(uc, mr); + count += len; + addr += len; + } + return UC_ERR_OK; +} + +MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) { unsigned int i; @@ -593,7 +806,7 @@ MemoryRegion *memory_mapping(ucengine* uc, uint64_t address) } static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback, - void *user_data, uc_hook_h *evh) + void *user_data, uchook *evh) { size_t i; @@ -607,12 +820,12 @@ 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; } static uc_err _hook_intr(struct uc_struct* uc, void *callback, - void *user_data, uc_hook_h *evh) + void *user_data, uchook *evh) { size_t i; @@ -626,12 +839,12 @@ 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; } static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callback, - void *user_data, uc_hook_h *evh) + void *user_data, uchook *evh) { size_t i; @@ -650,7 +863,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); @@ -661,7 +874,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 @@ -673,7 +886,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; } @@ -682,7 +895,7 @@ static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callb } UNICORN_EXPORT -uc_err uc_hook_add(ucengine *uc, uc_hook_h *hh, uc_hook_t type, void *callback, void *user_data, ...) +uc_err uc_hook_add(ucengine *uc, uchook *hh, uc_hook_t type, void *callback, void *user_data, ...) { va_list valist; int ret = UC_ERR_OK; @@ -738,7 +951,7 @@ uc_err uc_hook_add(ucengine *uc, uc_hook_h *hh, uc_hook_t type, void *callback, } UNICORN_EXPORT -uc_err uc_hook_del(ucengine *uc, uc_hook_h hh) +uc_err uc_hook_del(ucengine *uc, uchook hh) { return hook_del(uc, hh); }