|
| 1 | +# AArch64 classes and structures. |
| 2 | +# |
| 3 | +# @author elicn |
| 4 | + |
| 5 | +from typing import Any, Callable, NamedTuple, Tuple |
| 6 | + |
| 7 | +import ctypes |
| 8 | + |
| 9 | +from .. import Uc, UcError |
| 10 | +from .. import arm64_const as const |
| 11 | + |
| 12 | +from ..unicorn import uccallback |
| 13 | +from unicorn import UC_ERR_ARG, UC_HOOK_INSN |
| 14 | +from .types import uc_engine, UcTupledReg, UcReg128 |
| 15 | + |
| 16 | +ARM64CPReg = Tuple[int, int, int, int, int, int] |
| 17 | + |
| 18 | +HOOK_INSN_SYS_CFUNC = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p) |
| 19 | + |
| 20 | + |
| 21 | +class UcRegCP(UcTupledReg[ARM64CPReg]): |
| 22 | + """ARM64 coprocessors registers for instructions MRS, MSR |
| 23 | + """ |
| 24 | + |
| 25 | + _fields_ = ( |
| 26 | + ('crn', ctypes.c_uint32), |
| 27 | + ('crm', ctypes.c_uint32), |
| 28 | + ('op0', ctypes.c_uint32), |
| 29 | + ('op1', ctypes.c_uint32), |
| 30 | + ('op2', ctypes.c_uint32), |
| 31 | + ('val', ctypes.c_uint64) |
| 32 | + ) |
| 33 | + |
| 34 | + @property |
| 35 | + def value(self) -> int: |
| 36 | + return self.val |
| 37 | + |
| 38 | + |
| 39 | +class UcAArch64(Uc): |
| 40 | + """Unicorn subclass for ARM64 architecture. |
| 41 | + """ |
| 42 | + |
| 43 | + REG_RANGE_Q = range(const.UC_ARM64_REG_Q0, const.UC_ARM64_REG_Q31 + 1) |
| 44 | + REG_RANGE_V = range(const.UC_ARM64_REG_V0, const.UC_ARM64_REG_V31 + 1) |
| 45 | + |
| 46 | + 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: |
| 47 | + if htype != UC_HOOK_INSN: |
| 48 | + return super().hook_add(htype, callback, user_data, begin, end, aux1, aux2) |
| 49 | + |
| 50 | + insn = ctypes.c_int(aux1) |
| 51 | + |
| 52 | + def __hook_insn_sys(): |
| 53 | + @uccallback(HOOK_INSN_SYS_CFUNC) |
| 54 | + def __hook_insn_sys_cb(handle: int, reg: int, pcp_reg: Any, key: int) -> int: |
| 55 | + cp_reg = ctypes.cast(pcp_reg, ctypes.POINTER(UcRegCP)).contents |
| 56 | + |
| 57 | + class CpReg(NamedTuple): |
| 58 | + crn: int |
| 59 | + crm: int |
| 60 | + op0: int |
| 61 | + op1: int |
| 62 | + op2: int |
| 63 | + val: int |
| 64 | + |
| 65 | + cp_reg = CpReg(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val) |
| 66 | + |
| 67 | + return callback(self, reg, cp_reg, user_data) |
| 68 | + |
| 69 | + return __hook_insn_sys_cb |
| 70 | + |
| 71 | + handlers = { |
| 72 | + const.UC_ARM64_INS_MRS : __hook_insn_sys, |
| 73 | + const.UC_ARM64_INS_MSR : __hook_insn_sys, |
| 74 | + const.UC_ARM64_INS_SYS : __hook_insn_sys, |
| 75 | + const.UC_ARM64_INS_SYSL : __hook_insn_sys |
| 76 | + } |
| 77 | + |
| 78 | + handler = handlers.get(insn.value) |
| 79 | + |
| 80 | + if handler is None: |
| 81 | + raise UcError(UC_ERR_ARG) |
| 82 | + |
| 83 | + fptr = handler() |
| 84 | + |
| 85 | + return getattr(self, '_Uc__do_hook_add')(htype, fptr, begin, end, insn) |
| 86 | + |
| 87 | + @staticmethod |
| 88 | + def __select_reg_class(reg_id: int): |
| 89 | + """Select class for special architectural registers. |
| 90 | + """ |
| 91 | + |
| 92 | + reg_class = ( |
| 93 | + (UcAArch64.REG_RANGE_Q, UcReg128), |
| 94 | + (UcAArch64.REG_RANGE_V, UcReg128) |
| 95 | + ) |
| 96 | + |
| 97 | + return next((cls for rng, cls in reg_class if reg_id in rng), None) |
| 98 | + |
| 99 | + def reg_read(self, reg_id: int, aux: Any = None): |
| 100 | + # select register class for special cases |
| 101 | + reg_cls = UcAArch64.__select_reg_class(reg_id) |
| 102 | + |
| 103 | + if reg_cls is None: |
| 104 | + if reg_id == const.UC_ARM64_REG_CP_REG: |
| 105 | + return self._reg_read(reg_id, UcRegCP, *aux) |
| 106 | + |
| 107 | + else: |
| 108 | + # fallback to default reading method |
| 109 | + return super().reg_read(reg_id, aux) |
| 110 | + |
| 111 | + return self._reg_read(reg_id, reg_cls) |
| 112 | + |
| 113 | + def reg_write(self, reg_id: int, value) -> None: |
| 114 | + # select register class for special cases |
| 115 | + reg_cls = UcAArch64.__select_reg_class(reg_id) |
| 116 | + |
| 117 | + if reg_cls is None: |
| 118 | + if reg_id == const.UC_ARM64_REG_CP_REG: |
| 119 | + self._reg_write(reg_id, UcRegCP, value) |
| 120 | + |
| 121 | + else: |
| 122 | + # fallback to default writing method |
| 123 | + super().reg_write(reg_id, value) |
| 124 | + |
| 125 | + else: |
| 126 | + self._reg_write(reg_id, reg_cls, value) |
0 commit comments