Skip to content

Commit 6745cca

Browse files
committed
Add xref
1 parent 1bae63b commit 6745cca

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

xref/Screenshot_20230701_213942.png

74.8 KB
Loading

xref/xref.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""
2+
Show xref tree like dnSpy
3+
Refs:
4+
https://leanpub.com/IDAPython-Book
5+
https://doc.qt.io/qt-6/qtreeview.html
6+
https://blog.xorhex.com/blog/ida-plugin-contextmenu/
7+
"""
8+
from collections import defaultdict
9+
10+
from idc import get_func_name
11+
from ida_funcs import get_func
12+
from idautils import CodeRefsTo
13+
from idaapi import (
14+
PluginForm,
15+
action_handler_t,
16+
UI_Hooks,
17+
get_widget_type,
18+
BWN_DISASM,
19+
BWN_PSEUDOCODE,
20+
action_desc_t,
21+
attach_dynamic_action_to_popup,
22+
SETMENU_INS,
23+
get_screen_ea,
24+
)
25+
from ida_kernwin import jumpto
26+
from PyQt5.QtWidgets import QVBoxLayout, QTreeWidget, QTreeWidgetItem
27+
28+
29+
def generate_func_info(addr: int):
30+
name = get_func_name(addr)
31+
addr = get_func(addr).start_ea
32+
return name, addr
33+
34+
35+
class XrefForm(PluginForm):
36+
def __init__(self, root_addr: int):
37+
super().__init__()
38+
self.root_addr = root_addr
39+
self.xrefed = defaultdict(lambda: False)
40+
41+
def OnCreate(self, form):
42+
# Get parent widget
43+
self.parent = self.FormToPyQtWidget(form) # IDAPython
44+
self.populate_form()
45+
46+
def populate_form(self):
47+
# Use TreeWidget to show references
48+
self.tree = QTreeWidget()
49+
self.tree.setColumnCount(3)
50+
self.tree.setHeaderLabels(["Function name", "Function address", "Xref address"])
51+
self.tree.setExpandsOnDoubleClick(False)
52+
self.tree.clicked.connect(self.click_tree_item)
53+
self.tree.doubleClicked.connect(self.doubleclk_tree_item)
54+
55+
# Generate root_addr's info as root item
56+
name, addr = generate_func_info(self.root_addr)
57+
item = QTreeWidgetItem(self.tree)
58+
item.setText(0, name)
59+
item.setText(1, hex(addr))
60+
item.setText(2, hex(addr))
61+
62+
layout = QVBoxLayout()
63+
layout.addWidget(self.tree)
64+
self.parent.setLayout(layout)
65+
66+
def click_tree_item(self, idx):
67+
item = self.tree.currentItem()
68+
addr = item.text(1)
69+
if self.xrefed[addr]:
70+
# Skip already generated xref address
71+
return True
72+
for xref in CodeRefsTo(int(addr, 16), 0):
73+
# Add xref to item's child
74+
xname, xaddr = generate_func_info(xref)
75+
child = QTreeWidgetItem(item)
76+
child.setText(0, xname)
77+
child.setText(1, hex(xaddr))
78+
child.setText(2, hex(xref))
79+
self.tree.expandItem(item)
80+
self.xrefed[addr] = True
81+
82+
def doubleclk_tree_item(self, idx):
83+
item = self.tree.currentItem()
84+
addr = item.text(1)
85+
jumpto(int(addr, 16))
86+
87+
88+
class handler_class(action_handler_t):
89+
def activate(self, ctx):
90+
ea = get_screen_ea()
91+
plg = XrefForm(ea)
92+
plg.Show(f"Xref to {ea:x}")
93+
94+
def update(self, ctx):
95+
pass
96+
97+
98+
class ContextHooks(UI_Hooks):
99+
def finish_populating_widget_popup(self, form, popup):
100+
tft = get_widget_type(form)
101+
if tft == BWN_DISASM:
102+
action_name = action_desc_t(None, "Xref tree", handler_class())
103+
attach_dynamic_action_to_popup(
104+
form,
105+
popup,
106+
action_name,
107+
"Xref tree",
108+
SETMENU_INS,
109+
)
110+
elif tft == BWN_PSEUDOCODE:
111+
pass
112+
113+
114+
hooks = ContextHooks()
115+
hooks.hook()

0 commit comments

Comments
 (0)