Implement a reg state manager mixin class

This commit is contained in:
elicn
2022-06-08 16:59:14 +03:00
parent df47e2395f
commit 78731e38aa

View File

@@ -341,7 +341,84 @@ def _catch_hook_exception(func: Callable) -> Callable:
return wrapper return wrapper
class Uc: class RegStateManager:
"""Registers state manager.
Designed as a mixin class; not to be instantiated directly.
Some methods must be implemented by mixin instances
"""
_DEFAULT_REGTYPE = ctypes.c_uint64
def _do_reg_read(self, reg_id: int, reg_obj) -> int:
"""Private register read implementation.
Must be implemented by the mixin object
"""
raise NotImplementedError
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
"""Private register write implementation.
Must be implemented by the mixin object
"""
raise NotImplementedError
def _reg_read(self, reg_id: int, regtype, *args):
"""Register read helper method.
"""
reg = regtype(*args)
status = self._do_reg_read(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status, reg_id)
return reg.value
def _reg_write(self, reg_id: int, regtype, value) -> None:
"""Register write helper method.
"""
reg = regtype.from_param(value) if issubclass(regtype, ctypes.Structure) else regtype(value)
status = self._do_reg_write(reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status, reg_id)
def reg_read(self, reg_id: int, aux: Any = None):
"""Read architectural register value.
Args:
reg_id : register identifier (architecture-specific enumeration)
aux : auxiliary data (register specific)
Returns: register value (register-specific format)
Raises: `UcError` in case of invalid register id or auxiliary data
"""
return self._reg_read(reg_id, self._DEFAULT_REGTYPE)
def reg_write(self, reg_id: int, value) -> None:
"""Write to architectural register.
Args:
reg_id : register identifier (architecture-specific enumeration)
value : value to write (register-specific format)
Raises: `UcError` in case of invalid register id or value format
"""
self._reg_write(reg_id, self._DEFAULT_REGTYPE, value)
class Uc(RegStateManager):
"""Unicorn Engine class. """Unicorn Engine class.
""" """
@@ -483,39 +560,18 @@ class Uc:
# CPU state accessors # # CPU state accessors #
########################### ###########################
def _reg_read(self, reg_id: int, regtype, *args): def _do_reg_read(self, reg_id: int, reg_obj) -> int:
"""Register read helper method. """Private register read implementation.
""" """
reg = regtype(*args) return uclib.uc_reg_read(self._uch, reg_id, reg_obj)
status = uclib.uc_reg_read(self._uch, reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status)
return reg.value
def _reg_write(self, reg_id: int, regtype, value) -> None: def _do_reg_write(self, reg_id: int, reg_obj) -> int:
"""Register write helper method. """Private register write implementation.
""" """
reg = regtype.from_param(value) if issubclass(regtype, ctypes.Structure) else regtype(value) return uclib.uc_reg_write(self._uch, reg_id, reg_obj)
# reg = regtype.from_param(value) if hasattr(regtype, '_fields_') else regtype(value)
status = uclib.uc_reg_write(self._uch, reg_id, ctypes.byref(reg))
if status != uc.UC_ERR_OK:
raise UcError(status)
def reg_read(self, reg_id: int, aux: Any = None):
# TODO: have a configurable default register type?
return self._reg_read(reg_id, ctypes.c_uint64)
def reg_write(self, reg_id: int, value) -> None:
# TODO: same as above
self._reg_write(reg_id, ctypes.c_uint64, value)
########################### ###########################
@@ -1049,7 +1105,7 @@ class Uc:
self.__ctl_w(uc.UC_CTL_TB_FLUSH) self.__ctl_w(uc.UC_CTL_TB_FLUSH)
class UcContext: class UcContext(RegStateManager):
def __init__(self, h, arch: int, mode: int): def __init__(self, h, arch: int, mode: int):
self._context = uc_context() self._context = uc_context()
self._size = uclib.uc_context_size(h) self._size = uclib.uc_context_size(h)
@@ -1079,32 +1135,43 @@ class UcContext:
def mode(self) -> int: def mode(self) -> int:
return self._mode return self._mode
# return the value of a register
def reg_read(self, reg_id: int, opt=None):
# return reg_read(functools.partial(_uc.uc_context_reg_read, self._context), self.arch, reg_id, opt)
raise NotImplementedError
# write to a register # RegStateManager mixin method implementation
def reg_write(self, reg_id: int, value): def _do_reg_read(self, reg_id: int, reg_obj) -> int:
# return reg_write(functools.partial(_uc.uc_context_reg_write, self._context), self.arch, reg_id, value) """Private register read implementation.
raise NotImplementedError """
return uclib.uc_context_reg_read(self._context, reg_id, reg_obj)
# RegStateManager mixin method implementation
def _do_reg_write(self, reg_id: int, reg_obj) -> int:
"""Private register write implementation.
"""
return uclib.uc_context_reg_write(self._context, reg_id, reg_obj)
# Make UcContext picklable # Make UcContext picklable
def __getstate__(self): def __getstate__(self):
return (bytes(self), self.size, self.arch, self.mode) return bytes(self), self.size, self.arch, self.mode
def __setstate__(self, state) -> None:
context, size, arch, mode = state
self._context = ctypes.cast(ctypes.create_string_buffer(context, size), uc_context)
self._size = size
self._arch = arch
self._mode = mode
def __setstate__(self, state):
self._size = state[1]
self._context = ctypes.cast(ctypes.create_string_buffer(state[0], self.size), uc_context)
# __init__ won't be invoked, so we are safe to set it here. # __init__ won't be invoked, so we are safe to set it here.
self._to_free = False self._to_free = False
self._arch = state[2]
self._mode = state[3]
def __bytes__(self):
def __bytes__(self) -> bytes:
return ctypes.string_at(self.context, self.size) return ctypes.string_at(self.context, self.size)
def __del__(self): def __del__(self) -> None:
# We need this property since we shouldn't free it if the object is constructed from pickled bytes. # We need this property since we shouldn't free it if the object is constructed from pickled bytes.
if self._to_free: if self._to_free:
uclib.uc_context_free(self._context) uclib.uc_context_free(self._context)