Beispiel #1
0
def _create_class_structs__slices(classinfo, endmarkers=True):
    """Create the IDA structs for a C++ class."""
    classname = classinfo.classname
    # Open or create the structs.
    sidf = idau.struct_open(classname + '::fields', create=True)
    sid = idau.struct_open(classname, create=True)
    if sid is None or sidf is None:
        _log(0, 'Could not create class structs for {}', classname)
        return None
    assert all(not idc.IsUnion(s) for s in (sidf, sid))
    # Calculate the size of the ::fields struct.
    if classinfo.superclass:
        # If we have a superclass, our fields start after our superclass's fields end.
        fields_start = classinfo.superclass.class_size
    else:
        # If we don't have a superclass, our fields start after our vtable.
        fields_start = idau.WORD_SIZE
    fields_size = classinfo.class_size - fields_start
    # Add an ::end member to the fields struct if requested.
    if endmarkers:
        ret = idc.AddStrucMember(sidf, classname + '::end', fields_size,
                                 idc.FF_UNK, -1, 0)
        if ret not in (0, idc.STRUC_ERROR_MEMBER_NAME,
                       idc.STRUC_ERROR_MEMBER_OFFSET):
            # If that didn't work that's too bad, but continue anyway.
            _log(0, 'Could not create {}::end', classname)
    return sid, sidf, fields_start
Beispiel #2
0
def _create_class_structs__unions(classinfo):
    """Create the IDA structs for a C++ class."""
    classname = classinfo.classname
    sidf = idau.struct_open(classname + '::fields', create=True)
    sid = idau.struct_open(classname, union=True, create=True)
    if sid is None or sidf is None:
        _log(0, 'Could not create class structs for {}', classname)
        return None
    return sid, sidf
Beispiel #3
0
def _populate_wrapper_struct__slices(sid, classinfo):
    """Fill in the members of the wrapper struct."""
    # First add the vtable pointer.
    offset = 0
    vtable_ptr_type = '{}::vtable *'.format(classinfo.classname)
    ret = idau.struct_add_ptr(sid, 'vtable', offset, type=vtable_ptr_type)
    if ret not in (0, idc.STRUC_ERROR_MEMBER_OFFSET):
        _log(0, 'Could not create {}.vtable: {}', classinfo.classname, ret)
        return False
    # Now add all the ::fields structs.
    offset += idau.WORD_SIZE
    for ci in classinfo.ancestors(inclusive=True):
        # Get the sid of the ::fields struct.
        fields_sid = idau.struct_open(ci.classname + '::fields')
        if fields_sid is None:
            _log(0, 'Could not find {}::fields', ci.classname)
            return False
        # If this is a 0-length struct (no fields), skip it.
        size = idc.GetStrucSize(fields_sid)
        if size == 0:
            continue
        # If this is already in the wrapper struct, skip it. This avoids weird
        # STRUC_ERROR_MEMBER_VARLAST errors.
        if idc.GetMemberOffset(sid, ci.classname) != -1:
            continue
        # Add the ::fields struct to the wrapper.
        ret = idau.struct_add_struct(sid, ci.classname, offset, fields_sid)
        if ret != 0:
            _log(0, 'Could not create {}.{}: {}', classinfo.classname,
                 ci.classname, ret)
            return False
        offset += size
    return True
Beispiel #4
0
def _populate_vtable_struct(sid, classinfo):
    """Populate the ::vtable struct."""
    # For each ancestor from root down to us (inclusive), add our ::vmethods struct.
    for ci in classinfo.ancestors(inclusive=True):
        # Get the offset at which the ::vmethods for ci will be.
        offset = 0
        if ci.superclass:
            offset = ci.superclass.vtable_nmethods * idau.WORD_SIZE
        # The size is ci's vtable length minus the offset.
        vmethods_size = ci.vtable_nmethods * idau.WORD_SIZE - offset
        # If the vmethods_size is 0, skip this entry. Otherwise we get weird
        # "struct->til conversion failed" errors.
        if vmethods_size == 0:
            continue
        # Get the sid for ci's ::vmethods.
        vmethods_sid = idau.struct_open(ci.classname + '::vmethods')
        if vmethods_sid is None:
            _log(0, 'Could not find {}::vmethods', ci.classname)
            return False
        # Add this ::vmethods slice to the ::vtable struct.
        ret = idau.struct_add_struct(sid, ci.classname, offset, vmethods_sid)
        if ret != 0:
            _log(0, 'Could not add {}::vmethods to {}::vtable', ci.classname,
                 classinfo.classname)
            return False
    return True
def create_struct_fields(sid=None,
                         name=None,
                         accesses=None,
                         create=False,
                         base=0):
    """Create an IDA struct with fields corresponding to the specified access pattern.

    Given a sequence of (offset, size) tuples designating the valid access points to the struct,
    create fields in the struct at the corresponding positions.

    Options:
        sid: The struct id, if the struct already exists.
        name: The name of the struct to update or create.
        accesses: The set of (offset, size) tuples representing the valid access points in the
            struct.
        create: If True, then the struct will be created with the specified name if it does not
            already exist. Default is False.
        base: The base offset for the struct. Offsets smaller than this are ignored, otherwise the
            field is created at the offset minus the base. Default is 0.

    Either sid or name must be specified.
    """
    # Get the struct id.
    if sid is None:
        sid = idau.struct_open(name, create=True)
        if sid is None:
            _log(0, 'Could not open struct {}', name)
            return False
    else:
        name = idc.GetStrucName(sid)
        if name is None:
            _log(0, 'Invalid struct id {}', sid)
            return False
    # Now, for each (offset, size) pair, create a struct member. Right now we completely ignore the
    # possibility that some members will overlap (for various reasons; it's actually more common
    # than I initially thought, though I haven't investigated why).
    # TODO: In the future we should address this by either automatically generating sub-unions or
    # choosing the most appropriate member when permissible (e.g. (0, 8), (0, 2), (4, 4) might
    # create (0, 2), (2, 2), (4, 4)). I think the most reasonable default policy is to create the
    # biggest members that satisfy all accesses.
    success = True
    for offset, size in accesses:
        if offset < base:
            continue
        member = field_name(offset)
        ret = idau.struct_add_word(sid, member, offset - base, size)
        if ret != 0:
            if ret == idc.STRUC_ERROR_MEMBER_OFFSET:
                _log(2, 'Could not add {}.{} for access ({}, {})', name,
                     member, offset, size)
            else:
                success = False
                _log(1, 'Could not add {}.{} for access ({}, {}): {}', name,
                     member, offset, size, ret)
    return success
Beispiel #6
0
def _convert_operands_to_struct_offsets(access_addresses):
    """Convert the operands that generated struct accesses into struct offsets."""
    for classname, addresses_and_deltas in access_addresses.items():
        sid = idau.struct_open(classname)
        if sid is not None:
            for ea, delta in addresses_and_deltas:
                insn = idautils.DecodeInstruction(ea)
                if insn:
                    for op in insn.Operands:
                        if op.type == idaapi.o_displ:
                            if not idau.insn_op_stroff(insn, op.n, sid, delta):
                                _log(1, 'Could not convert {:#x} to struct offset for class {} '
                                        'delta {}', ea, classname, delta)
Beispiel #7
0
def _propagate_virtual_method_type_for_method(classinfo, class_vindex, vmethod):
    """Propagate the type of a class's virtual method to the vtable struct."""
    if not idau.is_function_start(vmethod):
        _log(2, 'Not a function start: {:x}', vmethod)
        return False
    vmethod_type = idc.GuessType(vmethod)
    if not vmethod_type:
        _log(2, 'No guessed type: {:x}', vmethod)
        return False
    vmethod_ptr_type = symbol.convert_function_type_to_function_pointer_type(vmethod_type)
    if not vmethod_ptr_type:
        _log(2, 'Could not convert to function pointer type: {:x}', vmethod)
        return False
    vmethods_sid = idau.struct_open(classinfo.classname + '::vmethods')
    vmethod_offset = class_vindex * idau.WORD_SIZE
    vmethod_mid = idc.GetMemberId(vmethods_sid, vmethod_offset)
    if not bool(idc.SetType(vmethod_mid, vmethod_ptr_type)):
        _log(2, 'Could not set vmethod field type: {:x}, {}, {}', vmethod, classinfo.classname,
                class_vindex)
        return False
    return True
Beispiel #8
0
def _set_class_style(style):
    """Set the global class style."""
    global _style_was_set, _create_class_structs, _populate_class_structs
    assert style in (CLASS_SLICES, CLASS_UNIONS)
    # Check the current style based on OSObject, a class that should always exist.
    sid = idau.struct_open('OSObject')
    want_union = style == CLASS_UNIONS
    if sid is None:
        # No global style has been set.
        idau.struct_create('OSObject', union=want_union)
    else:
        # A style already exists. Check that the requested style matches.
        is_union = bool(idc.IsUnion(sid))
        if is_union != want_union:
            raise ValueError('Incompatible style {}', style)
    # Set the appropriate functions based on the style.
    if style == CLASS_SLICES:
        _create_class_structs = _create_class_structs__slices
        _populate_class_structs = _populate_class_structs__slices
    else:
        _create_class_structs = _create_class_structs__unions
        _populate_class_structs = _populate_class_structs__unions
Beispiel #9
0
def _populate_wrapper_struct__unions(sid, classinfo):
    """Fill in the members of the wrapper struct."""
    # First add the vtable pointer.
    vtable_ptr_type = '{}::vtable *'.format(classinfo.classname)
    ret = idau.struct_add_ptr(sid, 'vtable', -1, type=vtable_ptr_type)
    if ret not in (0, idc.STRUC_ERROR_MEMBER_NAME):
        _log(0, 'Could not create {}.vtable: {}', classinfo.classname, ret)
        return False
    # Now add all the ::fields structs.
    for ci in classinfo.ancestors(inclusive=True):
        # Get the sid of the ::fields struct.
        fields_sid = idau.struct_open(ci.classname + '::fields')
        if fields_sid is None:
            _log(0, 'Could not find {}::fields', ci.classname)
            return False
        # Add the ::fields struct to the wrapper. Ignore STRUC_ERROR_MEMBER_UNIVAR if the ::fields
        # struct has length 0.
        ret = idau.struct_add_struct(sid, ci.classname, -1, fields_sid)
        if ret not in (0, idc.STRUC_ERROR_MEMBER_NAME, idc.STRUC_ERROR_MEMBER_UNIVAR):
            _log(0, 'Could not create {}.{}: {}', classinfo.classname, ci.classname, ret)
            return False
    return True