118 lines
3.2 KiB
Python
118 lines
3.2 KiB
Python
|
#!/usr/bin/env python3
|
||
|
#
|
||
|
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||
|
#
|
||
|
|
||
|
from functools import cached_property, lru_cache
|
||
|
|
||
|
from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM, UC_MODE_THUMB, UC_MODE_BIG_ENDIAN
|
||
|
from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_THUMB, CS_MODE_BIG_ENDIAN
|
||
|
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_BIG_ENDIAN
|
||
|
|
||
|
from qiling import Qiling
|
||
|
from qiling.arch.arch import QlArch
|
||
|
from qiling.arch import arm_const
|
||
|
from qiling.arch.register import QlRegisterManager
|
||
|
from qiling.const import QL_ARCH, QL_ENDIAN
|
||
|
|
||
|
|
||
|
class QlArchARM(QlArch):
|
||
|
type = QL_ARCH.ARM
|
||
|
bits = 32
|
||
|
|
||
|
def __init__(self, ql: Qiling, endian: QL_ENDIAN, thumb: bool):
|
||
|
super().__init__(ql)
|
||
|
|
||
|
self._init_endian = endian
|
||
|
self._init_thumb = thumb
|
||
|
|
||
|
self.arm_get_tls_addr = 0xFFFF0FE0
|
||
|
|
||
|
@cached_property
|
||
|
def uc(self) -> Uc:
|
||
|
mode = UC_MODE_ARM
|
||
|
|
||
|
if self._init_endian == QL_ENDIAN.EB:
|
||
|
mode += UC_MODE_BIG_ENDIAN
|
||
|
|
||
|
if self._init_thumb:
|
||
|
mode += UC_MODE_THUMB
|
||
|
|
||
|
return Uc(UC_ARCH_ARM, mode)
|
||
|
|
||
|
@cached_property
|
||
|
def regs(self) -> QlRegisterManager:
|
||
|
regs_map = dict(
|
||
|
**arm_const.reg_map,
|
||
|
**arm_const.reg_vfp,
|
||
|
**arm_const.reg_map_q,
|
||
|
**arm_const.reg_map_s
|
||
|
)
|
||
|
|
||
|
pc_reg = 'pc'
|
||
|
sp_reg = 'sp'
|
||
|
|
||
|
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
|
||
|
|
||
|
@property
|
||
|
def is_thumb(self) -> bool:
|
||
|
return bool(self.regs.cpsr & (1 << 5))
|
||
|
|
||
|
@property
|
||
|
def endian(self) -> QL_ENDIAN:
|
||
|
return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
|
||
|
|
||
|
@property
|
||
|
def effective_pc(self) -> int:
|
||
|
"""Get effective PC value, taking Thumb mode into account.
|
||
|
"""
|
||
|
|
||
|
# append 1 to pc if in thumb mode, or 0 otherwise
|
||
|
return self.regs.pc | int(self.is_thumb)
|
||
|
|
||
|
@lru_cache(maxsize=4)
|
||
|
def __cached_disasm(self, mode: int) -> Cs:
|
||
|
return Cs(CS_ARCH_ARM, mode)
|
||
|
|
||
|
@property
|
||
|
def disassembler(self) -> Cs:
|
||
|
# note: since endianess and thumb mode might change during execution, we cannot
|
||
|
# cache the disassembler instance directly; rather we pick the appropriate cached
|
||
|
# instance
|
||
|
|
||
|
mode = CS_MODE_ARM
|
||
|
|
||
|
if self.endian == QL_ENDIAN.EB:
|
||
|
mode += CS_MODE_BIG_ENDIAN
|
||
|
|
||
|
if self.is_thumb:
|
||
|
mode += CS_MODE_THUMB
|
||
|
|
||
|
return self.__cached_disasm(mode)
|
||
|
|
||
|
@lru_cache(maxsize=4)
|
||
|
def __cached_asm(self, mode: int) -> Ks:
|
||
|
return Ks(KS_ARCH_ARM, mode)
|
||
|
|
||
|
@property
|
||
|
def assembler(self) -> Ks:
|
||
|
# note: since endianess and thumb mode might change during execution, we cannot
|
||
|
# cache the assembler instance directly; rather we pick the appropriate cached
|
||
|
# instance
|
||
|
|
||
|
mode = KS_MODE_ARM
|
||
|
|
||
|
if self.endian == QL_ENDIAN.EB:
|
||
|
mode += KS_MODE_BIG_ENDIAN
|
||
|
|
||
|
if self.is_thumb:
|
||
|
mode += KS_MODE_THUMB
|
||
|
|
||
|
return self.__cached_asm(mode)
|
||
|
|
||
|
def enable_vfp(self) -> None:
|
||
|
# set full access to cp10 and cp11
|
||
|
self.regs.c1_c0_2 = self.regs.c1_c0_2 | (0b11 << 20) | (0b11 << 22)
|
||
|
|
||
|
self.regs.fpexc = (1 << 30)
|