Merge pull request #1458 from bet4it/patch

Port some patches from Unicorn1 to Unicorn2
This commit is contained in:
lazymio 2021-11-03 20:59:42 +01:00 committed by GitHub
commit 6b5529fcb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 165 additions and 23 deletions

View File

@ -1001,7 +1001,15 @@ if (MSVC)
qemu/util/qemu-thread-win32.c qemu/util/qemu-thread-win32.c
) )
if (CMAKE_SIZEOF_VOID_P EQUAL 8) if (CMAKE_SIZEOF_VOID_P EQUAL 8)
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) enable_language(ASM_MASM)
endif()
set(UNICORN_COMMON_SRCS ${UNICORN_COMMON_SRCS} qemu/util/setjmp-wrapper-win32.asm) set(UNICORN_COMMON_SRCS ${UNICORN_COMMON_SRCS} qemu/util/setjmp-wrapper-win32.asm)
endif() endif()
else() else()

View File

@ -78,4 +78,5 @@ Kevin Foo (chfl4gs): Travis-CI migration
Simon Gorchakov: PowerPC target Simon Gorchakov: PowerPC target
Stuart Dootson (studoot): MSVC compatibility with PowerPC target support Stuart Dootson (studoot): MSVC compatibility with PowerPC target support
Ziqiao Kong (lazymio): uc_context_free() API and various bug fix & improvement. Ziqiao Kong (lazymio): uc_context_free() API and various bug fix & improvement.
Sven Almgren (blindmatrix): bug fix
Chenxu Wu (kabeor): Documentation Chenxu Wu (kabeor): Documentation

View File

@ -33,3 +33,6 @@ More bindings created & maintained externally by community are available as foll
- pharo-unicorn: Pharo binding (by Guille Polito) - pharo-unicorn: Pharo binding (by Guille Polito)
https://github.com/guillep/pharo-unicorn https://github.com/guillep/pharo-unicorn
- Unicorn.js: JavaScript binding (by Alexandro Sanchez)
https://github.com/AlexAltea/unicorn.js

View File

@ -108,7 +108,7 @@ type
@user_data: user data passed to tracing APIs @user_data: user data passed to tracing APIs
@return: return true to continue, or false to stop program (due to invalid memory). @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. 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, In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback,

View File

@ -3,6 +3,7 @@
import ctypes import ctypes
import ctypes.util import ctypes.util
import distutils.sysconfig import distutils.sysconfig
from functools import wraps
import pkg_resources import pkg_resources
import inspect import inspect
import os.path import os.path
@ -307,6 +308,27 @@ def reg_write(reg_write_func, arch, reg_id, value):
return 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): class uc_x86_mmr(ctypes.Structure):
"""Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.""" """Memory-Management Register for instructions IDTR, GDTR, LDTR, TR."""
_fields_ = [ _fields_ = [
@ -410,6 +432,7 @@ class Uc(object):
self._ctype_cbs = [] self._ctype_cbs = []
self._callback_count = 0 self._callback_count = 0
self._cleanup.register(self) self._cleanup.register(self)
self._hook_exception = None # The exception raised in a hook
@staticmethod @staticmethod
def release_handle(uch): def release_handle(uch):
@ -427,6 +450,9 @@ class Uc(object):
if status != uc.UC_ERR_OK: if status != uc.UC_ERR_OK:
raise UcError(status) raise UcError(status)
if self._hook_exception is not None:
raise self._hook_exception
# stop emulation # stop emulation
def emu_stop(self): def emu_stop(self):
status = _uc.uc_emu_stop(self._uch) status = _uc.uc_emu_stop(self._uch)
@ -522,41 +548,49 @@ class Uc(object):
raise UcError(status) raise UcError(status)
return result.value return result.value
@_catch_hook_exception
def _hookcode_cb(self, handle, address, size, user_data): def _hookcode_cb(self, handle, address, size, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, address, size, data) cb(self, address, size, data)
@_catch_hook_exception
def _hook_mem_invalid_cb(self, handle, access, address, size, value, user_data): def _hook_mem_invalid_cb(self, handle, access, address, size, value, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
return cb(self, access, address, size, value, 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): def _hook_mem_access_cb(self, handle, access, address, size, value, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, access, address, size, value, data) cb(self, access, address, size, value, data)
@_catch_hook_exception
def _hook_intr_cb(self, handle, intno, user_data): def _hook_intr_cb(self, handle, intno, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, intno, data) cb(self, intno, data)
@_catch_hook_exception
def _hook_insn_invalid_cb(self, handle, user_data): def _hook_insn_invalid_cb(self, handle, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
return cb(self, data) return cb(self, data)
@_catch_hook_exception
def _hook_insn_in_cb(self, handle, port, size, user_data): def _hook_insn_in_cb(self, handle, port, size, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
return cb(self, port, size, data) return cb(self, port, size, data)
@_catch_hook_exception
def _hook_insn_out_cb(self, handle, port, size, value, user_data): def _hook_insn_out_cb(self, handle, port, size, value, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]
cb(self, port, size, value, data) cb(self, port, size, value, data)
@_catch_hook_exception
def _hook_insn_syscall_cb(self, handle, user_data): def _hook_insn_syscall_cb(self, handle, user_data):
# call user's callback with self object # call user's callback with self object
(cb, data) = self._callbacks[user_data] (cb, data) = self._callbacks[user_data]

View File

@ -67,6 +67,23 @@ typedef unsigned int uint32_t;
typedef signed long long int64_t; typedef signed long long int64_t;
typedef unsigned long long uint64_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 #ifndef _INTPTR_T_DEFINED
#define _INTPTR_T_DEFINED #define _INTPTR_T_DEFINED
#ifdef _WIN64 #ifdef _WIN64
@ -97,7 +114,36 @@ typedef _W64 unsigned int uintptr_t;
#define UINT16_MAX 0xffffui16 #define UINT16_MAX 0xffffui16
#define UINT32_MAX 0xffffffffui32 #define UINT32_MAX 0xffffffffui32
#define UINT64_MAX 0xffffffffffffffffui64 #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 #else // this system has stdint.h
#if defined(_MSC_VER) && (_MSC_VER == MSC_VER_VS2010)
#define _INTPTR 2
#endif
#include <stdint.h> #include <stdint.h>
#endif // (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || #endif // (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) ||
// defined(_KERNEL_MODE) // defined(_KERNEL_MODE)

View File

@ -408,7 +408,7 @@ typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,
@user_data: user data passed to tracing APIs @user_data: user data passed to tracing APIs
@return: return true to continue, or false to stop program (due to invalid @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 memory). NOTE: returning true to continue execution will only work if the
accessed memory is made accessible with the correct permissions during the accessed memory is made accessible with the correct permissions during the
hook. hook.
@ -642,7 +642,7 @@ UNICORN_EXPORT
uc_err uc_ctl(uc_engine *uc, uc_control_type option, ...); uc_err uc_ctl(uc_engine *uc, uc_control_type option, ...);
/* /*
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. Like glibc's errno, uc_errno might not retain its old value once accessed.
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@ -756,7 +756,7 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size);
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@begin: address where emulation starts @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, @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. 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, @count: the number of instructions to be emulated. When this value is 0,
@ -792,12 +792,12 @@ uc_err uc_emu_stop(uc_engine *uc);
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@hh: hook handle returned from this registration. To be used in uc_hook_del() @hh: hook handle returned from this registration. To be used in uc_hook_del()
API API
@type: hook type @type: hook type, refer to uc_hook_type enum
@callback: callback to be run when instruction is hit @callback: callback to be run when instruction is hit
@user_data: user-defined data. This will be passed to callback function in its @user_data: user-defined data. This will be passed to callback function in its
last argument @user_data last argument @user_data
@begin: start 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 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, 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 @end] NOTE 2: if @begin > @end, callback is called whenever this hook type is
triggered triggered
@ -818,7 +818,7 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
Unregister (remove) a hook callback. Unregister (remove) a hook callback.
This API removes the hook callback registered by uc_hook_add(). This API removes the hook callback registered by uc_hook_add().
NOTE: this should be called only when you no longer want to trace. 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() @uc: handle returned by uc_open()
@hh: handle returned by uc_hook_add() @hh: handle returned by uc_hook_add()
@ -846,7 +846,7 @@ typedef enum uc_prot {
This address must be aligned to 4KB, or this will return with UC_ERR_ARG This address must be aligned to 4KB, or this will return with UC_ERR_ARG
error. error.
@size: size of the new memory region to be mapped in. @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 This size must be a multiple of 4KB, or this will return with UC_ERR_ARG
error. error.
@perms: Permissions for the newly mapped region. @perms: Permissions for the newly mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | This must be some combination of UC_PROT_READ | UC_PROT_WRITE |
@ -867,7 +867,7 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
This address must be aligned to 4KB, or this will return with UC_ERR_ARG This address must be aligned to 4KB, or this will return with UC_ERR_ARG
error. error.
@size: size of the new memory region to be mapped in. @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 This size must be a multiple of 4KB, or this will return with UC_ERR_ARG
error. error.
@perms: Permissions for the newly mapped region. @perms: Permissions for the newly mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | This must be some combination of UC_PROT_READ | UC_PROT_WRITE |
@ -917,7 +917,7 @@ uc_err uc_mmio_map(uc_engine *uc, uint64_t address, size_t size,
This address must be aligned to 4KB, or this will return with UC_ERR_ARG This address must be aligned to 4KB, or this will return with UC_ERR_ARG
error. error.
@size: size of the memory region to be modified. @size: size of the memory region to be modified.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG This size must be a multiple of 4KB, or this will return with UC_ERR_ARG
error. error.
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
@ -935,7 +935,7 @@ uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size);
This address must be aligned to 4KB, or this will return with UC_ERR_ARG This address must be aligned to 4KB, or this will return with UC_ERR_ARG
error. error.
@size: size of the memory region to be modified. @size: size of the memory region to be modified.
This size must be multiple of 4KB, or this will return with UC_ERR_ARG This size must be a multiple of 4KB, or this will return with UC_ERR_ARG
error. error.
@perms: New permissions for the mapped region. @perms: New permissions for the mapped region.
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | This must be some combination of UC_PROT_READ | UC_PROT_WRITE |
@ -951,8 +951,8 @@ uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size,
/* /*
Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr() 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 This API allocates memory for @regions, and user must free this memory later
by free() to avoid leaking memory. by uc_free() to avoid leaking memory.
NOTE: memory regions may be splitted by uc_mem_unmap() NOTE: memory regions may be split by uc_mem_unmap()
@uc: handle returned by uc_open() @uc: handle returned by uc_open()
@regions: pointer to an array of uc_mem_region struct. This is allocated by @regions: pointer to an array of uc_mem_region struct. This is allocated by
@ -972,9 +972,9 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
differing arches or modes. differing arches or modes.
@uc: handle returned by uc_open() @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. 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 @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error). for detailed error).
@ -985,7 +985,7 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context);
/* /*
Free the memory allocated by uc_mem_regions. Free the memory allocated by uc_mem_regions.
WARNING: After Unicorn 1.0.1rc5, the memory allocated by uc_context_alloc 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 should be freed by uc_context_free(). Calling uc_free() may still work, but
the result is **undefined**. the result is **undefined**.
@mem: memory allocated by uc_mem_regions (returned in *regions). @mem: memory allocated by uc_mem_regions (returned in *regions).

View File

@ -468,7 +468,7 @@ static uc_err arm_query(struct uc_struct *uc, uc_query_type type,
// zero out ARM/THUMB mode // zero out ARM/THUMB mode
mode = uc->mode & ~(UC_MODE_ARM | UC_MODE_THUMB); mode = uc->mode & ~(UC_MODE_ARM | UC_MODE_THUMB);
// THUMB mode or ARM MOde // THUMB mode or ARM MOde
mode += mode |=
((ARM_CPU(mycpu)->env.thumb != 0) ? UC_MODE_THUMB : UC_MODE_ARM); ((ARM_CPU(mycpu)->env.thumb != 0) ? UC_MODE_THUMB : UC_MODE_ARM);
*result = mode; *result = mode;
return UC_ERR_OK; return UC_ERR_OK;

View File

@ -570,7 +570,7 @@ void helper_fldz_FT0(CPUX86State *env)
{ {
//FT0 = floatx80_zero; //FT0 = floatx80_zero;
floatx80 zero = { 0x0000000000000000LL, 0x0000 }; floatx80 zero = { 0x0000000000000000LL, 0x0000 };
ST0 = zero; FT0 = zero;
} }
uint32_t helper_fnstsw(CPUX86State *env) uint32_t helper_fnstsw(CPUX86State *env)

View File

@ -98,6 +98,16 @@ unsigned long qemu_getauxval(unsigned long type)
return 0; return 0;
} }
#elif defined(__FreeBSD__)
#include <sys/auxv.h>
unsigned long qemu_getauxval(unsigned long type)
{
unsigned long aux = 0;
elf_aux_info(type, &aux, sizeof(aux));
return aux;
}
#else #else
unsigned long qemu_getauxval(unsigned long type) unsigned long qemu_getauxval(unsigned long type)

View File

@ -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()

3
uc.c
View File

@ -1596,7 +1596,8 @@ uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
return uc->query(uc, type, result); return uc->query(uc, type, result);
} }
#endif #endif
return UC_ERR_ARG; *result = uc->mode;
break;
case UC_QUERY_TIMEOUT: case UC_QUERY_TIMEOUT:
*result = uc->timed_out; *result = uc->timed_out;