import logging
logger = logging.getLogger(__name__)
import re
from termcolor import cprint, colored
from prettytable import PrettyTable
[docs]class AssemblyBlock(object):
"""Represents an assembly block."""
def __init__(self, process, address):
self._process = process
self.instructions = []
self.address = address
self._parse_block()
def _parse_block(self):
address = self.address
end_block = ['ret', 'call', 'branch_relative']
while True:
self.instructions.append(AssemblyInstruction(self._process, address))
for ender in end_block:
if ender in self.instructions[-1].groups:
return
address = self.instructions[-1].address_next
def __getitem__(self, item):
return self.instructions[item]
def __str__(self):
s = []
for i in self.instructions:
s.append(str(i))
return '\n'.join(s)
def __repr__(self):
attrs = ['AssemblyBlock', str(len(self.instructions)), 'instructions']
return '<' + ' '.join(attrs) + '>'
[docs]class AssemblyInstruction(object):
"""Represents an assembly instruction."""
def __init__(self, process, address=None):
"""
Args:
process: Process object
address (int, optional): Address for this instruction. Will load
from this address.
"""
self._process = process
self.address = address
[docs] @classmethod
def from_frida_dict(klass, process, d):
"""Builds this assembly instruction from a frida dictionary, ala Instruction.parse()"""
instruction = klass(process)
instruction._parse_instruction(d)
return instruction
def _parse_instruction(self, inst):
"""Given an instruction dict as returned from Frida, parse it into this object."""
self.__address = types.Pointer(common.auto_int(inst['address']))
self.__address_next = types.Pointer(common.auto_int(inst['next']))
self.__size = inst['size']
self.__mnemonic = inst['mnemonic']
self.__args_str = inst['opStr']
self.__operands = inst['operands']
self.__registers_read = inst['regsRead']
self.__registers_written = inst['regsWritten']
self.__groups = inst['groups']
def _load_from_address(self):
inst = self._process.engine.run_script_generic("""send(Instruction.parse({}));""".format(self.address.js), raw=True, unload=True)[0][0]
self._parse_instruction(inst)
def __str__(self):
return "{address} {mnemonic: <20}{args}".format(
address=colored(hex(self.address), attrs=['bold']) + ":",
mnemonic=colored(self.mnemonic, "cyan"),
args=colored(self.args_str_resolved, "cyan", attrs=['bold']),
)
def __repr__(self):
attrs = ['AssemblyInstruction', hex(self.address), self.mnemonic, self.args_str]
return '<' + ' '.join(attrs) + '>'
def __hash__(self):
return hash((self.address, self.args_str))
@property
def args_str_resolved(self):
"""str: Attempt to resolve addresses in the args str into symbols."""
s = self.args_str
things = re.findall('0x[0-9a-f]+', s)
for thing in things:
sym = self._process.modules.lookup_symbol(int(thing,16))
if sym is not None:
s = s.replace(thing, str(sym))
return s
@property
def groups(self):
"""list: List of descriptive groups that this instruction belongs to."""
return self.__groups
@property
def registers_written(self):
"""list: List of registers written by this instruction."""
return self.__registers_written
@property
def registers_read(self):
"""list: List of registers that are read by this instruction."""
return self.__registers_read
@property
def operands(self):
"""list: List of operands."""
return self.__operands
@property
def args_str(self):
"""str: Operation arguments as a string."""
return self.__args_str
@property
def mnemonic(self):
"""str: Operation mnemonic."""
return self.__mnemonic
@property
def size(self):
"""int: Size of this instruction in bytes."""
return self.__size
@property
def address_next(self):
"""Pointer: Address of instruction following this one."""
return self.__address_next
@property
def address(self):
"""Pointer: Address where this instruction is located."""
return self.__address
@address.setter
def address(self, address):
if address is None:
self.__address = None
return
self.__address = types.Pointer(common.auto_int(address))
self._load_from_address()
from ... import types, common