From 647832b01b537baa79100d1368eb5d93899499c8 Mon Sep 17 00:00:00 2001 From: elicn Date: Thu, 20 Oct 2022 14:16:53 +0300 Subject: [PATCH] Make special regs definitions generic --- bindings/python/unicorn/arch/arm.py | 13 ++----- bindings/python/unicorn/arch/arm64.py | 13 ++----- bindings/python/unicorn/arch/intel.py | 35 +++-------------- bindings/python/unicorn/arch/types.py | 55 +++++++++++++++++++++++++-- bindings/python/unicorn/unicorn.py | 2 +- 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/bindings/python/unicorn/arch/arm.py b/bindings/python/unicorn/arch/arm.py index a914654c..0669072d 100644 --- a/bindings/python/unicorn/arch/arm.py +++ b/bindings/python/unicorn/arch/arm.py @@ -9,13 +9,12 @@ import ctypes from .. import Uc from .. import arm_const as const -from .types import UcReg128 +from .types import UcTupledReg, UcReg128 -ARMCPReg = Tuple[int, int, int, int, int, int, int] -ARMCPRegValue = Tuple[int, int, int, int, int, int, int, int] +ARMCPReg = Tuple[int, int, int, int, int, int, int, int] -class UcRegCP(ctypes.Structure): +class UcRegCP(UcTupledReg[ARMCPReg]): """ARM coprocessors registers for instructions MRC, MCR, MRRC, MCRR """ @@ -34,12 +33,6 @@ class UcRegCP(ctypes.Structure): def value(self) -> int: return self.val - @classmethod - def from_param(cls, param: ARMCPRegValue): - assert type(param) is tuple and len(param) == len(cls._fields_) - - return cls(*param) - class UcAArch32(Uc): """Unicorn subclass for ARM architecture. diff --git a/bindings/python/unicorn/arch/arm64.py b/bindings/python/unicorn/arch/arm64.py index 8cd217a2..475957a8 100644 --- a/bindings/python/unicorn/arch/arm64.py +++ b/bindings/python/unicorn/arch/arm64.py @@ -11,15 +11,14 @@ from .. import arm64_const as const from ..unicorn import uccallback from ..unicorn_const import UC_ERR_ARG, UC_HOOK_INSN -from .types import uc_engine, UcReg128 +from .types import uc_engine, UcTupledReg, UcReg128 -ARM64CPReg = Tuple[int, int, int, int, int] -ARM64CPRegValue = Tuple[int, int, int, int, int, int] +ARM64CPReg = Tuple[int, int, int, int, int, int] HOOK_INSN_SYS_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p) -class UcRegCP(ctypes.Structure): +class UcRegCP(UcTupledReg[ARM64CPReg]): """ARM64 coprocessors registers for instructions MRS, MSR """ @@ -36,12 +35,6 @@ class UcRegCP(ctypes.Structure): def value(self) -> int: return self.val - @classmethod - def from_param(cls, param: ARM64CPRegValue): - assert type(param) is tuple and len(param) == len(cls._fields_) - - return cls(*param) - class UcAArch64(Uc): """Unicorn subclass for ARM64 architecture. diff --git a/bindings/python/unicorn/arch/intel.py b/bindings/python/unicorn/arch/intel.py index 2cacec1b..010a881b 100644 --- a/bindings/python/unicorn/arch/intel.py +++ b/bindings/python/unicorn/arch/intel.py @@ -11,9 +11,10 @@ from .. import x86_const as const from ..unicorn import uccallback from ..unicorn_const import UC_ERR_ARG, UC_HOOK_INSN -from .types import uc_engine, UcReg128, UcReg256, UcReg512 +from .types import uc_engine, UcTupledReg, UcReg128, UcReg256, UcReg512 X86MMRReg = Tuple[int, int, int, int] +X86MSRReg = Tuple[int, int] X86FPReg = Tuple[int, int] HOOK_INSN_IN_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_int, ctypes.c_void_p) @@ -22,7 +23,7 @@ HOOK_INSN_SYSCALL_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_void_p) HOOK_INSN_CPUID_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_void_p) -class UcRegMMR(ctypes.Structure): +class UcRegMMR(UcTupledReg[X86MMRReg]): """Memory-Management Register for instructions IDTR, GDTR, LDTR, TR. """ @@ -33,18 +34,8 @@ class UcRegMMR(ctypes.Structure): ('flags', ctypes.c_uint32) # not used by GDTR and IDTR ) - @property - def value(self) -> X86MMRReg: - return tuple(getattr(self, fname) for fname, _ in self._fields_) - @classmethod - def from_param(cls, param: X86MMRReg): - assert type(param) is tuple and len(param) == len(cls._fields_) - - return cls(*param) - - -class UcRegMSR(ctypes.Structure): +class UcRegMSR(UcTupledReg[X86MSRReg]): _fields_ = ( ('rid', ctypes.c_uint32), ('val', ctypes.c_uint64) @@ -54,29 +45,13 @@ class UcRegMSR(ctypes.Structure): def value(self) -> int: return self.val - @classmethod - def from_param(cls, param: Tuple[int, int]): - assert type(param) is tuple and len(param) == len(cls._fields_) - return cls(*param) - - -class UcRegFPR(ctypes.Structure): +class UcRegFPR(UcTupledReg[X86FPReg]): _fields_ = ( ('mantissa', ctypes.c_uint64), ('exponent', ctypes.c_uint16) ) - @property - def value(self) -> X86FPReg: - return tuple(getattr(self, fname) for fname, _ in self._fields_) - - @classmethod - def from_param(cls, param: X86FPReg): - assert type(param) is tuple and len(param) == len(cls._fields_) - - return cls(*param) - class UcIntel(Uc): """Unicorn subclass for Intel architecture. diff --git a/bindings/python/unicorn/arch/types.py b/bindings/python/unicorn/arch/types.py index c03f4ff8..f139cfc5 100644 --- a/bindings/python/unicorn/arch/types.py +++ b/bindings/python/unicorn/arch/types.py @@ -2,6 +2,9 @@ # # @author elicn +from abc import abstractmethod +from typing import Generic, Tuple, TypeVar + import ctypes uc_err = ctypes.c_int @@ -12,7 +15,51 @@ uc_context = ctypes.c_void_p uc_hook_h = ctypes.c_size_t -class UcLargeReg(ctypes.Structure): +VT = TypeVar('VT', bound=Tuple[int, ...]) + + +class UcReg(ctypes.Structure): + """A base class for composite registers. + + This class is meant to be inherited, not instantiated directly. + """ + + @property + @abstractmethod + def value(self): + """Get register value. + """ + + pass + + @classmethod + @abstractmethod + def from_value(cls, value): + """Create a register instance from a given value. + """ + + pass + + +class UcTupledReg(UcReg, Generic[VT]): + """A base class for registers whose values are represented as a set + of fields. + + This class is meant to be inherited, not instantiated directly. + """ + + @property + def value(self) -> VT: + return tuple(getattr(self, fname) for fname, *_ in self.__class__._fields_) # type: ignore + + @classmethod + def from_value(cls, value: VT): + assert type(value) is tuple and len(value) == len(cls._fields_) + + return cls(*value) + + +class UcLargeReg(UcReg): """A base class for large registers that are internally represented as an array of multiple qwords. @@ -26,13 +73,13 @@ class UcLargeReg(ctypes.Structure): return sum(qword << (64 * i) for i, qword in enumerate(self.qwords)) @classmethod - def from_param(cls, param: int): - assert type(param) is int + def from_value(cls, value: int): + assert type(value) is int mask = (1 << 64) - 1 size = cls._fields_[0][1]._length_ - return cls(tuple((param >> (64 * i)) & mask for i in range(size))) + return cls(tuple((value >> (64 * i)) & mask for i in range(size))) class UcReg128(UcLargeReg): diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 36f57e58..9755f363 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -338,7 +338,7 @@ class RegStateManager: """Register write helper method. """ - reg = regtype.from_param(value) if issubclass(regtype, ctypes.Structure) else regtype(value) + reg = regtype.from_value(value) if issubclass(regtype, UcReg) else regtype(value) status = self._do_reg_write(reg_id, ctypes.byref(reg)) if status != uc.UC_ERR_OK: