Skip to content

Commit

Permalink
fix issue unicorn-engine#820
Browse files Browse the repository at this point in the history
  • Loading branch information
逸程 committed Nov 4, 2020
2 parents 5a30a16 + 770d567 commit d5fbfa1
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 39 deletions.
1 change: 1 addition & 0 deletions CREDITS.TXT
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ Philippe Antoine (Catena cyber): fuzzing
Huitao Chen (chenhuitao) & KaiJern Lau (xwings): Cmake support
Huitao Chen (chenhuitao) & KaiJern Lau (xwings): Python3 support for building
Kevin Foo (chfl4gs): Travis-CI migration
Ziqiao Kong (lazymio): uc_context_free() API and various bug fix & improvement.
14 changes: 13 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
This file details the changelog of Unicorn Engine.

-----------------------------------
[Version 1.0.2]: October 21st, 2020

- Fix Java binding compilation
- Enable building for ARM little-endian only (ignore big-endian)

------------------------------------
[Version 1.0.2-rc6]: Sept 24th, 2020

- Add uc_context_free() API
- Fix context saving/retoring API (core & Python binding)

------------------------------------
[Version 1.0.2-rc5]: Sept 22th, 2020
[Version 1.0.2-rc5]: Sept 22nd, 2020

- Add cmake option to build Unicorn as a static library
- Fix error handling of mmap()
Expand Down
7 changes: 7 additions & 0 deletions SPONSORS.TXT
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* Version 1.0.2 - October 21st, 2020

Release 1.0.2 was sponsored by the following companies (in no particular order).

- Catena Cyber: https://catenacyber.fr
- Grayshift: https://grayshift.com
- Google: https://google.com
3 changes: 3 additions & 0 deletions bindings/README
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion bindings/java/Makefile.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

JAVA_HOME := $(shell jrunscript -e 'java.lang.System.out.println(java.lang.System.getProperty("java.home"));')

JAVA_INC := $(shell realpath $(JAVA_HOME)/../include)
JAVA_INC := $(shell realpath $(JAVA_HOME)/include)

JAVA_PLATFORM_INC := $(shell dirname `find $(JAVA_INC) -name jni_md.h`)

Expand Down
2 changes: 1 addition & 1 deletion bindings/pascal/unicorn/Unicorn_dyn.pas
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ interface
@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,
Expand Down
8 changes: 7 additions & 1 deletion bindings/python/sample_x86.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from __future__ import print_function
from unicorn import *
from unicorn.x86_const import *

import pickle

X86_CODE32 = b"\x41\x4a\x66\x0f\xef\xc1" # INC ecx; DEC edx; PXOR xmm0, xmm1
X86_CODE32_LOOP = b"\x41\x4a\xeb\xfe" # INC ecx; DEC edx; JMP self-loop
Expand Down Expand Up @@ -453,11 +453,17 @@ def test_i386_context_save():
print(">>> Saving CPU context")
saved_context = mu.context_save()

print(">>> Pickling CPU context")
pickled_saved_context = pickle.dumps(saved_context)

print(">>> Running emulation for the second time")
mu.emu_start(address, address+1)
print(">>> Emulation done. Below is the CPU context")
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))

print(">>> Unpickling CPU context")
saved_context = pickle.loads(pickled_saved_context)

print(">>> CPU context restored. Below is the CPU context")
mu.context_restore(saved_context)
print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
Expand Down
37 changes: 31 additions & 6 deletions bindings/python/unicorn/unicorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class _uc_mem_region(ctypes.Structure):
_setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context)
_setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context)
_setup_prototype(_uc, "uc_context_size", ctypes.c_size_t, uc_engine)
_setup_prototype(_uc, "uc_context_free", ucerr, uc_context)
_setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32))

# uc_hook_add is special due to variable number of arguments
Expand Down Expand Up @@ -607,7 +608,7 @@ def context_save(self):
return context

def context_update(self, context):
status = _uc.uc_context_save(self._uch, context)
status = _uc.uc_context_save(self._uch, context.context)
if status != uc.UC_ERR_OK:
raise UcError(status)

Expand All @@ -631,16 +632,40 @@ def mem_regions(self):
_uc.uc_free(regions)


class UcContext(ctypes.Structure):
class UcContext:
def __init__(self, h):
self.context = uc_context()

status = _uc.uc_context_alloc(h, ctypes.byref(self.context))
self._context = uc_context()
self._size = _uc.uc_context_size(h)
self._to_free = True
status = _uc.uc_context_alloc(h, ctypes.byref(self._context))
if status != uc.UC_ERR_OK:
raise UcError(status)

@property
def context(self):
return self._context

@property
def size(self):
return self._size

# Make UcContext picklable
def __getstate__(self):
return (bytes(self), self.size)

def __setstate__(self, state):
self._size = state[1]
self._context = ctypes.cast(ctypes.create_string_buffer(state[0], self._size), uc_context)
# __init__ won'e be invoked, so we are safe to set it here.
self._to_free = False

def __bytes__(self):
return ctypes.string_at(self.context, self.size)

def __del__(self):
_uc.uc_free(self.context)
# We need this property since we shouldn't free it if the object is constructed from pickled bytes.
if self._to_free:
_uc.uc_context_free(self._context)


# print out debugging info
Expand Down
4 changes: 3 additions & 1 deletion include/uc_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,15 @@ struct uc_struct {
uint64_t next_pc; // save next PC for some special cases
bool hook_insert; // insert new hook at begin of the hook list (append by default)
bool slow_self_unpack;
struct list saved_contexts; // The contexts saved by this uc_struct.
};

// Metadata stub for the variable-size cpu context used with uc_context_*()
// We also save cpu->jmp_env, so emulation can be reentrant
struct uc_context {
size_t context_size; // size of the real internal context structure
unsigned int jmp_env_size; // size of cpu->jmp_env
size_t jmp_env_size; // size of cpu->jmp_env
struct uc_struct* uc; // the uc_struct which creates this context
char data[0]; // context + cpu->jmp_env
};

Expand Down
54 changes: 34 additions & 20 deletions include/unicorn/unicorn.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,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,
Expand Down Expand Up @@ -413,7 +413,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()
Expand Down Expand Up @@ -525,7 +525,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,
Expand Down Expand Up @@ -555,12 +555,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)
Expand All @@ -577,7 +577,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()
Expand All @@ -604,7 +604,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.
Expand All @@ -623,12 +623,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
Expand All @@ -645,7 +645,7 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t per
@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).
Expand All @@ -661,7 +661,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.
Expand All @@ -675,8 +675,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
Expand All @@ -696,9 +696,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).
Expand All @@ -707,10 +707,12 @@ UNICORN_EXPORT
uc_err uc_context_alloc(uc_engine *uc, uc_context **context);

/*
Free the memory allocated by uc_context_alloc & uc_mem_regions.
Free the memory allocated by uc_mem_regions.
WARNING: After Unicorn 1.0.1rc5, the memory allocated by uc_context_alloc should
be freed by uc_context_free(). Calling uc_free() may still work, but the result
is **undefined**.
@mem: memory allocated by uc_context_alloc (returned in *context), or
by uc_mem_regions (returned in *regions)
@mem: memory allocated by uc_mem_regions (returned in *regions).
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
Expand Down Expand Up @@ -738,7 +740,7 @@ uc_err uc_context_save(uc_engine *uc, uc_context *context);
state saved by uc_context_save().
@uc: handle returned by uc_open()
@buffer: handle returned by uc_context_alloc that has been used with uc_context_save
@context: handle returned by uc_context_alloc that has been used with uc_context_save
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
Expand All @@ -758,6 +760,18 @@ uc_err uc_context_restore(uc_engine *uc, uc_context *context);
UNICORN_EXPORT
size_t uc_context_size(uc_engine *uc);


/*
Free the context allocated by uc_context_alloc().
@context: handle returned by uc_context_alloc()
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
for detailed error).
*/
UNICORN_EXPORT
uc_err uc_context_free(uc_context *context);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion pkgconfig.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ PKG_MINOR = 0
PKG_EXTRA = 2

# version tag. Examples: rc1, b2, post1
PKG_TAG = rc5
# PKG_TAG = rc6
2 changes: 1 addition & 1 deletion qemu/include/qemu-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,14 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)

/* vector definitions */
#ifdef __ALTIVEC__
#include <altivec.h>
/* The altivec.h header says we're allowed to undef these for
* C++ compatibility. Here we don't care about C++, but we
* undef them anyway to avoid namespace pollution.
*/
#undef vector
#undef pixel
#undef bool
#include <altivec.h>
#define VECTYPE __vector unsigned char
#define SPLAT(p) vec_splat(vec_ld(0, p), 0)
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2)
Expand Down
2 changes: 1 addition & 1 deletion samples/sample_x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ static void test_i386_context_save(void)
printf(">>> EAX = 0x%x\n", r_eax);

// free the CPU context
err = uc_free(context);
err = uc_context_free(context);
if (err) {
printf("Failed on uc_free() with error returned: %u\n", err);
return;
Expand Down
Loading

0 comments on commit d5fbfa1

Please sign in to comment.