Skip to content

Instantly share code, notes, and snippets.

@junron
Last active December 17, 2024 03:26
Show Gist options
  • Select an option

  • Save junron/37a582a692d6cac3a6e55af42f55ee53 to your computer and use it in GitHub Desktop.

Select an option

Save junron/37a582a692d6cac3a6e55af42f55ee53 to your computer and use it in GitHub Desktop.
Recursive xref plugin for IDA
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