def _ok_to_rename_method(override, name): """Some method names are ok to rename.""" return (name.startswith('j_') and idau.iterlen(idautils.XrefsTo(override)) == 1)
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)