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 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. ''' idaapi.decode_insn(ea) features = idaapi.cmd.get_canon_feature() if self.arch.unknown: return (None, None, None) for n in range(0, len(self.CHANGE_OPND)): if idaapi.cmd.Operands[n].type in [idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase]: try: regname = self.arch.registers[idaapi.cmd.Operands[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: idaapi.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 blocks(start, end): '''Returns each block between the specified range of instructions''' block = start for ea in iterate(start, end): nextea = next(ea) if idaapi.is_basic_block_end(ea): yield block, nextea block = nextea continue return
def is_end_block_call(self): """ Property indicating if this instruction is the end of a basic block. This property will return True if the instruction is a call, see :meth:`is_end_block` for returning False on call which are not at the end of a block. :return bool: True if this instruction is the last one for a block, including the case when it is a call. """ return idaapi.is_basic_block_end(self.ea, True)
def is_end_block(self): """ Property indicating if this instruction is the end of a basic block. This property will return False if the instruction is a call which does not end a block such as display by IDA, see :meth:`is_end_block_call` for returning True on call. :return bool: True if this instruction is the last one for a block. """ return idaapi.is_basic_block_end(self.ea, False)
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 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] for xref in idautils.XrefsTo(func.startEA): if idaapi.is_call_insn(xref.frm): idaapi.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: # Stop searching if we've reached a conditional block or another call if idaapi.is_basic_block_end(ea) or ( ea != xref.frm and idaapi.is_call_insn(ea)): break idaapi.decode_insn(ea) features = idaapi.cmd.get_canon_feature() for n in range(0, len(self.CHANGE_OPND)): if idaapi.cmd.Operands[n].type in [ idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase ]: try: regname = self.arch.registers[ idaapi.cmd.Operands[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 = idc.GetString(xref.to) if string and len(string) > 4: args[index] = str break ea -= self.arch.insn_size yield args
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: for xref in idautils.XrefsTo(func.startEA): if idaapi.is_call_insn(xref.frm): idaapi.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: # Stop searching if we've reached a conditional block or another call if idaapi.is_basic_block_end(ea) or (ea != xref.frm and idaapi.is_call_insn(ea)): break idaapi.decode_insn(ea) features = idaapi.cmd.get_canon_feature() for n in range(0, len(self.CHANGE_OPND)): if idaapi.cmd.Operands[n].type in [idaapi.o_reg, idaapi.o_displ, idaapi.o_phrase]: try: regname = self.arch.registers[idaapi.cmd.Operands[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 = idc.GetString(xref.to) if string and len(string) > 4: args[index] = str break ea -= self.arch.insn_size yield args
def PopulateModel(self): self.Clean() vmr = get_vmr() w = NotifyProgress() w.show() ctr = 0 max = len(self.trace) # present clustering analysis in viewer prev_ctx = defaultdict(lambda: 0) for line in self.trace: ctr += 1 w.pbar_set(int(float(ctr) / float(max) * 100)) if isinstance(line, Traceline): tid = QtGui.QStandardItem('%s' % line.thread_id) addr = QtGui.QStandardItem('%x' % line.addr) disasm = QtGui.QStandardItem(line.disasm_str()) comment = QtGui.QStandardItem(''.join( c for c in line.comment if line.comment is not None)) context = QtGui.QStandardItem(''.join( '%s:%s ' % (c, line.ctx[c]) for c in line.ctx if line.ctx is not None)) prev_ctx = line.ctx self.sim.appendRow([tid, addr, disasm, comment, context]) else: cluster_node = QtGui.QStandardItem( 'Cluster %x-%x' % (line[0].addr, line[-1].addr)) self.sim.appendRow(cluster_node) if vmr.bb: cluster = line bbs = [] bb = [] # subdivide the clusters by basic blocks for line in cluster: assert isinstance(line, Traceline) if is_basic_block_end(line.addr): bb.append(line) bbs.append(bb) bb = [] else: bb.append(line) for bb in bbs: bb_sum = self.bb_func(bb, self.ctx_reg_size, prev_ctx) bb_node = QtGui.QStandardItem( 'BB%s Summary %x-%x: %s\t%s\t%s' % (bbs.index(bb), bb[0].addr, bb[-1].addr, ''.join( '%s ; ' % (''.join('%s, ' % c for c in line)) for line in bb_sum.disasm), ''.join( '%s, ' % c for c in filter(None, bb_sum.comment) if bb_sum.comment is not None), ''.join( '%s:%s ' % (c, bb_sum.ctx[c]) for c in bb_sum.ctx if bb_sum.ctx is not None))) for line in bb: tid = QtGui.QStandardItem('%s' % line.thread_id) addr = QtGui.QStandardItem('%x' % line.addr) disasm = QtGui.QStandardItem(line.disasm_str()) comment = QtGui.QStandardItem(''.join( c for c in line.comment if line.comment is not None)) context = QtGui.QStandardItem(''.join( '%s:%s ' % (c, line.ctx[c]) for c in line.ctx if line.ctx is not None)) bb_node.appendRow( [tid, addr, disasm, comment, context]) cluster_node.appendRow(bb_node) self.treeView.setFirstColumnSpanned( bbs.index(bb), cluster_node.index(), True) prev_ctx = bb[-1].ctx else: for l in line: tid = QtGui.QStandardItem('%s' % l.thread_id) addr = QtGui.QStandardItem('%x' % l.addr) disasm = QtGui.QStandardItem(l.disasm_str()) comment = QtGui.QStandardItem(''.join( c for c in l.comment if l.comment is not None)) context = QtGui.QStandardItem(''.join( '%s:%s ' % (c, l.ctx[c]) for c in l.ctx if l.ctx is not None)) cluster_node.appendRow( [tid, addr, disasm, comment, context]) w.close() self.treeView.resizeColumnToContents(0) self.treeView.resizeColumnToContents(1) self.treeView.resizeColumnToContents(2) self.treeView.resizeColumnToContents(3) self.treeView.resizeColumnToContents(4)