Example #1
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)
Example #2
0
def initialize_data_offsets():
    """Convert offsets in data segments into offsets in IDA.

    Segment names must be initialized with segments.initialize_segments() first.
    """
    # Normally, for user-space programs, this operation would be dangerous because there's a good
    # chance that a valid userspace address would happen to show up in regular program data that is
    # not actually an address. However, since kernel addresses are numerically much larger, the
    # chance of this happening is much less.
    for seg in idautils.Segments():
        name = idc.SegName(seg)
        if not (name.endswith('__DATA_CONST.__const') or name.endswith('__got')
                or name.endswith('__DATA.__data')):
            continue
        for word, ea in idau.ReadWords(seg, idc.SegEnd(seg), addresses=True):
            if idau.is_mapped(word, value=False):
                idc.OpOff(ea, 0, 0)
def tagged_pointer_next(ea, tp, end=None):
    assert ea
    # First try to get the offset to the next link.
    if tp:
        link_offset = tagged_pointer_link(tagged_pointer_tag(tp))
        if link_offset:
            return ea + link_offset
        # Skip the current tagged pointer in preparation for scanning.
        ea += idau.WORD_SIZE
    # We don't have a link. Do a forward scan until we find the next tagged pointer.
    _log(3, 'Scanning for next tagged pointer')
    if end is None:
        end = idc.SegEnd(ea)
    for value, value_ea in idau.ReadWords(ea, end, step=4, addresses=True):
        if is_tagged_pointer(value):
            return value_ea
    # If we didn't find any tagged pointers at all, return None.
    return None
Example #4
0
def _initialize_kext_regions():
    """Get region information for each kext based on iOS 12's __PRELINK_INFO.__kmod_start.

    NOTE: This only accounts for __TEXT_EXEC, not the other segments."""
    kmod_start = idc.SegByBase(idc.SegByName('__PRELINK_INFO.__kmod_start'))
    if kmod_start == idc.BADADDR:
        return
    for kmod in idau.ReadWords(kmod_start, idc.SegEnd(kmod_start)):
        _log(1, 'Found kmod {:x}', kmod)
        segments = list(_macho_segments_and_sections(kmod))
        if len(segments) != 1:
            _log(0, 'Skipping unrecognized kmod {:x}', kmod)
            continue
        segname, segstart, segend, sects = segments[0]
        if segname != '__TEXT_EXEC' or len(sects) != 1:
            _log(0, 'Skipping unrecognized kmod {:x}', kmod)
            continue
        kmod_name = 'kext.{:x}'.format(kmod)
        _log(1, 'Adding module:  {:x} - {:x}  {}', segstart, segend, kmod_name)
        _kext_regions.append((segstart, segend, kmod_name))
def _process_mod_init_func_section_for_metaclasses(segstart, found_metaclass):
    """Process a __mod_init_func section for OSMetaClass information."""
    segend = idc.SegEnd(segstart)
    for func in idau.ReadWords(segstart, segend):
        _process_mod_init_func_for_metaclasses(func, found_metaclass)
Example #6
0
def vtable_length(ea, end=None, scan=False):
    """Find the length of a virtual method table.

    This function checks whether the effective address could correspond to a virtual method table
    and calculates its length, including the initial empty entries. By default (when scan is
    False), this function returns the length of the vtable if the address could correspond to a
    vtable, or 0 if the address definitely could not be a vtable.

    Arguments:
        ea: The linear address of the start of the vtable.

    Options:
        end: The end address to search through. Defaults to the end of the section.
        scan: Set to True to indicate that this function is being called to scan memory for virtual
            method tables. Instead of returning the length of the vtable or 0, this function will
            return a tuple (possible, length). Additionally, as a slight optimization, this
            function will sometimes look ahead in order to increase the amount of data that can be
            skipped, reducing duplication of effort between subsequent calls.

    Returns:
        If scan is False (the default), then this function returns the length of the vtable in
        words, including the initial empty entries.

        Otherwise, this function returns a tuple (possible, length). If the address could
        correspond to the start of a vtable, then possible is True and length is the length of the
        vtable in words, including the initial empty entries. Otherwise, if the address is
        definitely not the start of a vtable, then possible is False and length is the number of
        words that can be skipped when searching for the next vtable.
    """
    # TODO: This function should be reorganized. The better way of doing it is to count the number
    # of zero entries, then the number of nonzero entries, then decide based on that. Less
    # special-casing that way.
    # TODO: We should have a static=True/False flag to indicate whether we want to include the
    # empty entries.
    def return_value(possible, length):
        if scan:
            return possible, length
        return length if possible else 0
    # Initialize default values.
    if end is None:
        end = idc.SegEnd(ea)
    words = idau.ReadWords(ea, end)
    # Iterate through the first VTABLE_OFFSET words. If any of them are nonzero, then we can skip
    # past all the words we just saw.
    for idx, word in enumerate(islice(words, VTABLE_OFFSET)):
        if word != 0:
            return return_value(False, idx + 1)
    # Now this first word after the padding section is special.
    first = next(words, None)
    if first is None:
        # We have 2 zeros followed by the end of our range.
        return return_value(False, VTABLE_OFFSET)
    elif first == 0:
        # We have VTABLE_OFFSET + 1 zero entries.
        zeros = VTABLE_OFFSET + 1
        if scan:
            # To avoid re-reading the data we just read in the case of a zero-filled section, let's
            # look ahead a bit until we find the first non-zero value.
            for word in words:
                if word is None:
                    return return_value(False, zeros)
                if word != 0:
                    break
                zeros += 1
            else:
                # We found no nonzero words before the end.
                return return_value(False, zeros)
        # We can skip all but the last VTABLE_OFFSET zeros.
        return return_value(False, zeros - VTABLE_OFFSET)
    # TODO: We should verify that all vtable entries refer to code.
    # Now we know that we have at least one nonzero value, our job is easier. Get the full length
    # of the vtable, including the first VTABLE_OFFSET entries and the subsequent nonzero entries,
    # until either we find a zero word (not included) or run out of words in the stream.
    length = VTABLE_OFFSET + 1 + idau.iterlen(takewhile(lambda word: word != 0, words))
    # Now it's simple: We are valid if the length is long enough, invalid if it's too short.
    return return_value(length >= MIN_VTABLE_LENGTH, length)