-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathshowcomments.py
More file actions
196 lines (167 loc) · 6.84 KB
/
showcomments.py
File metadata and controls
196 lines (167 loc) · 6.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
from PyQt5 import QtCore, QtWidgets
import ida_kernwin
import idaapi
import idautils
import idc
from idaapi import PluginForm
try:
from QtCore import QVariant
except ImportError:
QVariant = lambda *args: None if len(args) == 0 else args[0]
class Comment:
def __init__(self, address, comment_type, comment, function_name):
self.address = address
self.comment_type = comment_type
self.comment = comment
self.function_name = function_name
class CommentTableModel(QtCore.QAbstractTableModel):
columns = ["Address", "Type", "Comment", "Function Name"]
def __init__(self, parent=None, *args):
super(CommentTableModel, self).__init__()
self.table_data = []
def append(self, comment):
self.beginInsertRows(QtCore.QModelIndex(), len(self.table_data), len(self.table_data))
self.table_data.append(comment)
self.endInsertRows()
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.columns)
def data(self, index, role=QtCore.Qt.DisplayRole):
if (not index.isValid()) or (role != QtCore.Qt.DisplayRole):
return QVariant()
comment = self.table_data[index.row()]
match index.column():
case 0:
return comment.address
case 1:
return comment.comment_type
case 2:
return comment.comment
case 3:
return comment.function_name
case _:
return QVariant()
def dataAt(self, index):
return self.table_data[index.row()]
def headerData(self, column, orientation, role):
if (orientation != QtCore.Qt.Horizontal) or (role != QtCore.Qt.DisplayRole):
return QVariant()
if 0 <= column <= len(self.columns):
return self.columns[column]
return QVariant()
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.table_data)
class ShowComments(PluginForm):
def OnCreate(self, form):
# Get parent widget
self.parent = self.FormToPyQtWidget(form)
self.PopulateForm()
def PopulateForm(self):
# Create layout
layout = QtWidgets.QVBoxLayout()
# Create input for keyword
self.filter_input = QtWidgets.QLineEdit(self.parent)
self.filter_input.setPlaceholderText("Regular Expression Filter...")
self.filter_input.returnPressed.connect(self.filter_comments)
layout.addWidget(self.filter_input)
# table
self.table = QtWidgets.QTableView()
self.table.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
self.table.setSortingEnabled(True)
# populate table
self.reset_and_populate()
self.table.doubleClicked.connect(self.fn_get_cell_Value)
layout.addWidget(self.table)
# make our created layout the dialogs layout
self.parent.setLayout(layout)
def add_row(self, item_index, ea, cmt, cmt_type, function_name):
self.table_model.append(Comment(hex(ea), cmt_type, cmt, function_name))
item_index += 1
return item_index
def filter_comments(self):
if keyword := self.filter_input.text():
self.proxy_model.setFilterRegularExpression(keyword)
else:
self.proxy_model.setFilterRegularExpression(".*")
def populate_with_comments(self, item_index):
current_function_name = None
for ea in idautils.Heads():
# Check if the first address of a function contains a function (repeatable) comment
# IDAPython cheatsheet (https://gist.github.com/icecr4ck/7a7af3277787c794c66965517199fc9c)
function_name = idaapi.get_func_name(ea)
if function_name != current_function_name:
if function_cmt := idc.get_func_cmt(ea, True):
item_index = self.add_row(item_index, ea, function_cmt, "Function", function_name)
current_function_name = function_name
# Check if the address contains a regular (non-repeatable) comment
if cmt := idaapi.get_cmt(ea, False):
item_index = self.add_row(item_index, ea, cmt, "Regular", function_name)
# Now check if it contains a repeatable comment
if cmt := idaapi.get_cmt(ea, True):
item_index = self.add_row(item_index, ea, cmt, "Repeatable", function_name)
# Now check if it contains an anterior comment
cmt_data = []
cmt_idx = 0
while cmt := idaapi.get_extra_cmt(ea, idaapi.E_PREV + cmt_idx):
cmt_data.append(cmt)
cmt_idx += 1
if cmt_data:
item_index = self.add_row(item_index, ea, " ".join(cmt_data), "Anterior", function_name)
# Finally, check if it contains a posterior comment
cmt_data = []
cmt_idx = 0
while cmt := idaapi.get_extra_cmt(ea, idaapi.E_NEXT + cmt_idx):
cmt_data.append(cmt)
cmt_idx += 1
if cmt_data:
item_index = self.add_row(item_index, ea, " ".join(cmt_data), "Posterior", function_name)
def reset_and_populate(self):
# reset
try:
self.proxy_model.clear()
self.table.setModel(None)
except AttributeError:
pass
item_index = 0
# populate
self.table_model = CommentTableModel(self.table)
self.populate_with_comments(item_index)
# make proxy model
self.proxy_model = QtCore.QSortFilterProxyModel(self.table)
self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.proxy_model.setFilterKeyColumn(2)
self.proxy_model.setSourceModel(self.table_model)
# use proxy model and resize
self.table.setModel(self.proxy_model)
self.table.resizeColumnToContents(0)
self.table.resizeColumnToContents(1)
self.table.setColumnWidth(2, 300)
self.table.horizontalHeader().setStretchLastSection(True)
def fn_get_cell_Value(self, index):
col = index.column()
if col == 0 or col == 2:
row = index.row()
addr_idx = index.model().index(row, 0)
try:
ida_kernwin.jumpto(int(addr_idx.data(), 16))
except Exception:
pass
def OnClose(self, form):
pass
class showcomments_plugin_t(idaapi.plugin_t):
comment = "ShowComments"
version = "0.6.0"
website = "https://github.com/merces/showcomments"
help = ""
wanted_name = "ShowComments"
wanted_hotkey = "Ctrl-Alt-C"
flags = idaapi.PLUGIN_OK
def init(self):
return idaapi.PLUGIN_OK
def run(self, arg):
plg = ShowComments()
plg.Show("Comments")
pass
def term(self):
return
def PLUGIN_ENTRY():
return showcomments_plugin_t()