Source code for devirtualize.itanium

''' This modules implements vtable, vtablegroup and typeinfo for the Itanium ABI.
For reference, read https://mentorembedded.github.io/cxx-abi/abi.html.
'''

import idaapi
import idautils
import idc

from .utils import *

[docs]class ItaniumTypeInfo(object): ''' ItaniumTypeInfo is the RTTI typeinfo representation for the Itanium ABI. ''' def __init__(self, ea): #: The address of the start of this RTTI object self.ea = ea #: A list (in inheritance order) of the parent RTTI objects self.parents = [] # first entry is the vptr for the typeinfo ea, _ = get_address(ea) ea, self.nameptr = get_address(ea) #: The name present in this RTTI entry self.name = idc.GetString(self.nameptr) # After the name is either the base class typeinfo pointer # (in the case of single inheritance), or an array of # typeinfos for each base (in multiple inheritance) _, baseaddr = get_address(ea) # Crude test for whether the address seems like a # plausible location for the baseclass typeinfo if in_same_segment(baseaddr, ea): self.parents = [ItaniumTypeInfo(baseaddr)] return # Either this is multiple inheritance or no inheritance flags = idaapi.get_32bit(ea) count = idaapi.get_32bit(ea + 4) ea += 8 # Only valid '__flags' are 0, 1, and 2 if flags not in [0, 1, 2]: return self.flags = flags for i in range(count): ea, baseaddr = get_address(ea) self.parents.append(ItaniumTypeInfo(baseaddr)) # For now ignore the '__offset_flags' ea += TARGET_ADDRESS_SIZE @property def str_ea(self): return "{:02x}".format(self.ea)
[docs]class ItaniumVtable(object): ''' The Vtable representation for the Itanium ABI ''' def __init__(self, ea): #: The address of the start of this vtable self.ea = ea ea, offset_to_top = get_address(ea) #: The offset to the top of the object for a subobject with this vtable self.offset_to_top = as_signed(offset_to_top, TARGET_ADDRESS_SIZE) # Arbitrary bounds for offset size if self.offset_to_top < -0xFFFFFF or self.offset_to_top > 0xFFFFFF: raise ValueError("Invalid table address `0x{:02x}`".format(self.ea)) ea, typeinfo = get_address(ea) #: Handle to the RTTI object associated with this vtable (if any) self.typeinfo = None if typeinfo != 0: if not in_same_segment(typeinfo, self.ea): raise ValueError("Invalid table address `0x{:02x}`".format(self.ea)) else: self.typeinfo = ItaniumTypeInfo(typeinfo) #: A list of function addresses in this vtable (some may be NULL) self.functions = [] #: The address of the start of the function array self.address_point = ea try: next(idautils.XrefsTo(self.address_point)) except StopIteration: raise ValueError("Invalid table address `0x{:02x}`".format(self.ea)) while True: ea, func = get_address(ea) # The first few function pointers can be 0 if not is_in_executable_segment(func): if func == 0 and all([f == 0 for f in self.functions]): pass else: break self.functions.append(func) # Because the first two functions can be zero, and the RTTI # pointer and base offset can also be zero, require at least # one function to not be zero (so blocks of zero don't match). if all([f == 0 for f in self.functions]): raise ValueError("Invalid table address `0x{:02x}`".format(self.ea)) #: The size in bytes of this vtable self.size = TARGET_ADDRESS_SIZE*(len(self.functions) + 2) @property def name(self): if self.typeinfo is None: return None return self.typeinfo.name @property def str_ea(self): ''' A string representation of ``self.ea``, in hex. (Clickable when printed in IDA) ''' return "{:02x}".format(self.ea)
[docs]class ItaniumVTableGroup(object): ''' A group of consecutive vtables in the Itanium ABI ''' def __init__(self, ea): #: Start address of the table group self.ea = ea #: A list of the tables comprising this group self.tables = [] prev_offset = None while True: try: table = ItaniumVtable(ea) except ValueError: break # Sanity check the offset if prev_offset is None and table.offset_to_top != 0: break elif prev_offset is not None and table.offset_to_top >= prev_offset: break prev_offset = table.offset_to_top self.tables.append(table) ea += table.size if not self.tables: raise ValueError("Invalid vtable group address `0x{:02x}`".format(self.ea))
[docs] def primary_table(self): ''' Same as `self.tables[0]` ''' return self.tables[0]
@property def typeinfo(self): ''' A handle to the typeinfo for the primary table (and so, the full object associated with this table group). ''' return self.primary_table().typeinfo @property def size(self): ''' The size in bytes of this table group ''' return sum([s.size for s in self.tables]) @property def name(self): prim = self.primary_table() if prim.typeinfo is not None: return prim.name return None @property def str_ea(self): return "{:02x}".format(self.ea)