123 lines
4.3 KiB
Python
123 lines
4.3 KiB
Python
#!/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
|