Merge remote-tracking branch 'eli/uc-py-next' into staged
This commit is contained in:
commit
ffd34cbb8c
|
@ -1,15 +1,29 @@
|
|||
#!/bin/sh
|
||||
|
||||
./sample_x86.py
|
||||
python3 ./sample_arm.py
|
||||
echo "=========================="
|
||||
./shellcode.py
|
||||
python3 ./sample_armeb.py
|
||||
echo "=========================="
|
||||
./sample_arm.py
|
||||
python3 ./sample_arm64.py
|
||||
echo "=========================="
|
||||
./sample_arm64.py
|
||||
python3 ./sample_arm64eb.py
|
||||
echo "=========================="
|
||||
./sample_mips.py
|
||||
python3 ./sample_m68k.py
|
||||
echo "=========================="
|
||||
./sample_sparc.py
|
||||
python3 ./sample_mips.py
|
||||
echo "=========================="
|
||||
./sample_m68k.py
|
||||
python3 ./sample_ppc.py
|
||||
echo "=========================="
|
||||
python3 ./sample_riscv.py
|
||||
echo "=========================="
|
||||
python3 ./sample_s390x.py
|
||||
echo "=========================="
|
||||
python3 ./sample_sparc.py
|
||||
echo "=========================="
|
||||
python3 ./sample_tricore.py
|
||||
echo "=========================="
|
||||
python3 ./sample_x86.py
|
||||
echo "=========================="
|
||||
python3 ./shellcode.py
|
||||
echo "=========================="
|
||||
python3 ./sample_ctl.py
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
# By Lazymio(@wtdcode), 2021
|
||||
|
||||
from unicorn import *
|
||||
from unicorn.unicorn import UC_HOOK_EDGE_GEN_CB
|
||||
from unicorn.x86_const import *
|
||||
from datetime import datetime
|
||||
|
||||
|
@ -93,7 +92,7 @@ def test_uc_ctl_exits():
|
|||
uc.hook_add(UC_HOOK_EDGE_GENERATED, trace_new_edge)
|
||||
|
||||
# Trace cmp instruction.
|
||||
uc.hook_add(UC_HOOK_TCG_OPCODE, trace_tcg_sub, UC_TCG_OP_SUB, UC_TCG_OP_FLAG_CMP)
|
||||
uc.hook_add(UC_HOOK_TCG_OPCODE, trace_tcg_sub, aux1=UC_TCG_OP_SUB, aux2=UC_TCG_OP_FLAG_CMP)
|
||||
|
||||
uc.ctl_exits_enabled(True)
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# Unicorn Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
|
||||
# New and improved Unicorn Python bindings by elicn
|
||||
# based on Nguyen Anh Quynnh's work
|
||||
|
||||
from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const, riscv_const, s390x_const, tricore_const
|
||||
from .unicorn_const import *
|
||||
from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__
|
||||
from .unicorn import Uc, ucsubclass, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# AArch32 classes and structures.
|
||||
#
|
||||
# @author elicn
|
||||
|
||||
from typing import Any, Tuple
|
||||
|
||||
import ctypes
|
||||
|
||||
from .. import Uc
|
||||
from .. import arm_const as const
|
||||
|
||||
from .types import UcTupledReg, UcReg128
|
||||
|
||||
ARMCPReg = Tuple[int, int, int, int, int, int, int, int]
|
||||
|
||||
|
||||
class UcRegCP(UcTupledReg[ARMCPReg]):
|
||||
"""ARM coprocessors registers for instructions MRC, MCR, MRRC, MCRR
|
||||
"""
|
||||
|
||||
_fields_ = (
|
||||
('cp', ctypes.c_uint32),
|
||||
('is64', ctypes.c_uint32),
|
||||
('sec', ctypes.c_uint32),
|
||||
('crn', ctypes.c_uint32),
|
||||
('crm', ctypes.c_uint32),
|
||||
('opc1', ctypes.c_uint32),
|
||||
('opc2', ctypes.c_uint32),
|
||||
('val', ctypes.c_uint64)
|
||||
)
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
return self.val
|
||||
|
||||
|
||||
class UcAArch32(Uc):
|
||||
"""Unicorn subclass for ARM architecture.
|
||||
"""
|
||||
|
||||
REG_RANGE_Q = range(const.UC_ARM_REG_Q0, const.UC_ARM_REG_Q15 + 1)
|
||||
|
||||
@staticmethod
|
||||
def __select_reg_class(reg_id: int):
|
||||
"""Select class for special architectural registers.
|
||||
"""
|
||||
|
||||
reg_class = (
|
||||
(UcAArch32.REG_RANGE_Q, UcReg128),
|
||||
)
|
||||
|
||||
return next((cls for rng, cls in reg_class if reg_id in rng), None)
|
||||
|
||||
def reg_read(self, reg_id: int, aux: Any = None):
|
||||
# select register class for special cases
|
||||
reg_cls = UcAArch32.__select_reg_class(reg_id)
|
||||
|
||||
if reg_cls is None:
|
||||
if reg_id == const.UC_ARM_REG_CP_REG:
|
||||
return self._reg_read(reg_id, UcRegCP, *aux)
|
||||
|
||||
else:
|
||||
# fallback to default reading method
|
||||
return super().reg_read(reg_id, aux)
|
||||
|
||||
return self._reg_read(reg_id, reg_cls)
|
||||
|
||||
def reg_write(self, reg_id: int, value) -> None:
|
||||
# select register class for special cases
|
||||
reg_cls = UcAArch32.__select_reg_class(reg_id)
|
||||
|
||||
if reg_cls is None:
|
||||
if reg_id == const.UC_ARM_REG_CP_REG:
|
||||
self._reg_write(reg_id, UcRegCP, value)
|
||||
|
||||
else:
|
||||
# fallback to default writing method
|
||||
super().reg_write(reg_id, value)
|
||||
|
||||
else:
|
||||
self._reg_write(reg_id, reg_cls, value)
|
|
@ -0,0 +1,126 @@
|
|||
# AArch64 classes and structures.
|
||||
#
|
||||
# @author elicn
|
||||
|
||||
from typing import Any, Callable, NamedTuple, Tuple
|
||||
|
||||
import ctypes
|
||||
|
||||
from .. import Uc, UcError
|
||||
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, UcTupledReg, UcReg128
|
||||
|
||||
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(UcTupledReg[ARM64CPReg]):
|
||||
"""ARM64 coprocessors registers for instructions MRS, MSR
|
||||
"""
|
||||
|
||||
_fields_ = (
|
||||
('crn', ctypes.c_uint32),
|
||||
('crm', ctypes.c_uint32),
|
||||
('op0', ctypes.c_uint32),
|
||||
('op1', ctypes.c_uint32),
|
||||
('op2', ctypes.c_uint32),
|
||||
('val', ctypes.c_uint64)
|
||||
)
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
return self.val
|
||||
|
||||
|
||||
class UcAArch64(Uc):
|
||||
"""Unicorn subclass for ARM64 architecture.
|
||||
"""
|
||||
|
||||
REG_RANGE_Q = range(const.UC_ARM64_REG_Q0, const.UC_ARM64_REG_Q31 + 1)
|
||||
REG_RANGE_V = range(const.UC_ARM64_REG_V0, const.UC_ARM64_REG_V31 + 1)
|
||||
|
||||
def hook_add(self, htype: int, callback: Callable, user_data: Any = None, begin: int = 1, end: int = 0, aux1: int = 0, aux2: int = 0) -> int:
|
||||
if htype != UC_HOOK_INSN:
|
||||
return super().hook_add(htype, callback, user_data, begin, end, aux1, aux2)
|
||||
|
||||
insn = ctypes.c_int(aux1)
|
||||
|
||||
def __hook_insn_sys():
|
||||
@uccallback(HOOK_INSN_SYS_CFUNC)
|
||||
def __hook_insn_sys_cb(handle: int, reg: int, pcp_reg: Any, key: int) -> int:
|
||||
cp_reg = ctypes.cast(pcp_reg, ctypes.POINTER(UcRegCP)).contents
|
||||
|
||||
class CpReg(NamedTuple):
|
||||
crn: int
|
||||
crm: int
|
||||
op0: int
|
||||
op1: int
|
||||
op2: int
|
||||
val: int
|
||||
|
||||
cp_reg = CpReg(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val)
|
||||
|
||||
return callback(self, reg, cp_reg, user_data)
|
||||
|
||||
return __hook_insn_sys_cb
|
||||
|
||||
handlers = {
|
||||
const.UC_ARM64_INS_MRS : __hook_insn_sys,
|
||||
const.UC_ARM64_INS_MSR : __hook_insn_sys,
|
||||
const.UC_ARM64_INS_SYS : __hook_insn_sys,
|
||||
const.UC_ARM64_INS_SYSL : __hook_insn_sys
|
||||
}
|
||||
|
||||
handler = handlers.get(insn.value)
|
||||
|
||||
if handler is None:
|
||||
raise UcError(UC_ERR_ARG)
|
||||
|
||||
fptr = handler()
|
||||
|
||||
return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn)
|
||||
|
||||
@staticmethod
|
||||
def __select_reg_class(reg_id: int):
|
||||
"""Select class for special architectural registers.
|
||||
"""
|
||||
|
||||
reg_class = (
|
||||
(UcAArch64.REG_RANGE_Q, UcReg128),
|
||||
(UcAArch64.REG_RANGE_V, UcReg128)
|
||||
)
|
||||
|
||||
return next((cls for rng, cls in reg_class if reg_id in rng), None)
|
||||
|
||||
def reg_read(self, reg_id: int, aux: Any = None):
|
||||
# select register class for special cases
|
||||
reg_cls = UcAArch64.__select_reg_class(reg_id)
|
||||
|
||||
if reg_cls is None:
|
||||
if reg_id == const.UC_ARM64_REG_CP_REG:
|
||||
return self._reg_read(reg_id, UcRegCP, *aux)
|
||||
|
||||
else:
|
||||
# fallback to default reading method
|
||||
return super().reg_read(reg_id, aux)
|
||||
|
||||
return self._reg_read(reg_id, reg_cls)
|
||||
|
||||
def reg_write(self, reg_id: int, value) -> None:
|
||||
# select register class for special cases
|
||||
reg_cls = UcAArch64.__select_reg_class(reg_id)
|
||||
|
||||
if reg_cls is None:
|
||||
if reg_id == const.UC_ARM64_REG_CP_REG:
|
||||
self._reg_write(reg_id, UcRegCP, value)
|
||||
|
||||
else:
|
||||
# fallback to default writing method
|
||||
super().reg_write(reg_id, value)
|
||||
|
||||
else:
|
||||
self._reg_write(reg_id, reg_cls, value)
|
|
@ -0,0 +1,178 @@
|
|||
# Intel architecture classes and structures.
|
||||
#
|
||||
# @author elicn
|
||||
|
||||
from typing import Any, Callable, Tuple
|
||||
|
||||
import ctypes
|
||||
|
||||
from .. import Uc, UcError
|
||||
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, 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)
|
||||
HOOK_INSN_OUT_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint32, ctypes.c_int, ctypes.c_uint32, ctypes.c_void_p)
|
||||
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(UcTupledReg[X86MMRReg]):
|
||||
"""Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.
|
||||
"""
|
||||
|
||||
_fields_ = (
|
||||
('selector', ctypes.c_uint16), # not used by GDTR and IDTR
|
||||
('base', ctypes.c_uint64), # handle 32 or 64 bit CPUs
|
||||
('limit', ctypes.c_uint32),
|
||||
('flags', ctypes.c_uint32) # not used by GDTR and IDTR
|
||||
)
|
||||
|
||||
|
||||
class UcRegMSR(UcTupledReg[X86MSRReg]):
|
||||
_fields_ = (
|
||||
('rid', ctypes.c_uint32),
|
||||
('val', ctypes.c_uint64)
|
||||
)
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
return self.val
|
||||
|
||||
|
||||
class UcRegFPR(UcTupledReg[X86FPReg]):
|
||||
_fields_ = (
|
||||
('mantissa', ctypes.c_uint64),
|
||||
('exponent', ctypes.c_uint16)
|
||||
)
|
||||
|
||||
|
||||
class UcIntel(Uc):
|
||||
"""Unicorn subclass for Intel architecture.
|
||||
"""
|
||||
|
||||
REG_RANGE_MMR = (
|
||||
const.UC_X86_REG_IDTR,
|
||||
const.UC_X86_REG_GDTR,
|
||||
const.UC_X86_REG_LDTR,
|
||||
const.UC_X86_REG_TR
|
||||
)
|
||||
|
||||
REG_RANGE_FP = range(const.UC_X86_REG_FP0, const.UC_X86_REG_FP7 + 1)
|
||||
REG_RANGE_XMM = range(const.UC_X86_REG_XMM0, const.UC_X86_REG_XMM31 + 1)
|
||||
REG_RANGE_YMM = range(const.UC_X86_REG_YMM0, const.UC_X86_REG_YMM31 + 1)
|
||||
REG_RANGE_ZMM = range(const.UC_X86_REG_ZMM0, const.UC_X86_REG_ZMM31 + 1)
|
||||
|
||||
def hook_add(self, htype: int, callback: Callable, user_data: Any = None, begin: int = 1, end: int = 0, aux1: int = 0, aux2: int = 0) -> int:
|
||||
if htype != UC_HOOK_INSN:
|
||||
return super().hook_add(htype, callback, user_data, begin, end, aux1, aux2)
|
||||
|
||||
insn = ctypes.c_int(aux1)
|
||||
|
||||
def __hook_insn_in():
|
||||
@uccallback(HOOK_INSN_IN_CFUNC)
|
||||
def __hook_insn_in_cb(handle: int, port: int, size: int, key: int) -> int:
|
||||
return callback(self, port, size, user_data)
|
||||
|
||||
return __hook_insn_in_cb
|
||||
|
||||
def __hook_insn_out():
|
||||
@uccallback(HOOK_INSN_OUT_CFUNC)
|
||||
def __hook_insn_out_cb(handle: int, port: int, size: int, value: int, key: int):
|
||||
callback(self, port, size, value, user_data)
|
||||
|
||||
return __hook_insn_out_cb
|
||||
|
||||
def __hook_insn_syscall():
|
||||
@uccallback(HOOK_INSN_SYSCALL_CFUNC)
|
||||
def __hook_insn_syscall_cb(handle: int, key: int):
|
||||
callback(self, user_data)
|
||||
|
||||
return __hook_insn_syscall_cb
|
||||
|
||||
def __hook_insn_cpuid():
|
||||
@uccallback(HOOK_INSN_CPUID_CFUNC)
|
||||
def __hook_insn_cpuid_cb(handle: int, key: int) -> int:
|
||||
return callback(self, user_data)
|
||||
|
||||
return __hook_insn_cpuid_cb
|
||||
|
||||
handlers = {
|
||||
const.UC_X86_INS_IN : __hook_insn_in,
|
||||
const.UC_X86_INS_OUT : __hook_insn_out,
|
||||
const.UC_X86_INS_SYSCALL : __hook_insn_syscall,
|
||||
const.UC_X86_INS_SYSENTER : __hook_insn_syscall,
|
||||
const.UC_X86_INS_CPUID : __hook_insn_cpuid
|
||||
}
|
||||
|
||||
handler = handlers.get(insn.value)
|
||||
|
||||
if handler is None:
|
||||
raise UcError(UC_ERR_ARG)
|
||||
|
||||
fptr = handler()
|
||||
|
||||
return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn)
|
||||
|
||||
@staticmethod
|
||||
def __select_reg_class(reg_id: int):
|
||||
"""Select class for special architectural registers.
|
||||
"""
|
||||
|
||||
reg_class = (
|
||||
(UcIntel.REG_RANGE_MMR, UcRegMMR),
|
||||
(UcIntel.REG_RANGE_FP, UcRegFPR),
|
||||
(UcIntel.REG_RANGE_XMM, UcReg128),
|
||||
(UcIntel.REG_RANGE_YMM, UcReg256),
|
||||
(UcIntel.REG_RANGE_ZMM, UcReg512)
|
||||
)
|
||||
|
||||
return next((cls for rng, cls in reg_class if reg_id in rng), None)
|
||||
|
||||
def reg_read(self, reg_id: int, aux: Any = None):
|
||||
# select register class for special cases
|
||||
reg_cls = UcIntel.__select_reg_class(reg_id)
|
||||
|
||||
if reg_cls is None:
|
||||
# backward compatibility: msr read through reg_read
|
||||
if reg_id == const.UC_X86_REG_MSR:
|
||||
if type(aux) is not int:
|
||||
raise UcError(UC_ERR_ARG)
|
||||
|
||||
value = self.msr_read(aux)
|
||||
|
||||
else:
|
||||
value = super().reg_read(reg_id, aux)
|
||||
else:
|
||||
value = self._reg_read(reg_id, reg_cls)
|
||||
|
||||
return value
|
||||
|
||||
def reg_write(self, reg_id: int, value) -> None:
|
||||
# select register class for special cases
|
||||
reg_cls = UcIntel.__select_reg_class(reg_id)
|
||||
|
||||
if reg_cls is None:
|
||||
# backward compatibility: msr write through reg_write
|
||||
if reg_id == const.UC_X86_REG_MSR:
|
||||
if type(value) is not tuple or len(value) != 2:
|
||||
raise UcError(UC_ERR_ARG)
|
||||
|
||||
self.msr_write(*value)
|
||||
return
|
||||
|
||||
super().reg_write(reg_id, value)
|
||||
else:
|
||||
self._reg_write(reg_id, reg_cls, value)
|
||||
|
||||
def msr_read(self, msr_id: int) -> int:
|
||||
return self._reg_read(const.UC_X86_REG_MSR, UcRegMSR, msr_id)
|
||||
|
||||
def msr_write(self, msr_id: int, value: int) -> None:
|
||||
self._reg_write(const.UC_X86_REG_MSR, UcRegMSR, (msr_id, value))
|
|
@ -0,0 +1,92 @@
|
|||
# Common types and structures.
|
||||
#
|
||||
# @author elicn
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import Generic, Tuple, TypeVar
|
||||
|
||||
import ctypes
|
||||
|
||||
uc_err = ctypes.c_int
|
||||
uc_engine = ctypes.c_void_p
|
||||
uc_context = ctypes.c_void_p
|
||||
uc_hook_h = ctypes.c_size_t
|
||||
|
||||
|
||||
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.
|
||||
|
||||
This class is meant to be inherited, not instantiated directly.
|
||||
"""
|
||||
|
||||
qwords: ctypes.Array
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
return sum(qword << (64 * i) for i, qword in enumerate(self.qwords))
|
||||
|
||||
@classmethod
|
||||
def from_value(cls, value: int):
|
||||
assert type(value) is int
|
||||
|
||||
mask = (1 << 64) - 1
|
||||
size = cls._fields_[0][1]._length_
|
||||
|
||||
return cls(tuple((value >> (64 * i)) & mask for i in range(size)))
|
||||
|
||||
|
||||
class UcReg128(UcLargeReg):
|
||||
_fields_ = [('qwords', ctypes.c_uint64 * 2)]
|
||||
|
||||
|
||||
class UcReg256(UcLargeReg):
|
||||
_fields_ = [('qwords', ctypes.c_uint64 * 4)]
|
||||
|
||||
|
||||
class UcReg512(UcLargeReg):
|
||||
_fields_ = [('qwords', ctypes.c_uint64 * 8)]
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue