def argc(self, function): ''' Counts the number of arguments used by the specified function. ''' argv = set() notargv = set() ea = ida_shims.start_ea(function) end_ea = ida_shims.end_ea(function) if self.arch.unknown: return 0 while ea < end_ea: insn = ida_shims.decode_insn(ea) features = ida_shims.get_canon_feature(insn) for n in range(0, len(self.USE_OPND)): ops = ida_shims.get_operands(insn) if ops[n].type in [ idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase ]: try: regname = self.arch.registers[ops[n].reg] index = self.arch.argv.index(regname) except ValueError: continue if features & self.USE_OPND[n] and regname not in notargv: argv.update(self.arch.argv[:index + 1]) for n in range(0, len(self.CHANGE_OPND)): ops = ida_shims.get_operands(insn) if ops[n].type in [ idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase ]: try: regname = self.arch.registers[ops[n].reg] index = self.arch.argv.index(regname) except ValueError: continue if regname not in argv: notargv.update(self.arch.argv[index:]) if argv.union(notargv) == set(self.arch.argv): break # TODO: Use idc.NextHead(ea) instead... ea += self.arch.insn_size return len(argv)
def trace(self, ea): ''' Given an EA where an argument register is set, attempt to trace what function call that argument is passed to. @ea - The address of an instruction that modifies a function argument register. Returns a tuple of (function EA, argv index, argument register name) on success. Returns None on failure. ''' insn = ida_shims.decode_insn(ea) features = ida_shims.get_canon_feature(insn) if self.arch.unknown: return (None, None, None) for n in range(0, len(self.CHANGE_OPND)): ops = ida_shims.get_operands(insn) if ops[n].type in [idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase]: try: regname = self.arch.registers[ops[n].reg] index = self.arch.argv.index(regname) except ValueError: continue if features & self.CHANGE_OPND[n]: ea = ea - (self.arch.delay_slot * self.arch.insn_size) while True: insn = ida_shims.decode_insn(ea) if idaapi.is_call_insn(ea): for xref in idautils.XrefsFrom(ea): if xref.type in [idaapi.fl_CF, idaapi.fl_CN]: return (xref.to, index, regname) # If we couldn't figure out where the function call # was going to, just quit break try: is_block_end = idaapi.is_basic_block_end(ea) except TypeError: is_block_end = idaapi.is_basic_block_end(ea, True) if is_block_end: break # TODO: Use idc.NextHead(ea) instead... ea += self.arch.insn_size return (None, None, None)
def argv(self, func): ''' Attempts to identify what types of arguments are passed to a given function. Currently unused. ''' args = [None for x in self.arch.argv] if not self.arch.unknown: start_ea = ida_shims.start_ea(func) for xref in idautils.XrefsTo(start_ea): if idaapi.is_call_insn(xref.frm): insn = ida_shims.decode_insn(xref.frm) ea = xref.frm + (self.arch.delay_slot * self.arch.insn_size) end_ea = (xref.frm - (self.arch.insn_size * 10)) while ea >= end_ea: if idaapi.is_basic_block_end(ea) or \ (ea != xref.frm and idaapi.is_call_insn(ea)): break insn = ida_shims.decode_insn(ea) features = ida_shims.get_canon_feature(insn) for n in range(0, len(self.CHANGE_OPND)): ops = ida_shims.get_operands(insn) if ops[n].type in [ idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase ]: try: regname = self.arch.registers[ops[n].reg] index = self.arch.argv.index(regname) except ValueError: continue if features & self.CHANGE_OPND[n]: for xref in idautils.XrefsFrom(ea): # TODO: Where is this xref type defined? if xref.type == 1: string = \ ida_shims.get_strlit_contents( xref.to) if string and len(string) > 4: args[index] = str break ea -= self.arch.insn_size yield args
def _fix_data_offsets(self): ea = 0 count = 0 print("Fixing unresolved offset xrefs...", end=' ') while ea != idaapi.BADADDR: (ea, n) = idaapi.find_notype(ea, idaapi.SEARCH_DOWN) if ida_shims.can_decode(ea): insn = ida_shims.decode_insn(ea) ops = ida_shims.get_operands(insn) for i in range(0, len(ops)): op = ops[i] if op.type == idaapi.o_imm and idaapi.getseg(op.value): idaapi.add_dref(ea, op.value, (idaapi.dr_O | idaapi.XREF_USER)) count += 1 print("created %d new data xrefs" % count)
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)