예제 #1
0
def _symbolicate_overrides_for_classinfo(classinfo, processed):
    """A recursive function to symbolicate vtable overrides for a class and its superclasses."""
    # If we've already been processed, stop.
    if classinfo in processed:
        return
    # First propagate symbol information to our superclass.
    if classinfo.superclass:
        _symbolicate_overrides_for_classinfo(classinfo.superclass, processed)
    # Now symbolicate the superclass.
    for _, override, original in class_vtable_overrides(classinfo, methods=True):
        # Skip this method if the override already has a name and we can't rename it.
        override_name = idau.get_ea_name(override, user=True)
        if override_name and not _ok_to_rename_method(override, override_name):
            continue
        # Skip this method if the original does not have a name or if it's a bad name.
        original_name = idau.get_ea_name(original, user=True)
        if not original_name or _bad_name_dont_use_as_override(original_name):
            continue
        # Get the new override name if we substitute for the override class's name.
        new_name = _vtable_method_symbol_substitute_class(original_name, classinfo.classname)
        if not new_name:
            _log(0, 'Could not substitute class {} into method symbol {} for override {:#x}',
                    classinfo.classname, original_name, override)
            continue
        # Now that we have the new name, set it.
        if override_name:
            _log(2, 'Renaming {} -> {}', override_name, new_name)
        if not idau.set_ea_name(override, new_name, rename=True):
            _log(0, 'Could not set name {} for method {:#x}', new_name, override)
    # We're done.
    processed.add(classinfo)
예제 #2
0
def _process_offset(offset, ea, next_offset):
    """Process an offset in a __got section."""
    # Convert the address containing the offset into an offset in IDA, but continue if it fails.
    if not idc.OpOff(ea, 0, 0):
        _log(1, 'Could not convert {:#x} into an offset', ea)
    # Get the name to which the offset refers.
    name = idau.get_ea_name(offset, user=True)
    if not name:
        _log(3, 'Offset at address {:#x} has target {:#x} without a name', ea,
             offset)
        return False
    # Make sure this isn't an offset to another stub or to a jump function to another stub. See the
    # comment in _symbolicate_stub.
    if stub.symbol_references_stub(name):
        _log(
            1,
            'Offset at address {:#x} has target {:#x} (name {}) that references a stub',
            ea, offset, name)
        return False
    # Set the new name for the offset.
    symbol = next_offset(name)
    if symbol is None:
        _log(0, 'Could not generate offset symbol for {}: names exhausted',
             name)
        return False
    if not idau.set_ea_name(ea, symbol, auto=True):
        _log(2, 'Could not set name {} for offset at {:#x}', symbol, ea)
        return False
    return True
예제 #3
0
def _populate_vmethods_struct(sid, classinfo):
    """Populate the ::vmethods struct."""
    # Loop over the new vtable methods.
    super_nmethods = 0
    if classinfo.superclass:
        super_nmethods = classinfo.superclass.vtable_nmethods
    members = set()
    for index, vmethod in enumerate(vtable.class_vtable_methods(classinfo)):
        # Skip entries in the superclass's vtable.
        if index < super_nmethods:
            continue
        # Get the base name of the method (i.e., for Class::method(args), extract method).
        sym = idau.get_ea_name(vmethod, user=True)
        base = symbol.method_name(sym)
        if not base:
            base = 'method_{}'.format(index)
        base = symbol.make_ident(base)
        # We'll try to use the base as our method name, but if it already exists, try appending
        # "_1", "_2", etc.
        name = base
        suffix = 0
        while name in members:
            suffix += 1
            name = '{}_{}'.format(base, suffix)
        members.add(name)
        # Create the member.
        offset = (index - super_nmethods) * idau.WORD_SIZE
        ret = idau.struct_add_ptr(sid, name, offset, type='void *')
        if ret != 0:
            _log(0, 'Could not create {}::vmethods.{}: {}',
                 classinfo.classname, name, ret)
            return False
    return True
예제 #4
0
def _symbolicate_stub(stub, target, next_stub):
    """Set a symbol for a stub function."""
    name = idau.get_ea_name(target, username=True)
    if not name:
        _log(3, 'Stub {:#x} has target {:#x} without a name', stub, target)
        return False
    # Sometimes the target of the stub is a thunk in another kext. This is sometimes OK, but makes
    # a right mess of things when that thunk is itself a jump function for another stub, and
    # especially when there are multiple such jump functions to that stub in that kext.
    # Autorenaming of thunks interacts poorly with autonaming of stubs (you get things like
    # 'j_TARGET___stub_2_0', which stub_name_target() no longer thinks of as a stub). Thus, if the
    # current thing has '__stub_' in it, don't rename. The reason we don't just extract the inner
    # stub reference is that these jump functions are really wrappers with different names and
    # semantics in the original code, so it's not appropriate for us to cover that up with a stub.
    if symbol_references_stub(name):
        _log(
            1,
            'Stub {:#x} has target {:#x} (name {}) that references another stub',
            stub, target, name)
        return False
    symbol = next_stub(name)
    if symbol is None:
        _log(0, 'Could not generate stub symbol for {}: names exhausted', name)
        return False
    if not idau.set_ea_name(stub, symbol, auto=True):
        _log(2, 'Could not set name {} for stub at {:#x}', symbol, stub)
        return False
    return True
예제 #5
0
def _process_stubs_section(segstart, make_thunk, next_stub):
    """Process all the functions in a __stubs section."""
    segend = idc.SegEnd(segstart)
    # We'll go through each address and check if it has a reference. If it does, it is likely a
    # stub. As long as the address doesn't already have a stub name, process it.
    for ea in idau.Addresses(segstart, segend, step=1):
        if idc.isRef(idc.GetFlags(ea)) and not stub_name_target(idau.get_ea_name(ea)):
            _process_possible_stub(ea, make_thunk, next_stub)
예제 #6
0
def _process_offsets_section(segstart, next_offset):
    """Process all the offsets in a __got section."""
    for offset, ea in idau.ReadWords(segstart, idc.SegEnd(segstart), addresses=True):
        if not offset_name_target(idau.get_ea_name(ea)):
            # This is not a previously named offset.
            if idau.is_mapped(offset, value=False):
                _process_offset(offset, ea, next_offset)
            else:
                _log(-1, 'Offset {:#x} at address {:#x} is unmapped', offset, ea)
예제 #7
0
def add_vtable_symbol(vtable, classname):
    """Add a symbol for the virtual method table at the specified address.

    Arguments:
        vtable: The address of the virtual method table.
        classname: The name of the C++ class with this virtual method table.

    Returns:
        True if the data was successfully converted into a vtable and the symbol was added.
    """
    vtable_symbol = vtable_symbol_for_class(classname)
    if not idau.set_ea_name(vtable, vtable_symbol):
        _log(0, 'Address {:#x} already has name {} instead of vtable symbol {}'
                .format(vtable, idau.get_ea_name(vtable), vtable_symbol))
        return False
    return True
예제 #8
0
def add_metaclass_symbol(metaclass, classname):
    """Add a symbol for the OSMetaClass instance at the specified address.

    Arguments:
        metaclass: The address of the OSMetaClass instance.
        classname: The name of the C++ class with this OSMetaClass instance.

    Returns:
        True if the OSMetaClass instance's symbol was created successfully.
    """
    metaclass_symbol = metaclass_symbol_for_class(classname)
    if not idau.set_ea_name(metaclass, metaclass_symbol):
        _log(0, 'Address {:#x} already has name {} instead of OSMetaClass instance symbol {}'
                .format(metaclass, idau.get_ea_name(metaclass), metaclass_symbol))
        return False
    return True
 def found_vtable(metaclass, vtable, length):
     # Add our vtable length.
     vtable_lengths[vtable] = length
     # If our classname has a defined vtable symbol and that symbol's address isn't this vtable,
     # don't add the link.
     classname = metaclass_info[metaclass].classname
     proper_vtable_symbol = symbol.vtable_symbol_for_class(classname)
     proper_vtable_symbol_ea = idau.get_name_ea(proper_vtable_symbol)
     if proper_vtable_symbol_ea not in (idc.BADADDR, vtable):
         return
     # If our vtable has a symbol and it doesn't match the metaclass, skip adding a link.
     vtable_symbol = idau.get_ea_name(vtable, user=True)
     if vtable_symbol:
         vtable_classname = symbol.vtable_symbol_get_class(vtable_symbol)
         if vtable_classname != classname:
             _log(
                 2,
                 'Declining association between metaclass {:x} ({}) and vtable {:x} ({})',
                 metaclass, classname, vtable, vtable_classname)
             return
     # Add a link if they are in the same kext.
     if segment.kernelcache_kext(metaclass) == segment.kernelcache_kext(
             vtable):
         metaclass_to_vtable_builder.add_link(metaclass, vtable)