From d1496f370071441db703c3310f7bc7ae303251e4 Mon Sep 17 00:00:00 2001 From: smeng9 <38666763+smeng9@users.noreply.github.com> Date: Tue, 27 Oct 2020 20:47:59 -0500 Subject: [PATCH 1/7] Update binding list (#1349) --- bindings/README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bindings/README b/bindings/README index 24dd2c18..4df67f62 100644 --- a/bindings/README +++ b/bindings/README @@ -33,3 +33,6 @@ More bindings created & maintained externally by community are available as foll - pharo-unicorn: Pharo binding (by Guille Polito) https://github.com/guillep/pharo-unicorn + +- Unicorn.js: JavaScript binding (by Alexandro Sanchez) + https://github.com/AlexAltea/unicorn.js From 00b4a17c1bafe60f620fad9a24ec56c3eb0568c3 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Thu, 29 Oct 2020 04:16:45 +0100 Subject: [PATCH 2/7] Fix some typos in include files (#1350) --- bindings/pascal/unicorn/Unicorn_dyn.pas | 2 +- include/unicorn/unicorn.h | 34 ++++++++++++------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bindings/pascal/unicorn/Unicorn_dyn.pas b/bindings/pascal/unicorn/Unicorn_dyn.pas index 7db6bae7..427813cf 100755 --- a/bindings/pascal/unicorn/Unicorn_dyn.pas +++ b/bindings/pascal/unicorn/Unicorn_dyn.pas @@ -108,7 +108,7 @@ type @user_data: user data passed to tracing APIs @return: return true to continue, or false to stop program (due to invalid memory). - NOTE: returning true to continue execution will only work if if the accessed + NOTE: returning true to continue execution will only work if the accessed memory is made accessible with the correct permissions during the hook. In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback, diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index d7780e56..239ff064 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -327,7 +327,7 @@ typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type, @user_data: user data passed to tracing APIs @return: return true to continue, or false to stop program (due to invalid memory). - NOTE: returning true to continue execution will only work if if the accessed + NOTE: returning true to continue execution will only work if the accessed memory is made accessible with the correct permissions during the hook. In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback, @@ -440,7 +440,7 @@ UNICORN_EXPORT uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result); /* - Report the last error number when some API function fail. + Report the last error number when some API function fails. Like glibc's errno, uc_errno might not retain its old value once accessed. @uc: handle returned by uc_open() @@ -552,7 +552,7 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size); @uc: handle returned by uc_open() @begin: address where emulation starts - @until: address where emulation stops (i.e when this address is hit) + @until: address where emulation stops (i.e. when this address is hit) @timeout: duration to emulate the code (in microseconds). When this value is 0, we will emulate the code in infinite time, until the code is finished. @count: the number of instructions to be emulated. When this value is 0, @@ -586,12 +586,12 @@ uc_err uc_emu_stop(uc_engine *uc); @uc: handle returned by uc_open() @hh: hook handle returned from this registration. To be used in uc_hook_del() API - @type: hook type + @type: hook type, refer to uc_hook_type enum @callback: callback to be run when instruction is hit @user_data: user-defined data. This will be passed to callback function in its last argument @user_data - @begin: start address of the area where the callback is effect (inclusive) - @end: end address of the area where the callback is effect (inclusive) + @begin: start address of the area where the callback is in effect (inclusive) + @end: end address of the area where the callback is in effect (inclusive) NOTE 1: the callback is called only if related address is in range [@begin, @end] NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered @...: variable arguments (depending on @type) @@ -609,7 +609,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, Unregister (remove) a hook callback. This API removes the hook callback registered by uc_hook_add(). NOTE: this should be called only when you no longer want to trace. - After this, @hh is invalid, and nolonger usable. + After this, @hh is invalid, and no longer usable. @uc: handle returned by uc_open() @hh: handle returned by uc_hook_add() @@ -636,7 +636,7 @@ typedef enum uc_prot { @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_ARG 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_ARG error. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error. @perms: Permissions for the newly mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, or this will return with UC_ERR_ARG error. @@ -655,12 +655,12 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); @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_ARG 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_ARG error. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error. @perms: Permissions for the newly mapped region. This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, or this will return with UC_ERR_ARG error. @ptr: pointer to host memory backing the newly mapped memory. This host memory is - expected to be an equal or larger size than provided, and be mapped with at + expected to be of equal or larger size than provided, and be mapped with at least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is undefined. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum @@ -700,7 +700,7 @@ uc_err uc_mmio_map(uc_engine *uc, uint64_t address, size_t size, @address: starting address of the memory region to be unmapped. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. @size: size of the memory region to be modified. - This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG error. @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). @@ -716,7 +716,7 @@ uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size); @address: starting address of the memory region to be modified. This address must be aligned to 4KB, or this will return with UC_ERR_ARG error. @size: size of the memory region to be modified. - This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG 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_ARG error. @@ -730,8 +730,8 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t per /* Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr() This API allocates memory for @regions, and user must free this memory later - by free() to avoid leaking memory. - NOTE: memory regions may be splitted by uc_mem_unmap() + by uc_free() to avoid leaking memory. + NOTE: memory regions may be split by uc_mem_unmap() @uc: handle returned by uc_open() @regions: pointer to an array of uc_mem_region struct. This is allocated by @@ -751,9 +751,9 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count); differing arches or modes. @uc: handle returned by uc_open() - @context: pointer to a uc_engine*. This will be updated with the pointer to + @context: pointer to a uc_context*. This will be updated with the pointer to the new context on successful return of this function. - Later, this allocated memory must be freed with uc_free(). + Later, this allocated memory must be freed with uc_context_free(). @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum for detailed error). @@ -764,7 +764,7 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context); /* Free the memory allocated by uc_mem_regions. WARNING: After Unicorn 1.0.1rc5, the memory allocated by uc_context_alloc should - be free-ed by uc_context_free(). Calling uc_free() may still work, but the result + be freed by uc_context_free(). Calling uc_free() may still work, but the result is **undefined**. @mem: memory allocated by uc_mem_regions (returned in *regions). From e2a924a32b6fd3f24163b93996ac91782f518a25 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 31 Dec 2020 10:53:36 +0000 Subject: [PATCH 3/7] qemu_getauxval FreeBSD implementation backport (#1366) --- qemu/util/getauxval.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qemu/util/getauxval.c b/qemu/util/getauxval.c index 36afdfb9..b124107d 100644 --- a/qemu/util/getauxval.c +++ b/qemu/util/getauxval.c @@ -98,6 +98,16 @@ unsigned long qemu_getauxval(unsigned long type) return 0; } +#elif defined(__FreeBSD__) +#include + +unsigned long qemu_getauxval(unsigned long type) +{ + unsigned long aux = 0; + elf_aux_info(type, &aux, sizeof(aux)); + return aux; +} + #else unsigned long qemu_getauxval(unsigned long type) From 5f40667d91aeacfbe7b0f394dcac6ab78e9e2cf4 Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Mon, 26 Apr 2021 00:31:29 +0800 Subject: [PATCH 4/7] Support querying architecture mode besides arm (#1389) --- qemu/target/arm/unicorn_arm.c | 2 +- uc.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qemu/target/arm/unicorn_arm.c b/qemu/target/arm/unicorn_arm.c index 9a5a698d..e582691f 100644 --- a/qemu/target/arm/unicorn_arm.c +++ b/qemu/target/arm/unicorn_arm.c @@ -456,7 +456,7 @@ static uc_err arm_query(struct uc_struct *uc, uc_query_type type, size_t *result // zero out ARM/THUMB mode mode = uc->mode & ~(UC_MODE_ARM | UC_MODE_THUMB); // THUMB mode or ARM MOde - mode += ((ARM_CPU(mycpu)->env.thumb != 0)? UC_MODE_THUMB : UC_MODE_ARM); + mode |= ((ARM_CPU(mycpu)->env.thumb != 0)? UC_MODE_THUMB : UC_MODE_ARM); *result = mode; return UC_ERR_OK; default: diff --git a/uc.c b/uc.c index 3e24674c..bc50b9ba 100644 --- a/uc.c +++ b/uc.c @@ -1444,7 +1444,8 @@ uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result) return uc->query(uc, type, result); } #endif - return UC_ERR_ARG; + *result = uc->mode; + break; case UC_QUERY_TIMEOUT: *result = uc->timed_out; From 47ecfc1b2c11523c7bfadc9d620b8cf6cd1e9cb6 Mon Sep 17 00:00:00 2001 From: insane-shane Date: Sun, 25 Apr 2021 12:35:56 -0400 Subject: [PATCH 5/7] Handle exceptions raised in Python hook functions (#1387) --- bindings/python/unicorn/unicorn.py | 34 ++++++++++++++++++++++ tests/regress/hook_raises_exception.py | 39 ++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/regress/hook_raises_exception.py diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 58c6ee06..19df82e0 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -3,6 +3,7 @@ import ctypes import ctypes.util import distutils.sysconfig +from functools import wraps import pkg_resources import inspect import os.path @@ -307,6 +308,27 @@ def reg_write(reg_write_func, arch, reg_id, value): return +def _catch_hook_exception(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + """Catches exceptions raised in hook functions. + + If an exception is raised, it is saved to the Uc object and a call to stop + emulation is issued. + """ + try: + return func(self, *args, **kwargs) + except Exception as e: + # If multiple hooks raise exceptions, just use the first one + if self._hook_exception is None: + self._hook_exception = e + + self.emu_stop() + + return wrapper + + + class uc_x86_mmr(ctypes.Structure): """Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.""" _fields_ = [ @@ -410,6 +432,7 @@ class Uc(object): self._ctype_cbs = [] self._callback_count = 0 self._cleanup.register(self) + self._hook_exception = None # The exception raised in a hook @staticmethod def release_handle(uch): @@ -427,6 +450,9 @@ class Uc(object): if status != uc.UC_ERR_OK: raise UcError(status) + if self._hook_exception is not None: + raise self._hook_exception + # stop emulation def emu_stop(self): status = _uc.uc_emu_stop(self._uch) @@ -522,41 +548,49 @@ class Uc(object): raise UcError(status) return result.value + @_catch_hook_exception def _hookcode_cb(self, handle, address, size, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] cb(self, address, size, data) + @_catch_hook_exception def _hook_mem_invalid_cb(self, handle, access, address, size, value, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] return cb(self, access, address, size, value, data) + @_catch_hook_exception def _hook_mem_access_cb(self, handle, access, address, size, value, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] cb(self, access, address, size, value, data) + @_catch_hook_exception def _hook_intr_cb(self, handle, intno, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] cb(self, intno, data) + @_catch_hook_exception def _hook_insn_invalid_cb(self, handle, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] return cb(self, data) + @_catch_hook_exception def _hook_insn_in_cb(self, handle, port, size, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] return cb(self, port, size, data) + @_catch_hook_exception def _hook_insn_out_cb(self, handle, port, size, value, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] cb(self, port, size, value, data) + @_catch_hook_exception def _hook_insn_syscall_cb(self, handle, user_data): # call user's callback with self object (cb, data) = self._callbacks[user_data] diff --git a/tests/regress/hook_raises_exception.py b/tests/regress/hook_raises_exception.py new file mode 100644 index 00000000..0c7b5bc1 --- /dev/null +++ b/tests/regress/hook_raises_exception.py @@ -0,0 +1,39 @@ +import regress +from unicorn import Uc, UC_ARCH_X86, UC_MODE_64, UC_HOOK_CODE + +CODE = b"\x90" * 3 +CODE_ADDR = 0x1000 + + +class HookCounter(object): + """Counts number of hook calls.""" + + def __init__(self): + self.hook_calls = 0 + + def bad_code_hook(self, uc, address, size, data): + self.hook_calls += 1 + raise ValueError("Something went wrong") + + def good_code_hook(self, uc, address, size, data): + self.hook_calls += 1 + + +class TestExceptionInHook(regress.RegressTest): + + def test_exception_in_hook(self): + uc = Uc(UC_ARCH_X86, UC_MODE_64) + uc.mem_map(CODE_ADDR, 0x1000) + uc.mem_write(CODE_ADDR, CODE) + + counter = HookCounter() + uc.hook_add(UC_HOOK_CODE, counter.good_code_hook, begin=CODE_ADDR, end=CODE_ADDR + len(CODE)) + uc.hook_add(UC_HOOK_CODE, counter.bad_code_hook, begin=CODE_ADDR, end=CODE_ADDR + len(CODE)) + + self.assertRaises(ValueError, uc.emu_start, CODE_ADDR, CODE_ADDR + len(CODE)) + # Make sure hooks calls finish before raising (hook_calls == 2) + self.assertEqual(counter.hook_calls, 2) + + +if __name__ == "__main__": + regress.main() From f27c6fa655583a58ce0da7707b5533b22ced583e Mon Sep 17 00:00:00 2001 From: Sven Almgren Date: Sat, 8 May 2021 12:45:14 +0200 Subject: [PATCH 6/7] X86 instruction FTST was incorrectly overwriting ST0 instead of FT0 (#1372) * X86 instruction FTST was incorrectly overwriting ST0 instead of FT0 * credits update --- CREDITS.TXT | 1 + qemu/target/i386/fpu_helper.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CREDITS.TXT b/CREDITS.TXT index 8f5970d6..47cd8105 100644 --- a/CREDITS.TXT +++ b/CREDITS.TXT @@ -78,3 +78,4 @@ Kevin Foo (chfl4gs): Travis-CI migration Simon Gorchakov: PowerPC target Stuart Dootson (studoot): MSVC compatibility with PowerPC target support Ziqiao Kong (lazymio): uc_context_free() API and various bug fix & improvement. +Sven Almgren (blindmatrix): bug fix diff --git a/qemu/target/i386/fpu_helper.c b/qemu/target/i386/fpu_helper.c index 48a23c3b..b4eda9c7 100644 --- a/qemu/target/i386/fpu_helper.c +++ b/qemu/target/i386/fpu_helper.c @@ -570,7 +570,7 @@ void helper_fldz_FT0(CPUX86State *env) { //FT0 = floatx80_zero; floatx80 zero = { 0x0000000000000000LL, 0x0000 }; - ST0 = zero; + FT0 = zero; } uint32_t helper_fnstsw(CPUX86State *env) From 5eab884244713243a4dc62f40a8cd61ece0d6936 Mon Sep 17 00:00:00 2001 From: Sun Daowen Date: Sun, 16 May 2021 21:36:33 +0800 Subject: [PATCH 7/7] compile on VS2008/VS2010 (#1397) Compiling with Win32 Release/MinSizeRel/RelWithDebInfo in VS2008 will cause "fatal error C1063: compiler limit: compiler stack overflow" problem. Compiling with Win32 Debug or x64 does not have this problem. You can easily bypass this problem by changing the optimization options /O1 and /O2 to /Od. In VS2010, when _INTPTR is 0 or 1, UINTPTR_MAX is 0xFFFFFFFF. --- CMakeLists.txt | 10 ++++++++- include/unicorn/platform.h | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b561cf5c..1cc90e52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1000,7 +1000,15 @@ if (MSVC) qemu/util/qemu-thread-win32.c ) if (CMAKE_SIZEOF_VOID_P EQUAL 8) - enable_language(ASM_MASM) + if (MSVC_VERSION LESS 1600 AND MSVC_IDE) + add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/build/setjmp-wrapper-win32.dir/setjmp-wrapper-win32.obj" + COMMAND ml64 /c /nologo /Fo"${CMAKE_CURRENT_SOURCE_DIR}/build/setjmp-wrapper-win32.dir/setjmp-wrapper-win32.obj" /W3 /errorReport:prompt /Ta"${CMAKE_CURRENT_SOURCE_DIR}/qemu/util/setjmp-wrapper-win32.asm" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/qemu/util/setjmp-wrapper-win32.asm" + ) + set(UNICORN_SRCS ${UNICORN_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/build/setjmp-wrapper-win32.dir/setjmp-wrapper-win32.obj") + else() + enable_language(ASM_MASM) + endif() set(UNICORN_COMMON_SRCS ${UNICORN_COMMON_SRCS} qemu/util/setjmp-wrapper-win32.asm) endif() else() diff --git a/include/unicorn/platform.h b/include/unicorn/platform.h index 1d5dc314..c504924f 100644 --- a/include/unicorn/platform.h +++ b/include/unicorn/platform.h @@ -64,6 +64,23 @@ typedef unsigned int uint32_t; typedef signed long long int64_t; typedef unsigned long long uint64_t; +typedef signed char int_fast8_t; +typedef int int_fast16_t; +typedef int int_fast32_t; +typedef long long int_fast64_t; +typedef unsigned char uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +#if !defined(_W64) +#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +#define _W64 __w64 +#else +#define _W64 +#endif +#endif + #ifndef _INTPTR_T_DEFINED #define _INTPTR_T_DEFINED #ifdef _WIN64 @@ -94,7 +111,36 @@ typedef _W64 unsigned int uintptr_t; #define UINT16_MAX 0xffffui16 #define UINT32_MAX 0xffffffffui32 #define UINT64_MAX 0xffffffffffffffffui64 + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#ifdef _WIN64 +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else /* _WIN64 */ +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif /* _WIN64 */ + #else // this system has stdint.h + +#if defined(_MSC_VER) && (_MSC_VER == MSC_VER_VS2010) +#define _INTPTR 2 +#endif + #include #endif // (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || defined(_KERNEL_MODE)