From 7deb613a3c32a0be0ae8d44e048eff9e9e04ff14 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 16:58:03 +0300 Subject: [PATCH 1/7] Reduce namespace clutter --- bindings/python/unicorn/unicorn.py | 14 ++++++++++---- .../python/unicorn/unicorn_py3/arch/__init__.py | 4 ---- bindings/python/unicorn/unicorn_py3/arch/arm.py | 8 ++++++-- bindings/python/unicorn/unicorn_py3/arch/arm64.py | 11 +++++++---- bindings/python/unicorn/unicorn_py3/arch/intel.py | 14 +++++++++----- bindings/python/unicorn/unicorn_py3/arch/types.py | 3 +++ bindings/python/unicorn/unicorn_py3/unicorn.py | 14 ++++++-------- 7 files changed, 41 insertions(+), 27 deletions(-) diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 04b3e295a4..3ad3cb379a 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -1,8 +1,14 @@ -import sys +import sys as _sys -if sys.version_info[0] == 2: +from .unicorn_const import ( + UC_VERSION_MAJOR as __MAJOR, + UC_VERSION_MINOR as __MINOR, + UC_VERSION_PATCH as __PATCH +) + +__version__ = "%u.%u.%u" % (__MAJOR, __MINOR, __PATCH) + +if _sys.version_info.major == 2: from .unicorn_py2 import * else: from .unicorn_py3 import * - -__version__ = "%u.%u.%u" % (uc.UC_VERSION_MAJOR, uc.UC_VERSION_MINOR, uc.UC_VERSION_PATCH) diff --git a/bindings/python/unicorn/unicorn_py3/arch/__init__.py b/bindings/python/unicorn/unicorn_py3/arch/__init__.py index 1330905dc6..e69de29bb2 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/__init__.py +++ b/bindings/python/unicorn/unicorn_py3/arch/__init__.py @@ -1,4 +0,0 @@ -# from .arm import UcRegCP -# from .arm64 import UcRegCP64 -# from .intel import UcRegFPR, UcRegMMR, UcRegMSR -# from .types import UcReg128, UcReg256, UcReg512, UcReg, UcLargeReg, UcTupledReg \ No newline at end of file diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm.py b/bindings/python/unicorn/unicorn_py3/arch/arm.py index 7ec019b35b..98b5c91e40 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm.py @@ -6,9 +6,11 @@ import ctypes -from .. import Uc -from .. import arm_const as const +# traditional unicorn imports +from unicorn import arm_const as const +# newly introduced unicorn imports +from ..unicorn import Uc from .types import UcTupledReg, UcReg128 ARMCPReg = Tuple[int, int, int, int, int, int, int, int] @@ -83,3 +85,5 @@ def reg_write(self, reg_id: int, value) -> None: else: self._reg_write(reg_id, reg_cls, value) + +__all__ = ['UcRegCP', 'UcAArch32'] diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm64.py b/bindings/python/unicorn/unicorn_py3/arch/arm64.py index e6dcdb275c..c2b94f591e 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm64.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm64.py @@ -6,11 +6,12 @@ import ctypes -from .. import Uc, UcError -from .. import arm64_const as const +# traditional unicorn imports +from unicorn import arm64_const as const +from unicorn.unicorn_const import UC_ERR_ARG, UC_HOOK_INSN -from ..unicorn import uccallback -from unicorn import UC_ERR_ARG, UC_HOOK_INSN +# newly introduced unicorn imports +from ..unicorn import Uc, UcError, uccallback from .types import uc_engine, UcTupledReg, UcReg128 ARM64CPReg = Tuple[int, int, int, int, int, int] @@ -124,3 +125,5 @@ def reg_write(self, reg_id: int, value) -> None: else: self._reg_write(reg_id, reg_cls, value) + +__all__ = ['UcRegCP64', 'UcAArch64'] diff --git a/bindings/python/unicorn/unicorn_py3/arch/intel.py b/bindings/python/unicorn/unicorn_py3/arch/intel.py index ec3da95ab4..ea341bc355 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/intel.py +++ b/bindings/python/unicorn/unicorn_py3/arch/intel.py @@ -2,15 +2,16 @@ # # @author elicn -from typing import Any, Callable, Tuple +from typing import Any, Callable, Sequence, Tuple import ctypes -from .. import Uc, UcError -from .. import x86_const as const - -from unicorn.unicorn_py3 import uccallback +# traditional unicorn imports +from unicorn import x86_const as const from unicorn.unicorn_const import UC_ERR_ARG, UC_HOOK_INSN + +# newly introduced unicorn imports +from ..unicorn import Uc, UcError, uccallback from .types import uc_engine, UcTupledReg, UcReg128, UcReg256, UcReg512 X86MMRReg = Tuple[int, int, int, int] @@ -176,3 +177,6 @@ def msr_read(self, msr_id: int) -> int: def msr_write(self, msr_id: int, value: int) -> None: self._reg_write(const.UC_X86_REG_MSR, UcRegMSR, (msr_id, value)) + + +__all__ = ['UcRegMMR', 'UcRegMSR', 'UcRegFPR', 'UcIntel'] diff --git a/bindings/python/unicorn/unicorn_py3/arch/types.py b/bindings/python/unicorn/unicorn_py3/arch/types.py index f5793b91d9..ea6f471fdb 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/types.py +++ b/bindings/python/unicorn/unicorn_py3/arch/types.py @@ -94,3 +94,6 @@ class UcReg256(UcLargeReg): class UcReg512(UcLargeReg): _fields_ = [('qwords', ctypes.c_uint64 * 8)] + + +__all__ = ['uc_err', 'uc_engine', 'uc_context', 'uc_hook_h', 'UcReg', 'UcTupledReg', 'UcLargeReg', 'UcReg128', 'UcReg256', 'UcReg512'] diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 88d9d6c599..7a1db01dc0 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -5,10 +5,10 @@ import functools import weakref -from unicorn import x86_const, arm_const, arm64_const, unicorn_const as uc -from .arch.types import * +from unicorn import unicorn_const as uc +from .arch.types import uc_err, uc_engine, uc_context, uc_hook_h, UcReg -__version__ = f'{uc.UC_VERSION_MAJOR}.{uc.UC_VERSION_MINOR}.{uc.UC_VERSION_PATCH}' +# __version__ = f'{uc.UC_VERSION_MAJOR}.{uc.UC_VERSION_MINOR}.{uc.UC_VERSION_PATCH}' def _structure_repr(self): return "%s(%s)" % (self.__class__.__name__, ", ".join("%s=%s" % (k, getattr(self, k)) for (k, _) in self._fields_)) @@ -457,7 +457,7 @@ def __uc_subclass(pkgname: str, clsname: str): """ def __wrapped() -> Type[Uc]: - archmod = importlib.import_module(f'.arch.{pkgname}', f'unicorn.unicorn_py3') + archmod = importlib.import_module(f'.arch.{pkgname}', 'unicorn.unicorn_py3') return getattr(archmod, clsname) @@ -1163,8 +1163,6 @@ def __del__(self) -> None: UC_MMIO_WRITE_TYPE = Callable[[Uc, int, int, int, Any], None] +__all__ = ['Uc', 'UcContext', 'ucsubclass', 'UcError', 'uc_version', 'version_bind', 'uc_arch_supported', 'debug'] + -__all__ = [ - 'Uc', 'UcContext', 'ucsubclass', 'uc_version', 'uc_arch_supported', 'version_bind', 'UcError', 'uc', 'debug', 'uccallback', - 'x86_const', 'arm_const', 'arm64_const' -] \ No newline at end of file From 2da154721b30ed56605d3e6dad69db17f5930328 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 17:00:33 +0300 Subject: [PATCH 2/7] Remove repr surplus method --- bindings/python/unicorn/unicorn_py3/arch/arm.py | 4 ---- bindings/python/unicorn/unicorn_py3/arch/types.py | 4 ---- bindings/python/unicorn/unicorn_py3/unicorn.py | 6 ------ 3 files changed, 14 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm.py b/bindings/python/unicorn/unicorn_py3/arch/arm.py index 98b5c91e40..46c00c7aee 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm.py @@ -15,9 +15,6 @@ ARMCPReg = Tuple[int, int, int, int, int, int, int, int] -def _structure_repr(self): - return "%s(%s)" % (self.__class__.__name__, ", ".join("%s=%s" % (k, getattr(self, k)) for (k, _) in self._fields_)) - class UcRegCP(UcTupledReg[ARMCPReg]): """ARM coprocessors registers for instructions MRC, MCR, MRRC, MCRR @@ -38,7 +35,6 @@ class UcRegCP(UcTupledReg[ARMCPReg]): def value(self) -> int: return self.val - __repr__ = _structure_repr class UcAArch32(Uc): """Unicorn subclass for ARM architecture. diff --git a/bindings/python/unicorn/unicorn_py3/arch/types.py b/bindings/python/unicorn/unicorn_py3/arch/types.py index ea6f471fdb..8c0a9b63e9 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/types.py +++ b/bindings/python/unicorn/unicorn_py3/arch/types.py @@ -15,9 +15,6 @@ VT = TypeVar('VT', bound=Tuple[int, ...]) -def _structure_repr(self): - return "%s(%s)" % (self.__class__.__name__, ", ".join("%s=%s" % (k, getattr(self, k)) for (k, _) in self._fields_)) - class UcReg(ctypes.Structure): """A base class for composite registers. @@ -41,7 +38,6 @@ def from_value(cls, value): pass - _repr_ = _structure_repr class UcTupledReg(UcReg, Generic[VT]): """A base class for registers whose values are represented as a set diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 7a1db01dc0..7460edb931 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -10,9 +10,6 @@ # __version__ = f'{uc.UC_VERSION_MAJOR}.{uc.UC_VERSION_MINOR}.{uc.UC_VERSION_PATCH}' -def _structure_repr(self): - return "%s(%s)" % (self.__class__.__name__, ", ".join("%s=%s" % (k, getattr(self, k)) for (k, _) in self._fields_)) - class _uc_mem_region(ctypes.Structure): _fields_ = ( @@ -25,7 +22,6 @@ class _uc_mem_region(ctypes.Structure): def value(self) -> Tuple[int, int, int]: return tuple(getattr(self, fname) for fname, _ in self._fields_) - __repr__ = _structure_repr class uc_tb(ctypes.Structure): """"TranslationBlock @@ -36,8 +32,6 @@ class uc_tb(ctypes.Structure): ('icount', ctypes.c_uint16), ('size', ctypes.c_uint16) ) - - __repr__ = _structure_repr def __load_uc_lib() -> ctypes.CDLL: From 754194c7e877b3c4d7a2e9243f83a9285b716561 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 17:04:21 +0300 Subject: [PATCH 3/7] Improve documentation --- bindings/python/unicorn/unicorn_py3/arch/arm.py | 4 ++-- bindings/python/unicorn/unicorn_py3/arch/arm64.py | 4 ++-- bindings/python/unicorn/unicorn_py3/arch/intel.py | 10 ++++++++-- bindings/python/unicorn/unicorn_py3/arch/types.py | 13 +++++++++++-- bindings/python/unicorn/unicorn_py3/unicorn.py | 6 +++++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm.py b/bindings/python/unicorn/unicorn_py3/arch/arm.py index 46c00c7aee..6df9ec98f4 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm.py @@ -1,5 +1,5 @@ -# AArch32 classes and structures. -# +"""AArch32 classes and structures. +""" # @author elicn from typing import Any, Tuple diff --git a/bindings/python/unicorn/unicorn_py3/arch/arm64.py b/bindings/python/unicorn/unicorn_py3/arch/arm64.py index c2b94f591e..8e066ace4f 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/arm64.py +++ b/bindings/python/unicorn/unicorn_py3/arch/arm64.py @@ -1,5 +1,5 @@ -# AArch64 classes and structures. -# +"""AArch64 classes and structures. +""" # @author elicn from typing import Any, Callable, NamedTuple, Tuple diff --git a/bindings/python/unicorn/unicorn_py3/arch/intel.py b/bindings/python/unicorn/unicorn_py3/arch/intel.py index ea341bc355..8198429a9a 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/intel.py +++ b/bindings/python/unicorn/unicorn_py3/arch/intel.py @@ -1,5 +1,5 @@ -# Intel architecture classes and structures. -# +"""Intel architecture classes and structures. +""" # @author elicn from typing import Any, Callable, Sequence, Tuple @@ -37,6 +37,9 @@ class UcRegMMR(UcTupledReg[X86MMRReg]): class UcRegMSR(UcTupledReg[X86MSRReg]): + """Intel Model Specific Register + """ + _fields_ = ( ('rid', ctypes.c_uint32), ('val', ctypes.c_uint64) @@ -48,6 +51,9 @@ def value(self) -> int: class UcRegFPR(UcTupledReg[X86FPReg]): + """Intel Floating Point Register + """ + _fields_ = ( ('mantissa', ctypes.c_uint64), ('exponent', ctypes.c_uint16) diff --git a/bindings/python/unicorn/unicorn_py3/arch/types.py b/bindings/python/unicorn/unicorn_py3/arch/types.py index 8c0a9b63e9..8e7114c697 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/types.py +++ b/bindings/python/unicorn/unicorn_py3/arch/types.py @@ -1,5 +1,5 @@ -# Common types and structures. -# +"""Common types and structures. +""" # @author elicn from abc import abstractmethod @@ -81,14 +81,23 @@ def from_value(cls, value: int): class UcReg128(UcLargeReg): + """Large register holding a 128-bit value. + """ + _fields_ = [('qwords', ctypes.c_uint64 * 2)] class UcReg256(UcLargeReg): + """Large register holding a 256-bit value. + """ + _fields_ = [('qwords', ctypes.c_uint64 * 4)] class UcReg512(UcLargeReg): + """Large register holding a 512-bit value. + """ + _fields_ = [('qwords', ctypes.c_uint64 * 8)] diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 7460edb931..11df76a0bd 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -1,3 +1,7 @@ +"""New and improved Unicorn Python bindings by elicn +based on Nguyen Anh Quynnh's work +""" + from __future__ import annotations from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, TypeVar @@ -111,7 +115,7 @@ def __attempt_load(libname: str): T = TypeVar('T') def __pick_first_valid(iter: Iterable[T]) -> Optional[T]: - """Iterate till encountering a non-None element + """Iterate till encountering a non-None element and return it. """ return next((elem for elem in iter if elem is not None), None) From 4471377b776de8fe7b41e1a24b9929387bd9fce1 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 17:13:36 +0300 Subject: [PATCH 4/7] Styling fixes --- .../python/unicorn/unicorn_py3/unicorn.py | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 11df76a0bd..8b9949e4c2 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -24,7 +24,7 @@ class _uc_mem_region(ctypes.Structure): @property def value(self) -> Tuple[int, int, int]: - return tuple(getattr(self, fname) for fname, _ in self._fields_) + return tuple(getattr(self, fname) for fname, *_ in self._fields_) class uc_tb(ctypes.Structure): @@ -97,28 +97,28 @@ def _load_lib(path: Path, lib_name: str): pkg_resources.resource_filename(__name__, 'lib'), PurePath(inspect.getfile(__load_uc_lib)).parent.parent / 'lib', '', - "/usr/local/lib/" if sys.platform == 'darwin' else '/usr/lib64', - ] + [PurePath(p) / 'unicorn' / 'lib' for p in sys.path] # lazymio: ??? why PATH ?? + r'/usr/local/lib' if sys.platform == 'darwin' else r'/usr/lib64', + ] + [PurePath(p) / 'unicorn' / 'lib' for p in sys.path] # filter out None elements lib_locations = tuple(Path(loc) for loc in lib_locations if loc is not None) lib_name = { - 'cygwin' : 'cygunicorn.dll', - 'darwin' : 'libunicorn.2.dylib', - 'linux' : 'libunicorn.so.2', + 'cygwin': 'cygunicorn.dll', + 'darwin': 'libunicorn.2.dylib', + 'linux': 'libunicorn.so.2', 'linux2': 'libunicorn.so.2', - 'win32' : 'unicorn.dll' + 'win32': 'unicorn.dll' }.get(platform, "libunicorn.so") def __attempt_load(libname: str): T = TypeVar('T') - def __pick_first_valid(iter: Iterable[T]) -> Optional[T]: + def __pick_first_valid(it: Iterable[T]) -> Optional[T]: """Iterate till encountering a non-None element and return it. """ - return next((elem for elem in iter if elem is not None), None) + return next((elem for elem in it if elem is not None), None) return __pick_first_valid(_load_lib(loc, libname) for loc in lib_locations) @@ -965,11 +965,8 @@ def context_restore(self, context: UcContext) -> None: @staticmethod def __ctl_encode(ctl: int, op: int, nargs: int) -> int: - if not (op and (op & ~0b11) == 0): - raise ValueError("Op should be 0, 1, or 2") - - if not (nargs and (nargs & ~0b1111) == 0): - raise ValueError("Max number of arguments is 16") + assert nargs and (nargs & ~0b1111) == 0, f'nargs must not exceed value of 15 (got {nargs})' + assert op and (op & ~0b11) == 0, f'op must not exceed value of 3 (got {op})' return (op << 30) | (nargs << 26) | ctl From f573356a8b1c7eea3be0d87cc5a121b19415c4d3 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 17:15:24 +0300 Subject: [PATCH 5/7] Add initial support for batch operations --- .../python/unicorn/unicorn_py3/arch/intel.py | 5 ++ .../python/unicorn/unicorn_py3/unicorn.py | 87 ++++++++++++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/bindings/python/unicorn/unicorn_py3/arch/intel.py b/bindings/python/unicorn/unicorn_py3/arch/intel.py index 8198429a9a..6a5e59458e 100644 --- a/bindings/python/unicorn/unicorn_py3/arch/intel.py +++ b/bindings/python/unicorn/unicorn_py3/arch/intel.py @@ -184,5 +184,10 @@ def msr_read(self, msr_id: int) -> int: def msr_write(self, msr_id: int, value: int) -> None: self._reg_write(const.UC_X86_REG_MSR, UcRegMSR, (msr_id, value)) + def reg_read_batch(self, reg_ids: Sequence[int]) -> Tuple: + reg_types = [UcIntel.__select_reg_class(rid) or self._DEFAULT_REGTYPE for rid in reg_ids] + + return self._reg_read_batch(reg_ids, reg_types) + __all__ = ['UcRegMMR', 'UcRegMSR', 'UcRegFPR', 'UcIntel'] diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 8b9949e4c2..5c4a4a34e9 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -154,6 +154,7 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct __set_prototype('uc_errno', uc_err, uc_engine) __set_prototype('uc_reg_read', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p) __set_prototype('uc_reg_write', uc_err, uc_engine, ctypes.c_int, ctypes.c_void_p) + __set_prototype('uc_reg_read_batch', uc_err, uc_engine, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) __set_prototype('uc_mem_read', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) __set_prototype('uc_mem_write', uc_err, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) __set_prototype('uc_emu_start', uc_err, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) @@ -172,6 +173,7 @@ def __set_prototype(fname: str, restype: Type[ctypes._CData], *argtypes: Type[ct __set_prototype('uc_context_size', ctypes.c_size_t, uc_engine) __set_prototype('uc_context_reg_read', uc_err, uc_context, ctypes.c_int, ctypes.c_void_p) __set_prototype('uc_context_reg_write', uc_err, uc_context, ctypes.c_int, ctypes.c_void_p) + __set_prototype('uc_context_reg_read_batch', uc_err, uc_context, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) __set_prototype('uc_context_free', uc_err, uc_context) __set_prototype('uc_mem_regions', uc_err, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32)) # https://bugs.python.org/issue42880 @@ -330,11 +332,33 @@ def _do_reg_write(self, reg_id: int, reg_obj) -> int: raise NotImplementedError - def _reg_read(self, reg_id: int, regtype, *args): + def _do_reg_read_batch(self, reglist, vallist, count) -> int: + """Private batch register read implementation. + Must be implemented by the mixin object + """ + + raise NotImplementedError + + def _do_reg_write_batch(self, reglist, count) -> int: + """Private batch register write implementation. + Must be implemented by the mixin object + """ + + raise NotImplementedError + + @staticmethod + def __get_reg_read_arg(regtype: Type, *args): + return regtype(*args) + + @staticmethod + def __get_reg_write_arg(regtype: Type, value): + return regtype.from_value(value) if issubclass(regtype, UcReg) else regtype(value) + + def _reg_read(self, reg_id: int, regtype: Type, *args): """Register read helper method. """ - reg = regtype(*args) + reg = self.__get_reg_read_arg(regtype, *args) status = self._do_reg_read(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: @@ -346,12 +370,30 @@ def _reg_write(self, reg_id: int, regtype: Type, value) -> None: """Register write helper method. """ - reg = regtype.from_value(value) if issubclass(regtype, UcReg) else regtype(value) + reg = self.__get_reg_write_arg(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_batch(self, reg_ids: Sequence[int], reg_types: Sequence[Type]) -> Tuple: + """Batch register read helper method. + """ + + assert len(reg_ids) == len(reg_types) + + count = len(reg_ids) + reg_list = (ctypes.c_int * count)(*reg_ids) + val_list = [rtype() for rtype in reg_types] + ptr_list = (ctypes.c_void_p * count)(*(ctypes.c_void_p(ctypes.addressof(elem)) for elem in val_list)) + + status = self._do_reg_read_batch(reg_list, ptr_list, ctypes.c_int(count)) + + if status != uc.UC_ERR_OK: + raise UcError(status) + + return tuple(v.value for v in val_list) + def reg_read(self, reg_id: int, aux: Any = None): """Read architectural register value. @@ -378,6 +420,32 @@ def reg_write(self, reg_id: int, value) -> None: self._reg_write(reg_id, self._DEFAULT_REGTYPE, value) + def reg_read_batch(self, reg_ids: Sequence[int]) -> Tuple: + """Read a sequence of architectural registers. + + Args: + reg_ids: a sequence of register identifiers (architecture-specific enumeration) + + Returns: a tuple of registers values (register-specific format) + + Raises: `UcError` in case of invalid register id + """ + + reg_types = [self._DEFAULT_REGTYPE for _ in range(len(reg_ids))] + + return self._reg_read_batch(reg_ids, reg_types) + + def reg_write_batch(self, reg_info: Sequence[Tuple[int, Any]]) -> None: + """Write a sequece of architectural registers. + + Args: + regs_info: a sequence of tuples consisting of register identifiers and values + + Raises: `UcError` in case of invalid register id or value format + """ + + # TODO + ... def ucsubclass(cls): """Uc subclass decorator. @@ -589,6 +657,13 @@ def _do_reg_write(self, reg_id: int, reg_obj) -> int: return uclib.uc_reg_write(self._uch, reg_id, reg_obj) + def _do_reg_read_batch(self, reglist, vallist, count) -> int: + """Private batch register read implementation. + Do not call directly. + """ + + return uclib.uc_reg_read_batch(self._uch, reglist, vallist, count) + ########################### # Memory management # ########################### @@ -1130,6 +1205,12 @@ def _do_reg_write(self, reg_id: int, reg_obj) -> int: return uclib.uc_context_reg_write(self._context, reg_id, reg_obj) + def _do_reg_read_batch(self, reglist, vallist, count) -> int: + """Private batch register read implementation. + """ + + return uclib.uc_context_reg_read_batch(self._context, reglist, vallist, count) + # Make UcContext picklable def __getstate__(self): return bytes(self), self.size, self.arch, self.mode From 5cc2d80916e25e962ba939a3fb918c8ea34aa8f4 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 17:16:21 +0300 Subject: [PATCH 6/7] Adjust import path to new path --- bindings/python/unicorn/unicorn_py3/unicorn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/unicorn/unicorn_py3/unicorn.py b/bindings/python/unicorn/unicorn_py3/unicorn.py index 5c4a4a34e9..3dba5d1e2f 100644 --- a/bindings/python/unicorn/unicorn_py3/unicorn.py +++ b/bindings/python/unicorn/unicorn_py3/unicorn.py @@ -95,7 +95,7 @@ def _load_lib(path: Path, lib_name: str): lib_locations = [ os.getenv('LIBUNICORN_PATH'), pkg_resources.resource_filename(__name__, 'lib'), - PurePath(inspect.getfile(__load_uc_lib)).parent.parent / 'lib', + PurePath(inspect.getfile(__load_uc_lib)).parent / 'lib', '', r'/usr/local/lib' if sys.platform == 'darwin' else r'/usr/lib64', ] + [PurePath(p) / 'unicorn' / 'lib' for p in sys.path] From 386e0ed575bf49fac5870430655a14853954e6a6 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 19 Sep 2024 17:17:04 +0300 Subject: [PATCH 7/7] Include networking auditing sample in script --- bindings/python/sample_all.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/python/sample_all.sh b/bindings/python/sample_all.sh index a5e27809c0..378c139da4 100755 --- a/bindings/python/sample_all.sh +++ b/bindings/python/sample_all.sh @@ -27,3 +27,5 @@ echo "==========================" python3 ./shellcode.py echo "==========================" python3 ./sample_ctl.py +echo "==========================" +python3 ./sample_network_auditing.py