def populate_items(self): self.items = [] for (func_ea, xrefs) in self.profile.functions.iteritems(): if not self.function_filters or func_ea in self.function_filters: orig_items_len = len(self.items) for xref in xrefs: if not self.string_filters or \ xref.string in self.string_filters: if xref.type == callable: display_string = xref.string + "()" elif xref.type == str: display_string = '"%s"' % xref.string else: display_string = xref.string self.items.append([ ida_shims.get_name(func_ea), display_string, xref.xref, func_ea ]) if len(self.items) != orig_items_len: self.items.append([ self.DELIM_COL_1, self.DELIM_COL_2, idc.BADADDR, idc.BADADDR ]) # Remove the last delimiter column if self.items and self.items[-1][-1] == idc.BADADDR: self.items.pop(-1)
def rename_functions(self, debug=True, dry_run=False): ''' Renames functions starting with "sub_" based on unique string xrefs. @debug - Set to False to suppress debug output. @dry_run - Set to True to perform a dry run (functions will not actually be renamed). Returns the number of renamed functions. ''' count = 0 for (function_address, function_name) in self.func2str_mappings().items(): if ida_shims.get_name(function_address).startswith("sub_"): if dry_run or ida_shims.set_name(function_address, function_name): if debug: print("0x%.8X => %s" % (function_address, function_name)) count += 1 if debug: print("Renamed %d functions based on unique string xrefs!" % count) return count
def populate_items(self): self.items = [] for function in self.lb.functions: candidates = [] if function.leaf: if function.xrefs < self.min_xrefs: continue if function.loop is False and self.must_have_loop is True: continue for candidate in function.candidates: candidates.append(candidate) if function.xrefs: xrefs = str(function.xrefs) else: xrefs = "*" if function.argc is not None: argc = str(function.argc) else: argc = "*" if function.leaf: loops = str(function.loop) else: loops = "*" name = ida_shims.get_name(function.start) self.items.append([ name, xrefs, argc, loops, ', '.join(candidates), function.leaf ])
def __init__(self, **kwargs): self.argc = None self.loop = False self.leaf = False self.xrefs = None self.fmtarg = None self.start = idc.BADADDR self.end = idc.BADADDR self.candidates = {} self.argp = ArgParser() for (k, v) in kwargs.iteritems(): setattr(self, k, v) self.name = ida_shims.get_name(self.start) if self.xrefs is None: self.xrefs = len([x for x in idautils.XrefsTo(self.start)]) if not self.candidates: for prototype in self.PROTOTYPES: if self.leaf and prototype.fmtarg is None and \ prototype.argc == self.argc and prototype.loop == self.loop: if prototype.name in self.candidates: self.candidates[prototype.name] += 1 else: self.candidates[prototype.name] = 1 elif not self.leaf and \ self.fmtarg is not None and \ prototype.fmtarg is not None and \ self.fmtarg == prototype.fmtarg: if prototype.name in self.candidates: self.candidates[prototype.name] += 1 else: self.candidates[prototype.name] = 1
def _name(self, ea): name = ida_shims.get_name(ea) if not name: name = ida_shims.get_func_off_str(ea) if not name: name = '0x%X' % ea return name
def _find_and_plot_paths(self, sources, targets, klass=AlleyCatFunctionPaths): results = [] for target in targets: for source in sources: s = time.time() r = klass(source, target).paths e = time.time() print "Found %d paths in %f seconds." % (len(r), (e-s)) if r: results += r else: name = ida_shims.get_name(target) if not name: name = "0x%X" % target print "No paths found to", name if results: # Be sure to close any previous graph before creating a new one. # Failure to do so may crash IDA. try: self.graph.Close() except: pass self.graph = AlleyCatGraph(results, 'Path Graph') self.graph.Show()
def _build_function_xrefs(self): for function in idautils.Functions(): for xref in idautils.XrefsTo(function): func = idaapi.get_func(xref.frm) if func: start_ea = ida_shims.start_ea(func) if not self.functions.has_key(start_ea): self.functions[start_ea] = list() self.functions[start_ea].append(IDAProfilerXref( ea=function, string=ida_shims.get_name(function), xref=xref.frm, type=callable))
def rename(self, ea, name): # Don't rely on the name in curfunc, it could have already been renamed curname = ida_shims.get_name(ea) # Don't rename if the name is a special identifier, or if the ea has # already been named if curname.startswith('sub_') and \ name.split('_')[0] not in \ ['sub', 'loc', 'unk', 'dword', 'word', 'byte']: # Don't rename if the name already exists in the IDB if ida_shims.get_name_ea_simple(name) == idc.BADADDR: if ida_shims.set_name(ea, name): ida_shims.set_func_flags( ea, (ida_shims.get_func_flags(ea) | idc.FUNC_LIB)) return 1 return 0
def get_name_by_ea(self, ea): """ Get the name of the specified address. @ea - Address. Returns a name for the address, one of idc.Name, idc.GetFuncOffset or 0xXXXXXXXX. """ name = ida_shims.get_name(ea) if not name: name = ida_shims.get_func_off_str(ea) if not name: name = "0x%X" % ea return name
def pointify(self): counter = 0 print("Renaming pointers...", end=' ') for (name_ea, name) in idautils.Names(): for xref in idautils.XrefsTo(name_ea): xref_name = ida_shims.get_name(xref.frm) if xref_name and xref_name.startswith("off_"): i = 0 new_name = name + "_ptr" while ida_shims.get_name_ea_simple( new_name) != idc.BADADDR: new_name = name + "_ptr%d" % i i += 1 if ida_shims.set_name(xref.frm, new_name): counter += 1 #else: # print "Failed to create name '%s'!" % new_name print("renamed %d pointers" % counter)
def parse_function_tables(self): count = 0 for pattern in self.search(): name2func = {} ea = pattern.start while ea < pattern.stop: string_address = ida_shims.get_wide_dword( ea + (pattern.name_element * pattern.element_size)) function_address = ida_shims.get_wide_dword( ea + (pattern.function_element * pattern.element_size)) new_function_name = ida_shims.get_strlit_contents( string_address).decode("utf8") current_function_name = ida_shims.get_name(function_address) if not self.valid_function_name(new_function_name): print("ERROR: '%s' is not a valid function name. This is " \ "likely not a function table, or I have parsed it " \ "incorrectly!" % new_function_name) print(" Ignoring all entries in the structures " \ "between 0x%X and 0x%X.\n" % (pattern.start, pattern.stop)) name2func = {} break elif current_function_name.startswith("sub_"): name2func[new_function_name] = function_address ea += (pattern.num_elements * pattern.element_size) for (name, address) in name2func.items(): print("0x%.8X => %s" % (address, name)) ida_shims.set_name(address, name) count += 1 print("Renamed %d functions!" % count)
def generate(self): signatures = RizzoSignatures() # Generate unique string-based function signatures for (ea, string) in self.strings.iteritems(): # Only generate signatures on reasonably long strings with one xref if len(string.value) >= 8 and len(string.xrefs) == 1: func = idaapi.get_func(string.xrefs[0]) if func: str_hash = self.sighash(string.value) # Check for and remove string duplicate signatures (the same # string can appear more than once in an IDB). # If no duplicates, add this to the string signature dict. if str_hash in signatures.strings: del signatures.strings[str_hash] signatures.stringdups.add(str_hash) elif str_hash not in signatures.stringdups: signatures.strings[str_hash] = ida_shims.start_ea(func) # Generate formal, fuzzy, and immediate-based function signatures for ea in idautils.Functions(): func = idaapi.get_func(ea) if func: # Generate a signature for each block in this function blocks = self.function(func) # Build function-wide formal and fuzzy signatures by simply # concatenating the individual function block signatures. formal = self.sighash(''.join( [str(e) for (e, f, i, c) in blocks])) fuzzy = self.sighash(''.join( [str(f) for (e, f, i, c) in blocks])) # Add this signature to the function dictionary. start_ea = ida_shims.start_ea(func) signatures.functions[start_ea] = (ida_shims.get_name(start_ea), blocks) # Check for and remove formal duplicate signatures. # If no duplicates, add this to the formal signature dict. if signatures.formal.has_key(formal): del signatures.formal[formal] signatures.formaldups.add(formal) elif formal not in signatures.formaldups: signatures.formal[formal] = ida_shims.start_ea(func) # Check for and remove fuzzy duplicate signatures. # If no duplicates, add this to the fuzzy signature dict. if signatures.fuzzy.has_key(fuzzy): del signatures.fuzzy[fuzzy] signatures.fuzzydups.add(fuzzy) elif fuzzy not in signatures.fuzzydups: signatures.fuzzy[fuzzy] = ida_shims.start_ea(func) # Check for and remove immediate duplicate signatures. # If no duplicates, add this to the immediate signature dict. for (e, f, immediates, c) in blocks: for immediate in immediates: if signatures.immediates.has_key(immediate): del signatures.immediates[immediate] signatures.immediatedups.add(immediate) elif immediate not in signatures.immediatedups: signatures.immediates[immediate] = \ ida_shims.start_ea(func) # These need not be maintained across function calls, # and only add to the size of the saved signature file. signatures.fuzzydups = set() signatures.formaldups = set() signatures.stringdups = set() signatures.immediatedups = set() # DEBUG signatures.show() return signatures
def block(self, block): ''' Returns a tuple: ([formal, block, signatures], [fuzzy, block, signatures], set([unique, immediate, values]), [called, function, names]) ''' formal = [] fuzzy = [] functions = [] immediates = [] ea = ida_shims.start_ea(block) while ea < ida_shims.end_ea(block): insn = ida_shims.decode_insn(ea) # Get a list of all data/code refs from the current instruction drefs = [x for x in idautils.DataRefsFrom(ea)] crefs = [x for x in idautils.CodeRefsFrom(ea, False)] # Add all instruction mnemonics to the formal block hash formal.append(ida_shims.print_insn_mnem(ea)) # If this is a call instruction, be sure to note the name of the # function being called. This is used to apply call-based # signatures to functions. # # For fuzzy signatures, we can't use the actual name or EA of the # function, but rather just want to note that a function call was # made. # # Formal signatures already have the call instruction mnemonic, # which is more specific than just saying that a call was made. if idaapi.is_call_insn(ea): for cref in crefs: func_name = ida_shims.get_name(cref) if func_name: functions.append(func_name) fuzzy.append("funcref") # If there are data references from the instruction, check to see # if any of them are strings. These are looked up in the # pre-generated strings dictionary. # # String values are easily identifiable, and are used as part of # both the fuzzy and the formal signatures. # # It is more difficult to determine if non-string values are # constants or not; for both fuzzy and formal signatures, just use # "data" to indicate that some data was referenced. elif drefs: for dref in drefs: if self.strings.has_key(dref): formal.append(self.strings[dref].value) fuzzy.append(self.strings[dref].value) else: formal.append("dataref") fuzzy.append("dataref") # If there are no data or code references from the instruction, use # every operand as part of the formal signature. # # Fuzzy signatures are only concerned with interesting immediate # values, that is, values that are greater than 65,535, are not # memory addresses, and are not displayed as negative values. elif not drefs and not crefs: ops = ida_shims.get_operands(insn) for n in range(0, len(ops)): opnd_text = ida_shims.print_operand(ea, n) formal.append(opnd_text) if ops[n].type == idaapi.o_imm and \ not opnd_text.startswith('-'): if ops[n].value >= 0xFFFF: if ida_shims.get_full_flags(ops[n].value) == 0: fuzzy.append(str(ops[n].value)) immediates.append(ops[n].value) ea = ida_shims.next_head(ea) return (self.sighash(''.join(formal)), self.sighash(''.join(fuzzy)), immediates, functions)
def _profile_function(self): current_ea = ida_shims.get_screen_ea() current_function = ida_shims.get_func_name(current_ea) current_function_ea = ida_shims.get_name_ea_simple(current_function) if current_function: self.function = current_function ea = ida_shims.get_func_attr(current_function_ea, idc.FUNCATTR_START) end_ea = ida_shims.get_func_attr(current_function_ea, idc.FUNCATTR_END) self.highlighted = ida_shims.get_highlighted_identifier() while ea < end_ea and ea != idc.BADADDR and self.highlighted: i = 0 match = False optype = self.READ insn = ida_shims.decode_insn(ea) mnem = ida_shims.print_insn_mnem(ea) if self.highlighted in mnem: match = True elif idaapi.is_call_insn(ea): for xref in idautils.XrefsFrom(ea): if xref.type != 21: name = ida_shims.get_name(xref.to) if name and self.highlighted in name: match = True break else: while True: opnd = ida_shims.print_operand(ea, i) if opnd: if self.highlighted in opnd: canon_feature = ida_shims.get_canon_feature(insn) match = True if canon_feature & self.OPND_WRITE_FLAGS[i]: optype = self.WRITE i += 1 else: break if not match: comment = idc.GetCommentEx(ea, 0) if comment and self.highlighted in comment: match = True else: comment = idc.GetCommentEx(ea, 1) if comment and self.highlighted in comment: match = True else: comment = None if match: if ea > current_ea: direction = self.DOWN elif ea < current_ea: direction = self.UP else: direction = self.THIS self.xrefs[ea] = { 'offset': ida_shims.get_func_off_str(ea), 'mnem': mnem, 'type': optype, 'direction': direction, 'text': idc.GetDisasm(ea), } ea = ida_shims.next_head(ea)