-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathfindfuncmain.py
267 lines (231 loc) · 8.1 KB
/
findfuncmain.py
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
import sys
from PyQt5.QtWidgets import QApplication, QMessageBox, QVBoxLayout
from findfunc import findfunc_gui
inida = True
try:
# from idaapi import PluginForm
import idc
import idaapi
import idautils
import ida_bytes
import ida_ua
import ida_pro
from idaapi import PluginForm
except:
inida = False
print("not in ida")
if inida:
from findfunc.advanced_copy import copy_all_bytes, copy_bytes_no_imm, copy_only_opcodes, copy_only_disasm, copy_opcodes_and_imm
__AUTHOR__ = 'feber'
PLUGIN_NAME = "FindFunc (x86/x64)"
PLUGIN_HOTKEY = 'ctrl+alt+f'
VERSION = '1.4'
WINDOWTITLE = f'{PLUGIN_NAME} {VERSION}'
INFOSTR = f'For usage see: ' '<a href="https://github.com/FelixBer/FindFunc">https://github.com/FelixBer/FindFunc</a>'
# this is executed when running a script rather than plugin
if __name__ == "__main__":
if inida and not idaapi.get_input_file_path():
QMessageBox.information(None, "No File", "Please load a file in IDA first, then run script again.")
else:
app = QApplication(sys.argv) if not inida else None # if we run outside IDA for testing
tabwid = findfunc_gui.TabWid(False)
tabwid.setInfoString(INFOSTR)
tabwid.setWindowTitle(WINDOWTITLE)
tabwid.show()
if app:
sys.exit(app.exec_())
# plugin stuff
def PLUGIN_ENTRY():
return FindFunc()
class FindFunc(idaapi.plugin_t):
"""
Main Plugin Class
"""
flags = 0 # idaapi.PLUGIN_PROC # | idaapi.PLUGIN_FIX #| idaapi.PLUGIN_HIDE
comment = "Function Finder and Advanced copying of instruction bytes"
help = f"Edit->Plugin->FindFunc or {PLUGIN_HOTKEY}. Also: disasm->rightclick->copy all|opcode|noimm"
wanted_name = PLUGIN_NAME
wanted_hotkey = PLUGIN_HOTKEY
ACTION_COPY_BYTES = "feber:copy_bytes"
ACTION_COPY_OPC = "feber:copy_opc"
ACTION_COPY_OPC_IMM = "feber:copy_opc_imm"
ACTION_COPY_NO_IMM = "feber:copy_no_imm"
ACTION_COPY_DISASM = "feber:copy_disasm"
ACTION_FFOPEN = "feber:ffopen"
def __init__(self):
super().__init__()
self.hooks = ACUiHook()
self.maintabwidgtet = None
def init(self):
# see advanced_copy for details
action_desc = idaapi.action_desc_t(
self.ACTION_COPY_BYTES,
"copy bytes",
ACActionHandler(copy_all_bytes),
"ctrl+alt+b", # hotkey
"copy all selected bytes as hex",
31
)
assert idaapi.register_action(action_desc), "Action registration failed"
action_desc = idaapi.action_desc_t(
self.ACTION_COPY_OPC,
"copy opcodes",
ACActionHandler(copy_only_opcodes),
"ctrl+alt+o", # hotkey
"copy selected opcodes as hex, mask out non-opcode bytes",
31
)
assert idaapi.register_action(action_desc), "Action registration failed"
action_desc = idaapi.action_desc_t(
self.ACTION_COPY_OPC_IMM,
"copy opcodes and immediates",
ACActionHandler(copy_opcodes_and_imm),
"ctrl+alt+o+i", # hotkey
"copy instruction bytes, mask everything but opcodes + immediates",
31
)
assert idaapi.register_action(action_desc), "Action registration failed"
action_desc = idaapi.action_desc_t(
self.ACTION_COPY_NO_IMM,
"copy without immediates",
ACActionHandler(copy_bytes_no_imm),
"ctrl+alt+i", # hotkey
"copy instruction bytes, mask out all immediates",
31
)
assert idaapi.register_action(action_desc), "Action registration failed"
action_desc = idaapi.action_desc_t(
self.ACTION_COPY_DISASM,
"copy disasm",
ACActionHandler(copy_only_disasm),
"ctrl+alt+d", # hotkey
"copy disasm lines only",
31
)
assert idaapi.register_action(action_desc), "Action registration failed"
action_desc = idaapi.action_desc_t(
self.ACTION_FFOPEN,
"FindFunc",
ACActionHandler(open_form),
"ctrl+alt+f", # hotkey
"",
-1
)
assert idaapi.register_action(action_desc), "Action registration failed"
idaapi.attach_action_to_menu(
'View/',
self.ACTION_FFOPEN,
idaapi.SETMENU_APP)
self.hooks.hook()
idaapi.msg("%s %s by %s loaded\n" % (self.wanted_name, VERSION, __AUTHOR__))
return idaapi.PLUGIN_KEEP
def run(self, arg):
"""
Edit->Plugins->... or hotkey
"""
open_form()
def term(self):
self.hooks.unhook()
idaapi.unregister_action(self.ACTION_COPY_BYTES)
idaapi.unregister_action(self.ACTION_COPY_OPC)
idaapi.unregister_action(self.ACTION_COPY_OPC_IMM)
idaapi.unregister_action(self.ACTION_COPY_NO_IMM)
idaapi.unregister_action(self.ACTION_COPY_DISASM)
idaapi.unregister_action(self.ACTION_FFOPEN)
global cursession
global lastsavedsession
if cursession and cursession != lastsavedsession:
reply = QMessageBox.question(None, "Save Session", "Your FindFunc session has not been saved. Save now?",
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
findfunc_gui.TabWid.save_as_session(cursession)
lastsavedsession = cursession
# plugin helper stuff
cursession = ""
lastsavedsession = ""
def open_form():
"""
open the same form, no matter how the plugin is launched
"""
global ffform
try:
ffform
except Exception:
ffform = FunctionsListForm_t()
ffform.Show()
class FunctionsListForm_t(PluginForm):
"""
wrapper required for docking
"""
def OnCreate(self, form):
self.parent = self.FormToPyQtWidget(form)
self.mtw = findfunc_gui.TabWid(True)
self.mtw.setInfoString(INFOSTR)
self.mtw.setWindowTitle(WINDOWTITLE)
global cursession
if cursession:
self.mtw.clearAll()
self.mtw.load_session_from_text(cursession)
cursession = ""
layout = QVBoxLayout()
layout.addWidget(self.mtw)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
self.parent.setLayout(layout)
def OnClose(self, form):
global cursession
global lastsavedsession
cursession = self.mtw.session_to_text()
lastsavedsession = self.mtw.lastsessionsaved
def Show(self):
return PluginForm.Show(self, WINDOWTITLE, options=PluginForm.WOPN_PERSIST)
class ACActionHandler(idaapi.action_handler_t):
"""
Action handling helper
"""
def __init__(self, action_function):
idaapi.action_handler_t.__init__(self)
self.action_function = action_function
def activate(self, ctx):
self.action_function()
return 1
def update(self, ctx):
return idaapi.AST_ENABLE_ALWAYS
class ACUiHook(idaapi.UI_Hooks):
"""
Show context menus in disasm widgets
"""
def finish_populating_widget_popup(self, widget, popup):
"""
Right click menu is about to be shown
"""
form_type = idaapi.get_widget_type(widget)
if form_type == idaapi.BWN_DISASMS:
idaapi.attach_action_to_popup(
widget,
popup,
FindFunc.ACTION_COPY_BYTES,
# "copy all bytes",
# idaapi.SETMENU_APP
)
idaapi.attach_action_to_popup(
widget,
popup,
FindFunc.ACTION_COPY_OPC,
)
idaapi.attach_action_to_popup(
widget,
popup,
FindFunc.ACTION_COPY_OPC_IMM,
)
idaapi.attach_action_to_popup(
widget,
popup,
FindFunc.ACTION_COPY_NO_IMM,
)
idaapi.attach_action_to_popup(
widget,
popup,
FindFunc.ACTION_COPY_DISASM,
)
return 0