diff --git a/bindings/go/unicorn/unicorn_const.go b/bindings/go/unicorn/unicorn_const.go index 20c42bc9..8bd35349 100644 --- a/bindings/go/unicorn/unicorn_const.go +++ b/bindings/go/unicorn/unicorn_const.go @@ -48,26 +48,33 @@ const ( ERR_MAP = 12 ERR_WRITE_PROT = 13 ERR_READ_PROT = 14 - ERR_EXEC_PROT = 15 + ERR_FETCH_PROT = 15 ERR_ARG = 16 ERR_READ_UNALIGNED = 17 ERR_WRITE_UNALIGNED = 18 ERR_FETCH_UNALIGNED = 19 MEM_READ = 16 MEM_WRITE = 17 - MEM_READ_WRITE = 18 - MEM_FETCH = 19 - MEM_WRITE_PROT = 20 - MEM_READ_PROT = 21 - MEM_EXEC_PROT = 22 - HOOK_INTR = 32 - HOOK_INSN = 33 - HOOK_CODE = 34 - HOOK_BLOCK = 35 - HOOK_MEM_INVALID = 36 - HOOK_MEM_READ = 37 - HOOK_MEM_WRITE = 38 - HOOK_MEM_READ_WRITE = 39 + MEM_FETCH = 18 + MEM_READ_INVALID = 19 + MEM_WRITE_INVALID = 20 + MEM_FETCH_INVALID = 21 + MEM_WRITE_PROT = 22 + MEM_READ_PROT = 23 + MEM_FETCH_PROT = 24 + HOOK_INTR = 1 + HOOK_INSN = 2 + HOOK_CODE = 4 + HOOK_BLOCK = 8 + HOOK_MEM_READ_INVALID = 16 + HOOK_MEM_WRITE_INVALID = 32 + HOOK_MEM_FETCH_INVALID = 64 + HOOK_MEM_READ_PROT = 128 + HOOK_MEM_WRITE_PROT = 256 + HOOK_MEM_FETCH_PROT = 512 + HOOK_MEM_READ = 1024 + HOOK_MEM_WRITE = 2048 + HOOK_MEM_FETCH = 4096 PROT_NONE = 0 PROT_READ = 1 diff --git a/bindings/python/sample_x86.py b/bindings/python/sample_x86.py index 24b82450..151ed73c 100755 --- a/bindings/python/sample_x86.py +++ b/bindings/python/sample_x86.py @@ -33,7 +33,7 @@ def hook_code(uc, address, size, user_data): # callback for tracing invalid memory access (READ or WRITE) def hook_mem_invalid(uc, access, address, size, value, user_data): - if access == UC_MEM_WRITE: + if access == UC_MEM_WRITE_INVALID: print(">>> Missing memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" \ %(address, size, value)) # map this memory in with 2MB in size @@ -231,7 +231,7 @@ def test_i386_invalid_mem_write(): #mu.hook_add(UC_HOOK_CODE, hook_code) # intercept invalid memory events - mu.hook_add(UC_HOOK_MEM_INVALID, hook_mem_invalid) + mu.hook_add(UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID, hook_mem_invalid) try: # emulate machine code in infinite time @@ -349,7 +349,7 @@ def test_x86_64(): mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_access) mu.hook_add(UC_HOOK_MEM_READ, hook_mem_access) # actually you can also use READ_WRITE to trace all memory access - #mu.hook_add(UC_HOOK_MEM_READ_WRITE, hook_mem_access) + #mu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access) try: # emulate machine code in infinite time diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index f5e1c63e..dd67306a 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -272,11 +272,13 @@ class Uc(object): cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, cb, \ ctypes.cast(self._callback_count, ctypes.c_void_p), begin, end) - elif htype == UC_HOOK_MEM_INVALID: + elif htype & UC_HOOK_MEM_READ_INVALID or htype & UC_HOOK_MEM_WRITE_INVALID or \ + htype & UC_HOOK_MEM_FETCH_INVALID or htype & UC_HOOK_MEM_READ_PROT or \ + htype & UC_HOOK_MEM_WRITE_PROT or htype & UC_HOOK_MEM_FETCH_PROT: cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) - elif htype in (UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ_WRITE): + elif htype in (UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE): cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB) status = _uc.uc_hook_add(self._uch, ctypes.byref(_h2), htype, \ cb, ctypes.cast(self._callback_count, ctypes.c_void_p)) diff --git a/bindings/python/unicorn/unicorn_const.py b/bindings/python/unicorn/unicorn_const.py index ec0896a6..467cdd29 100644 --- a/bindings/python/unicorn/unicorn_const.py +++ b/bindings/python/unicorn/unicorn_const.py @@ -46,7 +46,7 @@ UC_ERR_INSN_INVALID = 11 UC_ERR_MAP = 12 UC_ERR_WRITE_PROT = 13 UC_ERR_READ_PROT = 14 -UC_ERR_EXEC_PROT = 15 +UC_ERR_FETCH_PROT = 15 UC_ERR_ARG = 16 UC_ERR_READ_UNALIGNED = 17 UC_ERR_WRITE_UNALIGNED = 18 @@ -54,19 +54,25 @@ UC_ERR_FETCH_UNALIGNED = 19 UC_MEM_READ = 16 UC_MEM_WRITE = 17 UC_MEM_FETCH = 18 -UC_MEM_WRITE_PROT = 19 -UC_MEM_READ_PROT = 20 -UC_MEM_FETCH_PROT = 21 -UC_HOOK_INTR = 32 -UC_HOOK_INSN = 33 -UC_HOOK_CODE = 34 -UC_HOOK_BLOCK = 35 -UC_HOOK_MEM_INVALID_READ = 36 -UC_HOOK_MEM_INVALID_WRITE = 37 -UC_HOOK_MEM_INVALID_FETCH = 38 -UC_HOOK_MEM_READ = 39 -UC_HOOK_MEM_WRITE = 40 -UC_HOOK_MEM_FETCH = 41 +UC_MEM_READ_INVALID = 19 +UC_MEM_WRITE_INVALID = 20 +UC_MEM_FETCH_INVALID = 21 +UC_MEM_WRITE_PROT = 22 +UC_MEM_READ_PROT = 23 +UC_MEM_FETCH_PROT = 24 +UC_HOOK_INTR = 1 +UC_HOOK_INSN = 2 +UC_HOOK_CODE = 4 +UC_HOOK_BLOCK = 8 +UC_HOOK_MEM_READ_INVALID = 16 +UC_HOOK_MEM_WRITE_INVALID = 32 +UC_HOOK_MEM_FETCH_INVALID = 64 +UC_HOOK_MEM_READ_PROT = 128 +UC_HOOK_MEM_WRITE_PROT = 256 +UC_HOOK_MEM_FETCH_PROT = 512 +UC_HOOK_MEM_READ = 1024 +UC_HOOK_MEM_WRITE = 2048 +UC_HOOK_MEM_FETCH = 4096 UC_PROT_NONE = 0 UC_PROT_READ = 1 diff --git a/hook.c b/hook.c index c201c315..dbc61a78 100644 --- a/hook.c +++ b/hook.c @@ -73,7 +73,7 @@ size_t hook_add(struct uc_struct *uc, int type, uint64_t begin, uint64_t end, vo if (begin > end) uc->hook_write_idx = i; break; - case UC_HOOK_MEM_READ_WRITE: + case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: uc->hook_mem_read = true; uc->hook_mem_write = true; if (begin > end) { @@ -109,8 +109,28 @@ uc_err hook_del(struct uc_struct *uc, uc_hook hh) uc->hook_write_idx = 0; } - if (hh == uc->hook_mem_idx) { - uc->hook_mem_idx = 0; + if (hh == uc->hook_mem_read_idx) { + uc->hook_mem_read_idx = 0; + } + + if (hh == uc->hook_mem_write_idx) { + uc->hook_mem_write_idx = 0; + } + + if (hh == uc->hook_mem_fetch_idx) { + uc->hook_mem_fetch_idx = 0; + } + + if (hh == uc->hook_mem_read_prot_idx) { + uc->hook_mem_read_prot_idx = 0; + } + + if (hh == uc->hook_mem_write_prot_idx) { + uc->hook_mem_write_prot_idx = 0; + } + + if (hh == uc->hook_mem_fetch_prot_idx) { + uc->hook_mem_fetch_prot_idx = 0; } if (hh == uc->hook_intr_idx) { @@ -176,13 +196,13 @@ static struct hook_struct *_hook_find(struct uc_struct *uc, int type, uint64_t a } break; case UC_HOOK_MEM_READ: - if (uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_READ || uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_READ_WRITE) { + if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_READ) { if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) return &uc->hook_callbacks[i]; } break; case UC_HOOK_MEM_WRITE: - if (uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_WRITE || uc->hook_callbacks[i].hook_type == UC_HOOK_MEM_READ_WRITE) { + if (uc->hook_callbacks[i].hook_type & UC_HOOK_MEM_WRITE) { if (uc->hook_callbacks[i].begin <= address && address <= uc->hook_callbacks[i].end) return &uc->hook_callbacks[i]; } diff --git a/include/uc_priv.h b/include/uc_priv.h index c196291a..1f19a58a 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -152,7 +152,13 @@ struct uc_struct { bool hook_block, hook_insn, hook_mem_read, hook_mem_write; uint64_t block_addr; // save the last block address we hooked // indexes to event callbacks - int hook_mem_idx; // for handling invalid memory access + int hook_mem_read_idx; // for handling invalid memory read access on unmapped memory + int hook_mem_write_idx; // for handling invalid memory write access on unmapped memory + int hook_mem_fetch_idx; // for handling invalid memory fetch access on unmapped memory + int hook_mem_read_prot_idx; // for handling invalid memory read access on read-protected memory + int hook_mem_write_prot_idx; // for handling invalid memory write access on write-protected memory + int hook_mem_fetch_prot_idx; // for handling invalid memory fetch access on non-executable memory + int hook_intr_idx; // for handling interrupt int hook_out_idx; // for handling OUT instruction (X86) int hook_in_idx; // for handling IN instruction (X86) diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 4947d199..21661f0c 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -120,7 +120,7 @@ typedef enum uc_err { UC_ERR_MAP, // Invalid memory mapping: uc_mem_map() UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start() UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start() - UC_ERR_EXEC_PROT, // Quit emulation due to UC_MEM_EXEC_PROT violation: uc_emu_start() + UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start() UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API) UC_ERR_READ_UNALIGNED, // Unaligned read UC_ERR_WRITE_UNALIGNED, // Unaligned write @@ -153,9 +153,12 @@ typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, uint32_ // All type of memory accesses for UC_HOOK_MEM_* typedef enum uc_mem_type { - UC_MEM_READ = 16, // Unmapped memory is read from - UC_MEM_WRITE, // Unmapped memory is written to - UC_MEM_FETCH, // Unmapped memory is fetched + UC_MEM_READ = 16, // Memory is read from + UC_MEM_WRITE, // Memory is written to + UC_MEM_FETCH, // Memory is fetched + UC_MEM_READ_INVALID, // Unmapped memory is read from + UC_MEM_WRITE_INVALID, // Unmapped memory is written to + UC_MEM_FETCH_INVALID, // Unmapped memory is fetched UC_MEM_WRITE_PROT, // Write to write protected, but mapped, memory UC_MEM_READ_PROT, // Read from read protected, but mapped, memory UC_MEM_FETCH_PROT, // Fetch from non-executable, but mapped, memory @@ -163,16 +166,19 @@ typedef enum uc_mem_type { // All type of hooks for uc_hook_add() API. typedef enum uc_hook_type { - UC_HOOK_INTR = 32, // Hook all interrupt events - UC_HOOK_INSN, // Hook a particular instruction - UC_HOOK_CODE, // Hook a range of code - UC_HOOK_BLOCK, // Hook basic blocks - UC_HOOK_MEM_READ_INVALID, // Hook for invalid memory read events - UC_HOOK_MEM_WRITE_INVALID, // Hook for invalid memory write events - UC_HOOK_MEM_FETCH_INVALID, // Hook for invalid memory fetch for execution events - UC_HOOK_MEM_READ, // Hook all memory read events. - UC_HOOK_MEM_WRITE, // Hook all memory write events. - UC_HOOK_MEM_FETCH, // Hook all memory fetch for execution events + UC_HOOK_INTR = 1 << 0, // Hook all interrupt events + UC_HOOK_INSN = 1 << 1, // Hook a particular instruction + UC_HOOK_CODE = 1 << 2, // Hook a range of code + UC_HOOK_BLOCK = 1 << 3, // Hook basic blocks + UC_HOOK_MEM_READ_INVALID = 1 << 4, // Hook for invalid memory read events + UC_HOOK_MEM_WRITE_INVALID = 1 << 5, // Hook for invalid memory write events + UC_HOOK_MEM_FETCH_INVALID = 1 << 6, // Hook for invalid memory fetch for execution events + UC_HOOK_MEM_READ_PROT = 1 << 7, // Hook for memory read on read-protected memory + UC_HOOK_MEM_WRITE_PROT = 1 << 8, // Hook for memory write on write-protected memory + UC_HOOK_MEM_FETCH_PROT = 1 << 9, // Hook for memory fetch on non-executable memory + UC_HOOK_MEM_READ = 1 << 10, // Hook memory read events. + UC_HOOK_MEM_WRITE = 1 << 11, // Hook memory write events. + UC_HOOK_MEM_FETCH = 1 << 12, // Hook memory fetch for execution events } uc_hook_type; // Callback function for hooking memory (UC_HOOK_MEM_*) @@ -381,7 +387,7 @@ uc_err uc_emu_stop(uc_engine *uc); for detailed error). */ UNICORN_EXPORT -uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback, void *user_data, ...); +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...); /* Unregister (remove) a hook callback. diff --git a/qemu/cputlb.c b/qemu/cputlb.c index 30a0727b..b9b16e30 100644 --- a/qemu/cputlb.c +++ b/qemu/cputlb.c @@ -300,7 +300,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) (addr & TARGET_PAGE_MASK))) { cpu_ldub_code(env1, addr); //check for NX related error from softmmu - if (env1->invalid_error == UC_ERR_EXEC_PROT) { + if (env1->invalid_error == UC_ERR_FETCH_PROT) { env1->invalid_error = UC_ERR_CODE_INVALID; return -1; } diff --git a/qemu/softmmu_template.h b/qemu/softmmu_template.h index 9e8afd46..9730c840 100644 --- a/qemu/softmmu_template.h +++ b/qemu/softmmu_template.h @@ -177,7 +177,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; uintptr_t haddr; DATA_TYPE res; - int mem_access, error_code; + int error_code; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); @@ -185,15 +185,16 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // memory can be unmapped while reading or fetching if (mr == NULL) { #if defined(SOFTMMU_CODE_ACCESS) - mem_access = UC_MEM_FETCH; error_code = UC_ERR_FETCH_INVALID; + if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( + uc, UC_MEM_FETCH_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { #else - mem_access = UC_MEM_READ; error_code = UC_ERR_READ_INVALID; + if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( + uc, UC_MEM_READ_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { #endif - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, mem_access, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -208,13 +209,13 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #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)) { + if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( + uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_EXEC_PROT; + env->invalid_error = UC_ERR_FETCH_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -233,9 +234,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && 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)( + if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { @@ -367,7 +368,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; uintptr_t haddr; DATA_TYPE res; - int mem_access, error_code; + int error_code; struct uc_struct *uc = env->uc; MemoryRegion *mr = memory_mapping(uc, addr); @@ -375,15 +376,16 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // memory can be unmapped while reading or fetching if (mr == NULL) { #if defined(SOFTMMU_CODE_ACCESS) - mem_access = UC_MEM_FETCH; error_code = UC_ERR_FETCH_INVALID; + if (uc->hook_mem_fetch_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_idx].callback)( + uc, UC_MEM_FETCH_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_idx].user_data)) { #else - mem_access = UC_MEM_READ; error_code = UC_ERR_READ_INVALID; + if (uc->hook_mem_read_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_idx].callback)( + uc, UC_MEM_READ_INVALID, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_read_idx].user_data)) { #endif - if (uc->hook_mem_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, mem_access, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { env->invalid_error = UC_ERR_OK; mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } else { @@ -398,13 +400,13 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, #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)) { + if (uc->hook_mem_fetch_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].callback)( + uc, UC_MEM_FETCH_PROT, addr, DATA_SIZE, 0, + uc->hook_callbacks[uc->hook_mem_fetch_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; - env->invalid_error = UC_ERR_EXEC_PROT; + env->invalid_error = UC_ERR_FETCH_PROT; // printf("***** Invalid fetch (non-executable) at " TARGET_FMT_lx "\n", addr); cpu_exit(uc->current_cpu); return 0; @@ -423,9 +425,9 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, int mmu_idx, // Unicorn: callback on non-readable memory if (READ_ACCESS_TYPE == MMU_DATA_LOAD && 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)( + if (uc->hook_mem_read_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_read_prot_idx].callback)( uc, UC_MEM_READ_PROT, addr, DATA_SIZE, 0, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_read_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { env->invalid_addr = addr; @@ -608,10 +610,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } // Unicorn: callback on invalid memory - if (uc->hook_mem_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + if (uc->hook_mem_write_idx && mr == NULL) { + if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( + uc, UC_MEM_WRITE_INVALID, addr, DATA_SIZE, (int64_t)val, + uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_INVALID; @@ -620,14 +622,15 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } else { env->invalid_error = UC_ERR_OK; + mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } } // 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)( + if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { @@ -754,10 +757,10 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } // Unicorn: callback on invalid memory - if (uc->hook_mem_idx && mr == NULL) { - if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_idx].callback)( - uc, UC_MEM_WRITE, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + if (uc->hook_mem_write_idx && mr == NULL) { + if (!((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_idx].callback)( + uc, UC_MEM_WRITE_INVALID, addr, DATA_SIZE, (int64_t)val, + uc->hook_callbacks[uc->hook_mem_write_idx].user_data)) { // save error & quit env->invalid_addr = addr; env->invalid_error = UC_ERR_WRITE_INVALID; @@ -766,14 +769,15 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } else { env->invalid_error = UC_ERR_OK; + mr = memory_mapping(uc, addr); // FIXME: what if mr is still NULL at this time? } } // 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)( + if (uc->hook_mem_write_prot_idx != 0 && ((uc_cb_eventmem_t)uc->hook_callbacks[uc->hook_mem_write_prot_idx].callback)( uc, UC_MEM_WRITE_PROT, addr, DATA_SIZE, (int64_t)val, - uc->hook_callbacks[uc->hook_mem_idx].user_data)) { + uc->hook_callbacks[uc->hook_mem_write_prot_idx].user_data)) { env->invalid_error = UC_ERR_OK; } else { diff --git a/samples/mem_apis.c b/samples/mem_apis.c index 6237c578..fea1a834 100644 --- a/samples/mem_apis.c +++ b/samples/mem_apis.c @@ -79,13 +79,13 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, default: printf("not ok - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", type, addr); return false; - case UC_MEM_READ: + case UC_MEM_READ_INVALID: printf("not ok - Read from invalid memory at 0x%"PRIx64 ", data size = %u\n", addr, size); return false; - case UC_MEM_WRITE: + case UC_MEM_WRITE_INVALID: printf("not ok - Write to invalid memory at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", addr, size, value); return false; - case UC_MEM_EXEC_PROT: + case UC_MEM_FETCH_PROT: printf("not ok - Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); return false; case UC_MEM_WRITE_PROT: @@ -147,7 +147,9 @@ static void do_nx_demo(bool cause_fault) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + uc_hook_add(uc, &trace1, + UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_FETCH_INVALID | UC_HOOK_MEM_FETCH_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_READ_PROT, + hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -226,7 +228,9 @@ static void do_perms_demo(bool change_perms) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + uc_hook_add(uc, &trace1, + UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_FETCH_INVALID | UC_HOOK_MEM_FETCH_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_READ_PROT, + hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } @@ -302,7 +306,9 @@ static void do_unmap_demo(bool do_unmap) // intercept code and invalid memory events if (uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0) != UC_ERR_OK || - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + uc_hook_add(uc, &trace1, + UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_FETCH_INVALID | UC_HOOK_MEM_FETCH_PROT | UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_READ_PROT, + hook_mem_invalid, NULL) != UC_ERR_OK) { printf("not ok - Failed to install hooks\n"); return; } diff --git a/samples/sample_x86.c b/samples/sample_x86.c index c6662dfe..f2a28916 100644 --- a/samples/sample_x86.c +++ b/samples/sample_x86.c @@ -73,7 +73,7 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, default: // return false to indicate we want to stop emulation return false; - case UC_MEM_WRITE: + case UC_MEM_WRITE_INVALID: printf(">>> Missing memory is being WRITE at 0x%"PRIx64 ", data size = %u, data value = 0x%"PRIx64 "\n", address, size, value); // map this memory in with 2MB in size @@ -421,7 +421,7 @@ static void test_i386_invalid_mem_write(void) uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)1, (uint64_t)0); // intercept invalid memory events - uc_hook_add(uc, &trace3, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace3, UC_HOOK_MEM_READ_INVALID | UC_HOOK_MEM_WRITE_INVALID, hook_mem_invalid, NULL); // emulate machine code in infinite time err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_MEM_WRITE) - 1, 0, 0); diff --git a/tests/regress/Makefile b/tests/regress/Makefile index 09214579..e03c27e9 100644 --- a/tests/regress/Makefile +++ b/tests/regress/Makefile @@ -1,5 +1,5 @@ CFLAGS += -I../include -LDFLAGS += ../libunicorn.a $(shell pkg-config --libs glib-2.0) -lpthread -lm +LDFLAGS += ../../libunicorn.a $(shell pkg-config --libs glib-2.0) -lpthread -lm TESTS = map_crash map_write TESTS += sigill sigill2 diff --git a/tests/regress/mem_exec.c b/tests/regress/mem_exec.c index 28f7d7a0..db9a2bc1 100644 --- a/tests/regress/mem_exec.c +++ b/tests/regress/mem_exec.c @@ -116,9 +116,9 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, { switch(type) { default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + printf("not ok %d - memory invalid type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); return false; - case UC_MEM_EXEC_PROT: + case UC_MEM_FETCH_PROT: printf("# Fetch from non-executable memory at 0x%"PRIx64 "\n", addr); //make page executable @@ -221,11 +221,11 @@ int main(int argc, char **argv, char **envp) } // 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++); + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_PROT | UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install memory invalid handler\n", log_num++); return 8; } else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + printf("ok %d - memory invalid handler installed\n", log_num++); } // emulate machine code until told to stop by hook_code diff --git a/tests/regress/mem_protect.c b/tests/regress/mem_protect.c index a9dcce9d..d29dc490 100644 --- a/tests/regress/mem_protect.c +++ b/tests/regress/mem_protect.c @@ -138,7 +138,7 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint32_t testval; switch(type) { default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + printf("not ok %d - memory 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); @@ -229,11 +229,11 @@ int main(int argc, char **argv, char **envp) } // 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++); + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_PROT, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install memory invalid handler\n", log_num++); return 7; } else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + printf("ok %d - memory invalid handler installed\n", log_num++); } // emulate machine code until told to stop by hook_code diff --git a/tests/regress/mem_unmap.c b/tests/regress/mem_unmap.c index 435d4665..06223bfa 100644 --- a/tests/regress/mem_unmap.c +++ b/tests/regress/mem_unmap.c @@ -133,7 +133,7 @@ static bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint32_t testval; switch(type) { default: - printf("not ok %d - UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", log_num++, type, addr); + printf("not ok %d - memory 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); @@ -224,11 +224,11 @@ int main(int argc, char **argv, char **envp) } // 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++); + if (uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_INVALID, hook_mem_invalid, NULL) != UC_ERR_OK) { + printf("not ok %d - Failed to install memory invalid handler\n", log_num++); return 7; } else { - printf("ok %d - UC_HOOK_MEM_INVALID installed\n", log_num++); + printf("ok %d - memory invalid handler installed\n", log_num++); } // emulate machine code until told to stop by hook_code diff --git a/tests/regress/nr_mem_test.c b/tests/regress/nr_mem_test.c index 3fb1f0f4..60e97db7 100644 --- a/tests/regress/nr_mem_test.c +++ b/tests/regress/nr_mem_test.c @@ -86,7 +86,7 @@ int main(int argc, char **argv, char **envp) //uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff); // intercept invalid memory events - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace1, UC_MEM_READ_PROT, hook_mem_invalid, NULL); // emulate machine code in infinite time printf("BEGIN execution\n"); diff --git a/tests/regress/reg_write_sign_extension.py b/tests/regress/reg_write_sign_extension.py index e2838da2..34d65506 100755 --- a/tests/regress/reg_write_sign_extension.py +++ b/tests/regress/reg_write_sign_extension.py @@ -24,7 +24,7 @@ class RegWriteSignExt(regress.RegressTest): # jmp ebx mu.mem_write(0x10000000, b'\xff\xe3') - mu.hook_add(unicorn.UC_HOOK_MEM_INVALID, hook_mem_invalid) + mu.hook_add(unicorn.UC_HOOK_MEM_FETCH_INVALID | unicorn.UC_HOOK_MEM_FETCH_PROT, hook_mem_invalid) mu.emu_start(0x10000000, 0x10000000 + 2, count=1) if __name__ == '__main__': diff --git a/tests/regress/regress.sh b/tests/regress/regress.sh new file mode 100755 index 00000000..f0b79900 --- /dev/null +++ b/tests/regress/regress.sh @@ -0,0 +1,13 @@ +#!/bin/sh + + +./map_crash map_write +./sigill sigill2 +./block_test +./ro_mem_test nr_mem_test +./timeout_segfault +./rep_movsb +./mem_unmap +./mem_protect +./mem_exec + diff --git a/tests/regress/ro_mem_test.c b/tests/regress/ro_mem_test.c index 02e13bac..1e612033 100644 --- a/tests/regress/ro_mem_test.c +++ b/tests/regress/ro_mem_test.c @@ -142,7 +142,7 @@ int main(int argc, char **argv, char **envp) //uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, (uint64_t)0x400000, (uint64_t)0x400fff); // intercept invalid memory events - uc_hook_add(uc, &trace1, UC_HOOK_MEM_INVALID, hook_mem_invalid, NULL); + uc_hook_add(uc, &trace1, UC_HOOK_MEM_WRITE_INVALID | UC_HOOK_MEM_WRITE_PROT, hook_mem_invalid, NULL); // emulate machine code in infinite time printf("BEGIN execution - 1\n"); diff --git a/tests/unit/Makefile b/tests/unit/Makefile index 499d1a5f..ae66d08e 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -11,7 +11,7 @@ all: ${ALL_TESTS} .PHONY: clean clean: - rm ${ALL_TESTS} + rm -rf ${ALL_TESTS} .PHONY: test test: export LD_LIBRARY_PATH=../../ diff --git a/uc.c b/uc.c index 2d97b7c9..d5219542 100644 --- a/uc.c +++ b/uc.c @@ -87,8 +87,8 @@ const char *uc_strerror(uc_err code) 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_FETCH_PROT: + return "Fetch from non-executable memory (UC_ERR_FETCH_PROT)"; case UC_ERR_ARG: return "Invalid argumet (UC_ERR_ARG)"; @@ -830,7 +830,7 @@ MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address) return NULL; } -static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback, +static uc_err _hook_mem_invalid(struct uc_struct* uc, int type, uc_cb_eventmem_t callback, void *user_data, uc_hook *evh) { size_t i; @@ -842,7 +842,18 @@ static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback, uc->hook_callbacks[i].callback = callback; uc->hook_callbacks[i].user_data = user_data; *evh = i; - uc->hook_mem_idx = i; + if (type & UC_HOOK_MEM_READ_INVALID) + uc->hook_mem_read_idx = i; + if (type & UC_HOOK_MEM_READ_PROT) + uc->hook_mem_read_prot_idx = i; + if (type & UC_HOOK_MEM_WRITE_INVALID) + uc->hook_mem_write_idx = i; + if (type & UC_HOOK_MEM_WRITE_PROT) + uc->hook_mem_write_prot_idx = i; + if (type & UC_HOOK_MEM_FETCH_INVALID) + uc->hook_mem_fetch_idx = i; + if (type & UC_HOOK_MEM_FETCH_PROT) + uc->hook_mem_fetch_prot_idx = i; return UC_ERR_OK; } else return UC_ERR_NOMEM; @@ -920,7 +931,7 @@ static uc_err _hook_insn(struct uc_struct *uc, unsigned int insn_id, void *callb } UNICORN_EXPORT -uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback, void *user_data, ...) +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, void *user_data, ...) { va_list valist; int ret = UC_ERR_OK; @@ -929,9 +940,26 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback va_start(valist, user_data); + if (type & UC_HOOK_MEM_READ_INVALID) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_INVALID, callback, user_data, hh); + + if (type & UC_HOOK_MEM_WRITE_INVALID) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_INVALID, callback, user_data, hh); + + if (type & UC_HOOK_MEM_FETCH_INVALID) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_INVALID, callback, user_data, hh); + + if (type & UC_HOOK_MEM_READ_PROT) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_READ_PROT, callback, user_data, hh); + + if (type & UC_HOOK_MEM_WRITE_PROT) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_WRITE_PROT, callback, user_data, hh); + + if (type & UC_HOOK_MEM_FETCH_PROT) + ret = _hook_mem_invalid(uc, UC_HOOK_MEM_FETCH_PROT, callback, user_data, hh); + switch(type) { default: - ret = UC_ERR_HOOK; break; case UC_HOOK_INTR: ret = _hook_intr(uc, callback, user_data, hh); @@ -950,9 +978,6 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback end = va_arg(valist, uint64_t); ret = _hook_code(uc, UC_HOOK_BLOCK, begin, end, callback, user_data, hh); break; - case UC_HOOK_MEM_INVALID: - ret = _hook_mem_invalid(uc, callback, user_data, hh); - break; case UC_HOOK_MEM_READ: begin = va_arg(valist, uint64_t); end = va_arg(valist, uint64_t); @@ -963,10 +988,10 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, uc_hook_type type, void *callback end = va_arg(valist, uint64_t); ret = _hook_mem_access(uc, UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); break; - case UC_HOOK_MEM_READ_WRITE: + case UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE: begin = va_arg(valist, uint64_t); end = va_arg(valist, uint64_t); - ret = _hook_mem_access(uc, UC_HOOK_MEM_READ_WRITE, begin, end, callback, user_data, hh); + ret = _hook_mem_access(uc, UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, begin, end, callback, user_data, hh); break; }