Last active
December 17, 2024 03:26
-
-
Save junron/37a582a692d6cac3a6e55af42f55ee53 to your computer and use it in GitHub Desktop.
Recursive xref plugin for IDA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sark | |
| import idaapi | |
| import ida_kernwin | |
| from PyQt5.QtCore import * | |
| from PyQt5.QtGui import * | |
| from PyQt5.QtWidgets import * | |
| class Hooks(idaapi.UI_Hooks): | |
| def finish_populating_widget_popup(self, form, popup): | |
| type = idaapi.get_widget_type(form) | |
| if type == idaapi.BWN_DISASM or type == idaapi.BWN_PSEUDOCODE: | |
| idaapi.attach_action_to_popup(form, popup, RecursiveXref.get_name(), '') | |
| class RecursiveXref(sark.ui.ActionHandler): | |
| TEXT = "Show recursive xrefs" | |
| def _activate(self, ctx): | |
| func = sark.Function(ctx.cur_ea) | |
| form = RecursiveXrefDisplay(func) | |
| form.Show(f"Recursive xrefs for {nice_name(func)}", idaapi.WOPN_DP_INSIDE | idaapi.WOPN_DP_RIGHT) | |
| class RecursiveXrefDisplay(idaapi.PluginForm): | |
| def __init__(self, target: sark.Function): | |
| super().__init__() | |
| self.target = target | |
| def OnCreate(self, form): | |
| self.parent = self.FormToPyQtWidget(form) | |
| layout = QVBoxLayout() | |
| treeview = QTreeWidget() | |
| treeview.setHeaderHidden(True) | |
| top = QTreeWidgetItem(treeview) | |
| label = make_label(nice_name(self.target)) | |
| treeview.setItemWidget(top, 0, label) | |
| recursive_xref(self.target, top, treeview, []) | |
| layout.addWidget(treeview) | |
| self.parent.setLayout(layout) | |
| def make_label(s): | |
| label = QLabel(s) | |
| font = label.font() | |
| font.setPointSizeF(9) | |
| label.setFont(font) | |
| return label | |
| def nice_name(func: sark.Function): | |
| ea = func.ea | |
| return idaapi.get_ea_name(ea, idaapi.GN_SHORT|idaapi.GN_DEMANGLED) | |
| def func_or_none(ea): | |
| try: | |
| return sark.Function(ea) | |
| except sark.exceptions.SarkNoFunction: | |
| return None | |
| def goto_address(ea): | |
| ida_kernwin.jumpto(int(ea)) | |
| def make_link(ea): | |
| return f"<a href='{ea}'>{hex(ea)}</a>" | |
| def recursive_xref(func: sark.Function, widget_item: QTreeWidgetItem, treeview: QTreeWidget, all_callsites): | |
| if func.name == "main": | |
| return | |
| callers = {} | |
| for xref in func.xrefs_to: | |
| caller = func_or_none(xref.frm) | |
| if caller is None or xref.frm in all_callsites: | |
| continue | |
| all_callsites.append(xref.frm) | |
| if caller in callers: | |
| callers[caller].append(xref.frm) | |
| else: | |
| callers[caller] = [xref.frm] | |
| for caller, callsites in callers.items(): | |
| child_label = None | |
| func_name = nice_name(caller) | |
| if len(callsites) == 1: | |
| child_label = make_label(f"{func_name} @ {make_link(callsites[0])}") | |
| else: | |
| child_label = make_label(f"{func_name} ({', '.join(make_link(ea) for ea in callsites)})") | |
| child_label.linkActivated.connect(goto_address) | |
| child_item = QTreeWidgetItem(widget_item) | |
| treeview.setItemWidget(child_item, 0, child_label) | |
| recursive_xref(caller, child_item, treeview, all_callsites) | |
| class RecursiveXrefPlugins(idaapi.plugin_t): | |
| flags = 0 | |
| comment = 'Show recursive xrefs' | |
| help = 'Show recursive xrefs.' | |
| wanted_name = 'Recursive Xrefs' | |
| wanted_hotkey = "" | |
| def init(self): | |
| print("[REXREF] Plugin loaded!") | |
| RecursiveXref.register() | |
| self.hooks = Hooks() | |
| self.hooks.hook() | |
| return idaapi.PLUGIN_KEEP | |
| def term(self): | |
| self.hooks.unhook() | |
| RecursiveXref.unregister() | |
| def run(self, arg): | |
| pass | |
| def PLUGIN_ENTRY(): | |
| return RecursiveXrefPlugins() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment