Self Improving IDAPro
Adding new right click menu options to IDAPro
Intro
IDA plugins empower reverse engineers by enabling custom IDAPro functionality. Context hooks are a type of plugin which allows for customization of IDAPro’s right click menus. Having a premade context hook script template makes it really simple to quickly customize IDA’s user interface (UI). This article covers what a basic template might look like for adding items to IDA’s context menu and walks through a quick example of attaching a python script to it. This post assumes python comprehension and for a more comprehensive explanation of the different IDAPython parts in this post, see this Hex-Rays article.
Note: The code is tested against the latest version of IDA which at this time is v8.1.
Template Setup
Code Snippet 1 contains the boiler plate code needed to add items to the context menu. These lines are used to determine whether to add the context menu to the disassembly or the pseudocode view:
if tft == idaapi.BWN_DISASM:
elif tft == idaapi.BWN_PSEUDOCODE:
These are not the only 2 options. See the list of BWN_
global variables in the IDA Python Docs for the full list of IDA views.
import idaapi
import idautils
class ContextHooks(idaapi.UI_Hooks):
def finish_populating_widget_popup(self, form, popup):
tft = idaapi.get_widget_type(form)
if tft == idaapi.BWN_DISASM:
pass
elif tft == idaapi.BWN_PSEUDOCODE:
pass
hooks = ContextHooks()
hooks.hook()
Add Code Snippet 2 into each if
block of Code Snippet 1 where we want to add a context menu option.
CONTEXT_MENU_PATH = 'Main Entry/SubMenu Entry/'
ITEM_NAME = 'Context Menu Entry Name'
action_name_xorhex = idaapi.action_desc_t(
None, ITEM_NAME, handler_class()
)
idaapi.attach_dynamic_action_to_popup(
form,
popup,
action_name_xorhex,
CONTEXT_MENU_PATH,
idaapi.SETMENU_INS,
)
Table 1 outlines the items to change in Code Snippet 2.
Line No. | Snippet | Description |
---|---|---|
1 | CONTEXT_MENU_PATH | The Menu item name and the submenu name. Make sure this ends with a / or IDA may not render all the text prior. |
2 | ITEM_NAME | The display name of the item added to the context menu. |
4 | action_name_xorhex | The variable idaapi.action_desc_t gets assigned to. |
5 | handler_class() | Initialize the class that handles the code that gets executed upon the item being selected. |
10 | action_name_xorhex | Make sure this matches the variable name assigned in line 1. |
Recommend updating the action_name_xorhex
and handler_class
to something more meaningful; especially if adding more than one entry.
The handler_class
is the final piece of code needed, which can be declared like this:
class handler_class(idaapi.action_handler_t):
def activate(self, ctx):
pass
def update(self, ctx):
pass
Recommend updating the class name to something more meaningful.
Example
Using the above code snippets, let’s put construct an example that ties all of them together. For our example, we will insert an item into the context menu which adds breakpoints to all call
instructions in the current function.
Step 1 - Action Handler Class
Write the handler function to:
- walk through the basic blocks
- look for
call
instructions - add a breakpoint at the
call
location
class handler_add_call_bps_to_function(idaapi.action_handler_t):
def activate(self, ctx):
func = idaapi.get_func(idaapi.get_screen_ea())
flowchart = idaapi.FlowChart(func)
for block in flowchart:
ea = block.start_ea
while ea < block.end_ea:
instr = idautils.DecodeInstruction(ea)
if instr.itype in [idaapi.NN_callni, idaapi.NN_call, idaapi.NN_callfi]:
idaapi.add_bpt(instr.ea)
ea = idaapi.next_head(instr.ea, block.end_ea)
def update(self, ctx):
pass
Note: The last instruction in a basic block belongs to the next basic block, so it can safely be skipped.
Step 2 - Context Hooks Class
Update the context hook class. Only the items listed in Table 1 are updated.
CONTEXT_MENU_PATH = 'XorHex/BreakPoints/'
ITEM_NAME = 'Add to calls'
class ContextHooks(idaapi.UI_Hooks):
def finish_populating_widget_popup(self, form, popup):
tft = idaapi.get_widget_type(form)
if tft == idaapi.BWN_DISASM:
action_add_call_bps = idaapi.action_desc_t(
None, ITEM_NAME, handler_add_call_bps_to_function()
)
idaapi.attach_dynamic_action_to_popup(
form,
popup,
action_add_call_bps,
CONTEXT_MENU_PATH,
idaapi.SETMENU_INS,
)
elif tft == idaapi.BWN_PSEUDOCODE:
pass
Step 3 - Add to IDA
Save the python file into IDA’s plugin directory and (re)start IDA.
- Linux:
~/.idapro/plugins/
Open a binary and, in the disassembly view, right click to see the added option.
Conclusion
By having a ready-made template, one can quickly turn IDAPython scripts into UI-driven features.
Appendix
Example Script
import idaapi
import idautils
CONTEXT_MENU_PATH = 'XorHex/BreakPoints/'
ITEM_NAME = 'Add to calls'
class handler_add_call_bps_to_function(idaapi.action_handler_t):
def activate(self, ctx):
func = idaapi.get_func(idaapi.get_screen_ea())
flowchart = idaapi.FlowChart(func)
for block in flowchart:
ea = block.start_ea
while ea < block.end_ea:
instr = idautils.DecodeInstruction(ea)
if instr.itype in [idaapi.NN_callni, idaapi.NN_call, idaapi.NN_callfi]:
idaapi.add_bpt(instr.ea)
ea = idaapi.next_head(instr.ea, block.end_ea)
def update(self, ctx):
pass
class ContextHooks(idaapi.UI_Hooks):
def finish_populating_widget_popup(self, form, popup):
tft = idaapi.get_widget_type(form)
if tft == idaapi.BWN_DISASM:
action_add_call_bps = idaapi.action_desc_t(
None, ITEM_NAME, handler_add_call_bps_to_function()
)
idaapi.attach_dynamic_action_to_popup(
form,
popup,
action_add_call_bps,
CONTEXT_MENU_PATH,
idaapi.SETMENU_INS,
)
elif tft == idaapi.BWN_PSEUDOCODE:
pass
hooks = ContextHooks()
hooks.hook()
Template Script
import idaapi
import idautils
CONTEXT_MENU_PATH = 'Main Entry/SubMenu Entry/'
ITEM_NAME = 'Context Menu Entry Name'
class handler_class(idaapi.action_handler_t):
def activate(self, ctx):
pass
def update(self, ctx):
pass
class ContextHooks(idaapi.UI_Hooks):
def finish_populating_widget_popup(self, form, popup):
tft = idaapi.get_widget_type(form)
if tft == idaapi.BWN_DISASM:
action_name_xorhex = idaapi.action_desc_t(
None, ITEM_NAME, handler_class()
)
idaapi.attach_dynamic_action_to_popup(
form,
popup,
action_name_xorhex,
CONTEXT_MENU_PATH,
idaapi.SETMENU_INS,
)
elif tft == idaapi.BWN_PSEUDOCODE:
pass
hooks = ContextHooks()
hooks.hook()