""" summary: show decompiler cross-references description: Show decompiler-style Xref when the `Ctrl+X` key is pressed in the Decompiler window. * supports any global name: functions, strings, integers, ... * supports structure member. author: EiNSTeiN_ (einstein@g3nius.org) level: advanced """ import ida_kernwin import ida_hexrays import ida_typeinf import ida_idaapi import ida_funcs import idautils import traceback from PyQt5 import QtCore, QtWidgets XREF_EA = 0 XREF_STRUC_MEMBER = 1 class XrefsForm(ida_kernwin.PluginForm): def __init__(self, target): ida_kernwin.PluginForm.__init__(self) self.target = target if type(self.target) == ida_hexrays.cfunc_t: self.__type = XREF_EA self.__ea = self.target.entry_ea self.__name = 'Xrefs of %x' % (self.__ea, ) elif type(self.target) == ida_hexrays.cexpr_t and self.target.opname == 'obj': self.__type = XREF_EA self.__ea = self.target.obj_ea self.__name = 'Xrefs of %x' % (self.__ea, ) elif type(self.target) == ida_hexrays.cexpr_t and self.target.opname in ('memptr', 'memref'): self.__type = XREF_STRUC_MEMBER name = self.get_struc_name() self.__name = 'Xrefs of %s' % (name, ) else: raise ValueError('cannot show xrefs for this kind of target') return def get_struc_name(self): x = self.target.operands['x'] m = self.target.operands['m'] xtype = x.type xtype.remove_ptr_or_array() typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, xtype, '', '') tif = ida_typeinf.tinfo_t() if not tif.get_named_type(None, typename): print("Error while retrieving %s type.", typename) return typename udm = ida_typeinf.udm_t() udm.offset = m if tif.find_udm(udm, ida_typeinf.STRMEM_OFFSET) == -1: print("Error while retrieving %s member.", typename) return typename return '%s::%s' % (typename, udm.name) def OnCreate(self, widget): # Get parent widget self.parent = self.FormToPyQtWidget(widget) self.populate_form() return def Show(self): ida_kernwin.PluginForm.Show(self, self.__name) return def populate_form(self): # Create layout layout = QtWidgets.QVBoxLayout() layout.addWidget(QtWidgets.QLabel(self.__name)) self.table = QtWidgets.QTableWidget() layout.addWidget(self.table) self.table.setColumnCount(3) self.table.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem("Address")) self.table.setHorizontalHeaderItem(1, QtWidgets.QTableWidgetItem("Function")) self.table.setHorizontalHeaderItem(2, QtWidgets.QTableWidgetItem("Line")) self.table.setColumnWidth(0, 80) self.table.setColumnWidth(1, 150) self.table.setColumnWidth(2, 450) self.table.cellDoubleClicked.connect(self.double_clicked) #~ self.table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows ) self.parent.setLayout(layout) self.populate_table() return def double_clicked(self, row, column): ea = self.functions[row] ida_hexrays.open_pseudocode(ea, True) return def get_decompiled_line(self, cfunc, ea): print(repr(ea)) if ea not in cfunc.eamap: print('strange, %x is not in %x eamap' % (ea, cfunc.entry_ea)) return insnvec = cfunc.eamap[ea] lines = [] for stmt in insnvec: qp = ida_hexrays.qstring_printer_t(cfunc, False) stmt._print(0, qp) s = qp.s.split('\n')[0] #~ s = ida_lines.tag_remove(s) lines.append(s) return '\n'.join(lines) def get_items_for_ea(self, ea): frm = [x.frm for x in idautils.XrefsTo(self.__ea)] items = [] for ea in frm: cfunc = ida_hexrays.decompile(ea) if not cfunc: print('Decompilation of %x failed' % (ea, )) continue self.functions.append(cfunc.entry_ea) self.items.append((ea, ida_funcs.get_func_name(cfunc.entry_ea) or "", self.get_decompiled_line(cfunc, ea))) def get_items_for_type(self): x = self.target.operands['x'] m = self.target.operands['m'] xtype = x.type xtype.remove_ptr_or_array() typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, xtype, '', '') addresses = [] for ea in idautils.Functions(): cfunc = ida_hexrays.decompile(ea, flags=ida_hexrays.DECOMP_GXREFS_FORCE) if not cfunc: print('Decompilation of %x failed' % (ea, )) continue for citem in cfunc.treeitems: citem = citem.to_specific_type if not (type(citem) == ida_hexrays.cexpr_t and citem.opname in ('memptr', 'memref')): continue _x = citem.operands['x'] _m = citem.operands['m'] _xtype = _x.type _xtype.remove_ptr_or_array() _typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, _xtype, '', '') if not (_typename == typename and _m == m): continue parent = citem while parent: if type(parent.to_specific_type) == ida_hexrays.cinsn_t: break parent = cfunc.body.find_parent_of(parent) if not parent: print('cannot find parent statement (?!)') continue if parent.ea in addresses: continue if parent.ea == ida_idaapi.BADADDR: print('parent.ea is BADADDR') continue addresses.append(parent.ea) self.functions.append(cfunc.entry_ea) self.items.append(( parent.ea, ida_funcs.get_func_name(cfunc.entry_ea) or "", self.get_decompiled_line(cfunc, parent.ea))) return [] def populate_table(self): self.functions = [] self.items = [] if self.__type == XREF_EA: self.get_items_for_ea(self.__ea) else: self.get_items_for_type() self.table.setRowCount(len(self.items)) i = 0 for item in self.items: address, func, line = item item = QtWidgets.QTableWidgetItem('0x%x' % (address, )) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) self.table.setItem(i, 0, item) item = QtWidgets.QTableWidgetItem(func) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) self.table.setItem(i, 1, item) item = QtWidgets.QTableWidgetItem(line) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) self.table.setItem(i, 2, item) i += 1 self.table.resizeRowsToContents() return def OnClose(self, widget): pass class show_xrefs_ah_t(ida_kernwin.action_handler_t): def __init__(self): ida_kernwin.action_handler_t.__init__(self) self.sel = None def activate(self, ctx): vu = ida_hexrays.get_widget_vdui(ctx.widget) if not vu or not self.sel: print("No vdui? Strange, since this action should be enabled only for pseudocode views.") return 0 form = XrefsForm(self.sel) form.Show() return 1 def update(self, ctx): if ctx.widget_type != ida_kernwin.BWN_PSEUDOCODE: return ida_kernwin.AST_DISABLE_FOR_WIDGET vu = ida_hexrays.get_widget_vdui(ctx.widget) vu.get_current_item(ida_hexrays.USE_KEYBOARD) item = vu.item self.sel = None if item.citype == ida_hexrays.VDI_EXPR and item.it.to_specific_type.opname in ('obj', 'memref', 'memptr'): # if an expression is selected. verify that it's either a cot_obj, cot_memref or cot_memptr self.sel = item.it.to_specific_type elif item.citype == ida_hexrays.VDI_FUNC: # if the function itself is selected, show xrefs to it. self.sel = item.f return ida_kernwin.AST_ENABLE if self.sel else ida_kernwin.AST_DISABLE class vds_xrefs_hooks_t(ida_hexrays.Hexrays_Hooks): def populating_popup(self, widget, phandle, vu): ida_kernwin.attach_action_to_popup(widget, phandle, "vdsxrefs:show", None) return 0 if ida_hexrays.init_hexrays_plugin(): adesc = ida_kernwin.action_desc_t('vdsxrefs:show', 'Show xrefs', show_xrefs_ah_t(), "Ctrl+X") if ida_kernwin.register_action(adesc): vds_xrefs_hooks = vds_xrefs_hooks_t() vds_xrefs_hooks.hook() else: print("Couldn't register action.") else: print('hexrays is not available.')