Shofel2_T124_python/venv/lib/python3.10/site-packages/qiling/arch/utils.py

123 lines
4.3 KiB
Python
Raw Normal View History

2024-05-25 16:45:07 +00:00
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
"""
This module is intended for general purpose functions that are only used in qiling.arch
"""
from typing import Tuple
from os.path import basename
from functools import lru_cache
from keystone import (Ks, KS_ARCH_ARM, KS_ARCH_ARM64, KS_ARCH_MIPS, KS_ARCH_X86, KS_ARCH_PPC,
KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_PPC32, KS_MODE_16, KS_MODE_32, KS_MODE_64,
KS_MODE_LITTLE_ENDIAN, KS_MODE_BIG_ENDIAN)
from qiling import Qiling
from qiling.const import QL_ARCH, QL_ENDIAN, QL_VERBOSE
class QlArchUtils:
def __init__(self, ql: Qiling):
self.ql = ql
self._disasm_hook = None
self._block_hook = None
@lru_cache(maxsize=64)
def get_base_and_name(self, addr: int) -> Tuple[int, str]:
for begin, end, _, name, _ in self.ql.mem.map_info:
if begin <= addr < end:
return begin, basename(name)
return addr, '-'
def disassembler(self, ql: Qiling, address: int, size: int):
data = ql.mem.read(address, size)
# knowing that all binary sections are aligned to page boundary allows
# us to 'cheat' and search for the containing image using the aligned
# address instead of the actual one.
#
# also, the locality property determines that consequent instructions
# are most likely to reside at the same page in memory, so the containing
# page of the current instruction is probably the same as the previous
# one.
#
# both assumptions make it possible to cache the search results and pull
# them off by lru, which provides about 20% speed-up in this case
ba, name = self.get_base_and_name(ql.mem.align(address))
anibbles = ql.arch.bits // 4
for insn in ql.arch.disassembler.disasm(data, address):
offset = insn.address - ba
ql.log.info(f'{insn.address:0{anibbles}x} [{name:20s} + {offset:#08x}] {insn.bytes.hex(" "):20s} {insn.mnemonic:20s} {insn.op_str}')
if ql.verbose >= QL_VERBOSE.DUMP:
for reg in ql.arch.regs.register_mapping:
ql.log.info(f'{reg:10s} : {ql.arch.regs.read(reg):#x}')
def setup_output(self, verbosity: QL_VERBOSE):
def ql_hook_block_disasm(ql: Qiling, address: int, size: int):
self.ql.log.info(f'\nTracing basic block at {address:#x}')
if self._disasm_hook:
self._disasm_hook.remove()
self._disasm_hook = None
if self._block_hook:
self._block_hook.remove()
self._block_hook = None
if verbosity >= QL_VERBOSE.DISASM:
try: # monkey patch disassembler
from qiling.extensions.r2 import R2
r2 = R2(self.ql)
self._disasm_hook = self.ql.hook_code(r2.disassembler)
except (ImportError, ModuleNotFoundError):
self._disasm_hook = self.ql.hook_code(self.disassembler)
if verbosity >= QL_VERBOSE.DUMP:
self._block_hook = self.ql.hook_block(ql_hook_block_disasm)
# used by qltool prior to ql instantiation. to get an assembler object
# after ql instantiation, use the appropriate ql.arch method
def assembler(arch: QL_ARCH, endianess: QL_ENDIAN, is_thumb: bool) -> Ks:
"""Instantiate an assembler object for a specified architecture.
Args:
arch: architecture type
endianess: architecture endianess
is_thumb: thumb mode for ARM (ignored otherwise)
Returns: an assembler object
"""
endian = {
QL_ENDIAN.EL: KS_MODE_LITTLE_ENDIAN,
QL_ENDIAN.EB: KS_MODE_BIG_ENDIAN
}[endianess]
thumb = KS_MODE_THUMB if is_thumb else 0
asm_map = {
QL_ARCH.CORTEX_M: (KS_ARCH_ARM, KS_MODE_ARM + KS_MODE_LITTLE_ENDIAN + KS_MODE_THUMB),
QL_ARCH.ARM: (KS_ARCH_ARM, KS_MODE_ARM + endian + thumb),
QL_ARCH.ARM64: (KS_ARCH_ARM64, KS_MODE_ARM),
QL_ARCH.MIPS: (KS_ARCH_MIPS, KS_MODE_MIPS32 + endian),
QL_ARCH.A8086: (KS_ARCH_X86, KS_MODE_16),
QL_ARCH.X86: (KS_ARCH_X86, KS_MODE_32),
QL_ARCH.X8664: (KS_ARCH_X86, KS_MODE_64),
QL_ARCH.PPC: (KS_ARCH_PPC, KS_MODE_PPC32 + KS_MODE_BIG_ENDIAN)
}
if arch in asm_map:
return Ks(*asm_map[arch])
raise NotImplementedError