├── plugin
├── prefix
│ ├── __init__.py
│ └── resources
│ │ ├── bulk.png
│ │ ├── clear.png
│ │ └── recursive.png
└── ida_prefix.py
├── screenshots
├── bulk.gif
├── main.png
├── clearing.gif
└── recursive.gif
├── LICENSE
└── README.md
/plugin/prefix/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/screenshots/bulk.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/screenshots/bulk.gif
--------------------------------------------------------------------------------
/screenshots/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/screenshots/main.png
--------------------------------------------------------------------------------
/screenshots/clearing.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/screenshots/clearing.gif
--------------------------------------------------------------------------------
/screenshots/recursive.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/screenshots/recursive.gif
--------------------------------------------------------------------------------
/plugin/prefix/resources/bulk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/plugin/prefix/resources/bulk.png
--------------------------------------------------------------------------------
/plugin/prefix/resources/clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/plugin/prefix/resources/clear.png
--------------------------------------------------------------------------------
/plugin/prefix/resources/recursive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gaasedelen/prefix/HEAD/plugin/prefix/resources/recursive.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Andrew Marumoto, Markus Gaasedelen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Prefix - Function Prefixing for IDA Pro
2 |
3 |
4 |
5 |
6 |
7 | ## Overview
8 |
9 | Prefix is a small function prefixing plugin for [IDA Pro](https://www.hex-rays.com/products/ida/). The plugin augments IDA's function renaming capabilities by adding a handful of convenient prefixing actions to relevant right click menus.
10 |
11 | ## Releases
12 |
13 | * v1.2 -- Supoort IDA 7.0 -> 7.4, Python 2/3, deprecates IDA 6.x.
14 | * v1.1 -- Added IDA 7 support.
15 | * v1.0 -- Initial release
16 |
17 | ## Installation
18 |
19 | Prefix is a cross-platform (Windows, macOS, Linux) Python 2/3 plugin. It takes zero third party dependencies, making the code both portable and easy to install.
20 |
21 | 1. From your disassembler's python console, run the following command to find its plugin directory:
22 | - **IDA Pro**: `os.path.join(idaapi.get_user_idadir(), "plugins")`
23 |
24 | 2. Copy the contents of this repository's `/plugin/` folder to the listed directory.
25 | 3. Restart your disassembler.
26 |
27 | This plugin is only supported for IDA 7.0 and newer.
28 |
29 | ## Usage
30 |
31 | The Prefix plugin loads automatically when an IDB is opened. The plugin will populate right click menus in the Functions, Disassembly, and HexRays views with additional actions when appropriate.
32 |
33 | ## Recursive Prefix
34 |
35 | A common technique to quickly triage and group related functions while reverse engineering is via a recursive prefix.
36 |
37 |
38 |
39 |
40 |
41 | Right clicking a function in the disassembly view now provides an option to 'Recursively prefix' a function and all of its callee's.
42 |
43 | ## Bulk Prefix
44 |
45 | Any number of functions can now be selected in the functions view and assigned a user specified prefix.
46 |
47 |
48 |
49 |
50 |
51 | ## Clear Prefix
52 |
53 | User prefixes can easily be cleared via the 'Clear prefix' option added to the Functions window right click menu.
54 |
55 |
56 |
57 |
58 |
59 | ## Authors
60 |
61 | * Andrew Marumoto
62 | * Markus Gaasedelen ([@gaasedelen](https://twitter.com/gaasedelen))
63 |
--------------------------------------------------------------------------------
/plugin/ida_prefix.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import idc
4 | import idaapi
5 | import idautils
6 |
7 | from PyQt5 import QtGui, QtCore, QtWidgets
8 |
9 | #------------------------------------------------------------------------------
10 | # IDA Plugin
11 | #------------------------------------------------------------------------------
12 |
13 | VERSION = "v1.2"
14 | AUTHORS = ['Andrew Marumoto', 'Markus Gaasedelen']
15 |
16 | def PLUGIN_ENTRY():
17 | """
18 | Required plugin entry point for IDAPython Plugins.
19 | """
20 | return prefix_t()
21 |
22 | class prefix_t(idaapi.plugin_t):
23 | """
24 | The IDA Plugin for Prefix.
25 | """
26 |
27 | flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE
28 | help = ""
29 | comment = "A plugin for easy function prefixing"
30 | wanted_name = "prefix"
31 | wanted_hotkey = ""
32 |
33 | #--------------------------------------------------------------------------
34 | # Plugin Overloads
35 | #--------------------------------------------------------------------------
36 |
37 | def init(self):
38 | """
39 | This is called by IDA when it is loading the plugin.
40 | """
41 |
42 | # initialize the menu actions our plugin will inject
43 | self._init_action_bulk()
44 | self._init_action_clear()
45 | self._init_action_recursive()
46 |
47 | # initialize plugin hooks
48 | self._init_hooks()
49 |
50 | # done
51 | idaapi.msg("%s %s initialized...\n" % (self.wanted_name, VERSION))
52 | return idaapi.PLUGIN_KEEP
53 |
54 | def run(self, arg):
55 | """
56 | This is called by IDA when this file is loaded as a script.
57 | """
58 | idaapi.msg("%s cannot be run as a script.\n" % self.wanted_name)
59 |
60 | def term(self):
61 | """
62 | This is called by IDA when it is unloading the plugin.
63 | """
64 |
65 | # unhook our plugin hooks
66 | self._hooks.unhook()
67 |
68 | # unregister our actions & free their resources
69 | self._del_action_bulk()
70 | self._del_action_clear()
71 | self._del_action_recursive()
72 |
73 | # done
74 | idaapi.msg("%s terminated...\n" % self.wanted_name)
75 |
76 | #--------------------------------------------------------------------------
77 | # Plugin Hooks
78 | #--------------------------------------------------------------------------
79 |
80 | def _init_hooks(self):
81 | """
82 | Install plugin hooks into IDA.
83 | """
84 | self._hooks = Hooks()
85 | self._hooks.ready_to_run = self._init_hexrays_hooks
86 | self._hooks.hook()
87 |
88 | def _init_hexrays_hooks(self):
89 | """
90 | Install Hex-Rrays hooks (when available).
91 |
92 | NOTE: This is called when the ui_ready_to_run event fires.
93 | """
94 | if idaapi.init_hexrays_plugin():
95 | idaapi.install_hexrays_callback(self._hooks.hxe_callback)
96 |
97 | #--------------------------------------------------------------------------
98 | # IDA Actions
99 | #--------------------------------------------------------------------------
100 |
101 | ACTION_BULK = "prefix:bulk"
102 | ACTION_CLEAR = "prefix:clear"
103 | ACTION_RECURSIVE = "prefix:recursive"
104 |
105 | def _init_action_bulk(self):
106 | """
107 | Register the bulk prefix action with IDA.
108 | """
109 |
110 | # load the icon for this action
111 | self._bulk_icon_id = idaapi.load_custom_icon(plugin_resource("bulk.png"))
112 |
113 | # describe the action
114 | action_desc = idaapi.action_desc_t(
115 | self.ACTION_BULK, # The action name.
116 | "Prefix selected functions", # The action text.
117 | IDACtxEntry(bulk_prefix), # The action handler.
118 | None, # Optional: action shortcut
119 | "Assign a user prefix to the selected functions", # Optional: tooltip
120 | self._bulk_icon_id # Optional: the action icon
121 | )
122 |
123 | # register the action with IDA
124 | assert idaapi.register_action(action_desc), "Action registration failed"
125 |
126 | def _init_action_clear(self):
127 | """
128 | Register the clear prefix action with IDA.
129 | """
130 |
131 | # load the icon for this action
132 | self._clear_icon_id = idaapi.load_custom_icon(plugin_resource("clear.png"))
133 |
134 | # describe the action
135 | action_desc = idaapi.action_desc_t(
136 | self.ACTION_CLEAR, # The action name.
137 | "Clear prefixes", # The action text.
138 | IDACtxEntry(clear_prefix), # The action handler.
139 | None, # Optional: action shortcut
140 | "Clear user prefixes from the selected functions", # Optional: tooltip
141 | self._clear_icon_id # Optional: the action icon
142 | )
143 |
144 | # register the action with IDA
145 | assert idaapi.register_action(action_desc), "Action registration failed"
146 |
147 | def _init_action_recursive(self):
148 | """
149 | Register the recursive rename action with IDA.
150 | """
151 |
152 | # load the icon for this action
153 | self._recursive_icon_id = idaapi.load_custom_icon(plugin_resource("recursive.png"))
154 |
155 | # describe the action
156 | action_desc = idaapi.action_desc_t(
157 | self.ACTION_RECURSIVE, # The action name.
158 | "Recursive function prefix", # The action text.
159 | IDACtxEntry(recursive_prefix_cursor), # The action handler.
160 | None, # Optional: action shortcut
161 | "Recursively prefix callees of this function", # Optional: tooltip
162 | self._recursive_icon_id # Optional: the action icon
163 | )
164 |
165 | # register the action with IDA
166 | assert idaapi.register_action(action_desc), "Action registration failed"
167 |
168 | def _del_action_bulk(self):
169 | """
170 | Delete the bulk prefix action from IDA.
171 | """
172 | idaapi.unregister_action(self.ACTION_BULK)
173 | idaapi.free_custom_icon(self._bulk_icon_id)
174 | self._bulk_icon_id = idaapi.BADADDR
175 |
176 | def _del_action_clear(self):
177 | """
178 | Delete the clear prefix action from IDA.
179 | """
180 | idaapi.unregister_action(self.ACTION_CLEAR)
181 | idaapi.free_custom_icon(self._clear_icon_id)
182 | self._clear_icon_id = idaapi.BADADDR
183 |
184 | def _del_action_recursive(self):
185 | """
186 | Delete the recursive rename action from IDA.
187 | """
188 | idaapi.unregister_action(self.ACTION_RECURSIVE)
189 | idaapi.free_custom_icon(self._recursive_icon_id)
190 | self._recursive_icon_id = idaapi.BADADDR
191 |
192 | #------------------------------------------------------------------------------
193 | # Plugin Hooks
194 | #------------------------------------------------------------------------------
195 |
196 | class Hooks(idaapi.UI_Hooks):
197 |
198 | def ready_to_run(self):
199 | """
200 | UI ready to run -- an IDA event fired when everything is spunup.
201 |
202 | NOTE: this is a placeholder func, it gets replaced on a live instance
203 | but we need it defined here for IDA 7.2+ to properly hook it.
204 | """
205 | pass
206 |
207 | def finish_populating_widget_popup(self, widget, popup):
208 | """
209 | A right click menu is about to be shown. (IDA 7)
210 | """
211 | inject_prefix_actions(widget, popup, idaapi.get_widget_type(widget))
212 | return 0
213 |
214 | def hxe_callback(self, event, *args):
215 | """
216 | HexRays event callback.
217 |
218 | We lump this under the (UI) Hooks class for organizational reasons.
219 | """
220 |
221 | #
222 | # if the event callback indicates that this is a popup menu event
223 | # (in the hexrays window), we may want to install our prefix menu
224 | # actions depending on what the cursor right clicked.
225 | #
226 |
227 | if event == idaapi.hxe_populating_popup:
228 | form, popup, vu = args
229 |
230 | #
231 | # if the user cursor isn't hovering over a function ref, there
232 | # is nothing for us to do
233 | #
234 |
235 | if get_cursor_func_ref() == idaapi.BADADDR:
236 | return 0
237 |
238 | #
239 | # the user cursor is hovering over a valid target for a recursive
240 | # function prefix. insert the prefix action entry into the menu
241 | #
242 |
243 | idaapi.attach_action_to_popup(
244 | form,
245 | popup,
246 | prefix_t.ACTION_RECURSIVE,
247 | "Rename global item",
248 | idaapi.SETMENU_APP
249 | )
250 |
251 | # done
252 | return 0
253 |
254 | #------------------------------------------------------------------------------
255 | # Prefix Wrappers
256 | #------------------------------------------------------------------------------
257 |
258 | def recursive_prefix_cursor():
259 | """
260 | Recursive prefix under the user cursor.
261 | """
262 |
263 | # get the function reference under the user cursor (if there is one)
264 | target = get_cursor_func_ref()
265 | if target == idaapi.BADADDR:
266 | return
267 |
268 | # execute the recursive prefix
269 | recursive_prefix(target)
270 |
271 | def inject_prefix_actions(form, popup, form_type):
272 | """
273 | Inject prefix actions to popup menu(s) based on context.
274 | """
275 |
276 | #
277 | # disassembly window
278 | #
279 |
280 | if form_type == idaapi.BWN_DISASMS:
281 |
282 | #
283 | # if the user cursor isn't hovering over a function ref, there
284 | # is nothing for us to do
285 | #
286 |
287 | if get_cursor_func_ref() == idaapi.BADADDR:
288 | return
289 |
290 | #
291 | # the user cursor is hovering over a valid target for a recursive
292 | # function prefix. insert the prefix action entry into the menu
293 | #
294 |
295 | idaapi.attach_action_to_popup(
296 | form,
297 | popup,
298 | prefix_t.ACTION_RECURSIVE,
299 | "Rename",
300 | idaapi.SETMENU_APP
301 | )
302 |
303 | #
304 | # functions window
305 | #
306 |
307 | elif form_type == idaapi.BWN_FUNCS:
308 |
309 | # inject the 'Bulk' function prefix action
310 | idaapi.attach_action_to_popup(
311 | form,
312 | popup,
313 | prefix_t.ACTION_BULK,
314 | "Delete function(s)...",
315 | idaapi.SETMENU_INS
316 | )
317 |
318 | # inject the 'Clear prefix' action
319 | idaapi.attach_action_to_popup(
320 | form,
321 | popup,
322 | prefix_t.ACTION_CLEAR,
323 | "Delete function(s)...",
324 | idaapi.SETMENU_INS
325 | )
326 |
327 | # inject a menu separator
328 | idaapi.attach_action_to_popup(
329 | form,
330 | popup,
331 | None,
332 | "Delete function(s)...",
333 | idaapi.SETMENU_INS
334 | )
335 |
336 | # done
337 | return 0
338 |
339 | #------------------------------------------------------------------------------
340 | # Prefix API
341 | #------------------------------------------------------------------------------
342 |
343 | PREFIX_DEFAULT = "MyPrefix"
344 | PREFIX_SEPARATOR = '%'
345 |
346 | def recursive_prefix(addr):
347 | """
348 | Recursively prefix a function tree with a user defined string.
349 | """
350 | func_addr = idaapi.get_name_ea(idaapi.BADADDR, idaapi.get_func_name(addr))
351 | if func_addr == idaapi.BADADDR:
352 | idaapi.msg("Prefix: 0x%08X does not belong to a defined function\n" % addr)
353 | return
354 |
355 | # prompt the user for a prefix to apply to the selected functions
356 | tag = idaapi.ask_str(PREFIX_DEFAULT, 0, "Function Tag")
357 |
358 | # the user closed the window... ignore
359 | if tag == None:
360 | return
361 |
362 | # the user put a blank string and hit 'okay'... notify & ignore
363 | elif tag == '':
364 | idaapi.warning("[ERROR] Tag cannot be empty [ERROR]")
365 | return
366 |
367 | # recursively collect all the functions called by this function
368 | nodes_xref_down = graph_down(func_addr, path=set([]))
369 |
370 | # graph_down returns the int address needs to be converted
371 | tmp = []
372 | tmp1 = ''
373 | for func_addr in nodes_xref_down:
374 | tmp1 = idaapi.get_func_name(func_addr)
375 | if tmp1:
376 | tmp.append(tmp1)
377 | nodes_xref_down = tmp
378 |
379 | # prefix the tree of functions
380 | for rename in nodes_xref_down:
381 | func_addr = idaapi.get_name_ea(idaapi.BADADDR, rename)
382 | if tag not in rename:
383 | idaapi.set_name(func_addr,'%s%s%s' % (str(tag), PREFIX_SEPARATOR, rename), idaapi.SN_NOWARN)
384 |
385 | # refresh the IDA views
386 | refresh_views()
387 |
388 | def bulk_prefix():
389 | """
390 | Prefix the Functions window selection with a user defined string.
391 | """
392 |
393 | # prompt the user for a prefix to apply to the selected functions
394 | tag = idaapi.ask_str(PREFIX_DEFAULT, 0, "Function Tag")
395 |
396 | # the user closed the window... ignore
397 | if tag == None:
398 | return
399 |
400 | # the user put a blank string and hit 'okay'... notify & ignore
401 | elif tag == '':
402 | idaapi.warning("[ERROR] Tag cannot be empty [ERROR]")
403 | return
404 |
405 | #
406 | # loop through all the functions selected in the 'Functions window' and
407 | # apply the user defined prefix tag to each one.
408 | #
409 |
410 | for func_name in get_selected_funcs():
411 |
412 | # ignore functions that already have the specified prefix applied
413 | if func_name.startswith(tag):
414 | continue
415 |
416 | # apply the user defined prefix to the function (rename it)
417 | new_name = '%s%s%s' % (str(tag), PREFIX_SEPARATOR, func_name)
418 | func_addr = idaapi.get_name_ea(idaapi.BADADDR, func_name)
419 | idaapi.set_name(func_addr, new_name, idaapi.SN_NOWARN)
420 |
421 | # refresh the IDA views
422 | refresh_views()
423 |
424 | def clear_prefix():
425 | """
426 | Clear user defined prefixes from the selected functions in the Functions window.
427 | """
428 |
429 | #
430 | # loop through all the functions selected in the 'Functions window' and
431 | # clear any user defined prefixes applied to them.
432 | #
433 |
434 | for func_name in get_selected_funcs():
435 |
436 | #
437 | # locate the last (rfind) prefix separator in the function name as
438 | # we will want to keep everything that comes after it
439 | #
440 |
441 | i = func_name.rfind(PREFIX_SEPARATOR)
442 |
443 | # if there is no prefix (separator), there is nothing to trim
444 | if i == -1:
445 | continue
446 |
447 | # trim the prefix off the original function name and discard it
448 | new_name = func_name[i+1:]
449 | func_addr = idaapi.get_name_ea(idaapi.BADADDR, func_name)
450 | idaapi.set_name(func_addr, new_name, idaapi.SN_NOWARN)
451 |
452 | # refresh the IDA views
453 | refresh_views()
454 |
455 | #------------------------------------------------------------------------------
456 | # IDA Util
457 | #------------------------------------------------------------------------------
458 |
459 | def refresh_views():
460 | """
461 | Refresh the IDA views.
462 | """
463 |
464 | # refresh IDA views
465 | idaapi.refresh_idaview_anyway()
466 |
467 | # refresh hexrays
468 | current_widget = idaapi.get_current_widget()
469 | vu = idaapi.get_widget_vdui(current_widget)
470 | if vu:
471 | vu.refresh_ctext()
472 |
473 | def get_all_funcs():
474 | """
475 | Enumerate all function names defined in the IDB.
476 | """
477 | return set(idaapi.get_func_name(ea) for ea in idautils.Functions())
478 |
479 | def get_cursor_func_ref():
480 | """
481 | Get the function reference under the user cursor.
482 |
483 | Returns BADADDR or a valid function address.
484 | """
485 | current_widget = idaapi.get_current_widget()
486 | form_type = idaapi.get_widget_type(current_widget)
487 | vu = idaapi.get_widget_vdui(current_widget)
488 |
489 | #
490 | # hexrays view is active
491 | #
492 |
493 | if vu:
494 | cursor_addr = vu.item.get_ea()
495 |
496 | #
497 | # disassembly view is active
498 | #
499 |
500 | elif form_type == idaapi.BWN_DISASM:
501 | cursor_addr = idaapi.get_screen_ea()
502 | opnum = idaapi.get_opnum()
503 |
504 | if opnum != -1:
505 |
506 | #
507 | # if the cursor is over an operand value that has a function ref,
508 | # use that as a valid rename target
509 | #
510 |
511 | op_addr = idc.get_operand_value(cursor_addr, opnum)
512 | op_func = idaapi.get_func(op_addr)
513 |
514 | if op_func and op_func.start_ea == op_addr:
515 | return op_addr
516 |
517 | # unsupported/unknown view is active
518 | else:
519 | return idaapi.BADADDR
520 |
521 | #
522 | # if the cursor is over a function definition or other reference, use that
523 | # as a valid rename target
524 | #
525 |
526 | cursor_func = idaapi.get_func(cursor_addr)
527 | if cursor_func and cursor_func.start_ea == cursor_addr:
528 | return cursor_addr
529 |
530 | # fail
531 | return idaapi.BADADDR
532 |
533 | def get_selected_funcs():
534 | """
535 | Return the list of function names selected in the Functions window.
536 | """
537 | import sip
538 | twidget = idaapi.find_widget("Functions window")
539 | widget = sip.wrapinstance(int(twidget), QtWidgets.QWidget)
540 |
541 | # TODO: test this
542 | if not widget:
543 | idaapi.warning("Unable to find 'Functions window'")
544 | return
545 |
546 | #
547 | # locate the table widget within the Functions window that actually holds
548 | # all the visible function metadata
549 | #
550 |
551 | table = widget.findChild(QtWidgets.QTableView)
552 |
553 | #
554 | # scrape the selected function names from the Functions window table
555 | #
556 |
557 | selected_funcs = [str(s.data()) for s in table.selectionModel().selectedRows()]
558 |
559 | #
560 | # re-map the scraped names as they appear in the function table, to their true
561 | # names as they are saved in the IDB. See the match_funcs(...) function
562 | # comment for more details
563 | #
564 |
565 | return match_funcs(selected_funcs)
566 |
567 | def match_funcs(qt_funcs):
568 | """
569 | Convert function names scraped from Qt to their *actual* representation.
570 |
571 | The function names we scrape from the Functions window Qt table actually
572 | use the underscore character ('_') as a substitute for a variety of
573 | different characters.
574 |
575 | For example, a function named foo%bar in the IDB will appears as foo_bar
576 | in the Functions window table.
577 |
578 | This function takes a list of names as they appear in the Functions window
579 | table such as the following:
580 |
581 | ['foo_bar']
582 |
583 | And applies a best effort lookup to return a list of the 'true' function
584 | names as they are stored in the IDB.
585 |
586 | ['foo%bar']
587 |
588 | TODO: rewrite this to be more efficient for larger idbs
589 | TODO: takes first matching function, may want to change it to make the requirements more strict
590 | """
591 | res = set()
592 | ida_funcs = get_all_funcs()
593 | for f in qt_funcs:
594 | for f2 in ida_funcs:
595 | if len(f) == len(f2):
596 | i = 0
597 | while i < len(f) and (f[i] == f2[i] or f[i] == '_'):
598 | i += 1
599 |
600 | if i == len(f):
601 | res.add(f2)
602 | break
603 |
604 | return list(res)
605 |
606 | def graph_down(ea, path=set()):
607 | """
608 | Recursively collect all function calls.
609 |
610 | Copied with minor modifications from
611 | http://hooked-on-mnemonics.blogspot.com/2012/07/renaming-subroutine-blocks-and.html
612 | """
613 | path.add(ea)
614 |
615 | #
616 | # extract all the call instructions from the current function
617 | #
618 |
619 | call_instructions = []
620 | instruction_info = idaapi.insn_t()
621 | for address in idautils.FuncItems(ea):
622 |
623 | # decode the instruction
624 | if not idaapi.decode_insn(instruction_info, address):
625 | continue
626 |
627 | # check if this instruction is a call
628 | if not idaapi.is_call_insn(instruction_info):
629 | continue
630 |
631 | # save this address as a call instruction
632 | call_instructions.append(address)
633 |
634 | #
635 | # iterate through all the instructions in the target function (ea) and
636 | # inspect all the call instructions
637 | #
638 |
639 | for x in call_instructions:
640 |
641 | # TODO
642 | for r in idautils.XrefsFrom(x, idaapi.XREF_FAR):
643 | #print(0x%08X" % h, "--calls-->", "0x%08X" % r.to)
644 | if not r.iscode:
645 | continue
646 |
647 | # get the function pointed at by this call
648 | func = idaapi.get_func(r.to)
649 | if not func:
650 | continue
651 |
652 | # ignore calls to imports / library calls / thunks
653 | if (func.flags & (idaapi.FUNC_THUNK | idaapi.FUNC_LIB)) != 0:
654 | continue
655 |
656 | #
657 | # if we have not traversed to the destination function that this
658 | # call references, recurse down to it to continue our traversal
659 | #
660 |
661 | if r.to not in path:
662 | graph_down(r.to, path)
663 |
664 | return path
665 |
666 | class IDACtxEntry(idaapi.action_handler_t):
667 | """
668 | A basic Context Menu class to utilize IDA's action handlers.
669 | """
670 |
671 | def __init__(self, action_function):
672 | idaapi.action_handler_t.__init__(self)
673 | self.action_function = action_function
674 |
675 | def activate(self, ctx):
676 | """
677 | Execute the embedded action_function when this context menu is invoked.
678 | """
679 | self.action_function()
680 | return 1
681 |
682 | def update(self, ctx):
683 | """
684 | Ensure the context menu is always available in IDA.
685 | """
686 | return idaapi.AST_ENABLE_ALWAYS
687 |
688 | #------------------------------------------------------------------------------
689 | # Plugin Util
690 | #------------------------------------------------------------------------------
691 |
692 | PLUGIN_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), prefix_t.wanted_name))
693 |
694 | def plugin_resource(resource_name):
695 | """
696 | Return the full path for a given plugin resource file.
697 | """
698 | return os.path.join(
699 | PLUGIN_PATH,
700 | "resources",
701 | resource_name
702 | )
703 |
704 |
--------------------------------------------------------------------------------