xorhex logo

xorhex

Focus on Threat Research Things.

Self Improving IDAPro

Adding new right click menu options to IDAPro

xorhex

5-Minute Read

IDA Plugin Context Menu Plugin Blog Header Picture

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()
Code Snippet 1 (IDA Python Context Hook Script)

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,
)
Code Snippet 2 (Attach Action to IDA Python Context Menu)

Table 1 outlines the items to change in Code Snippet 2.

Line No.SnippetDescription
1CONTEXT_MENU_PATHThe Menu item name and the submenu name. Make sure this ends with a / or IDA may not render all the text prior.
2ITEM_NAMEThe display name of the item added to the context menu.
4action_name_xorhexThe variable idaapi.action_desc_t gets assigned to.
5handler_class()Initialize the class that handles the code that gets executed upon the item being selected.
10action_name_xorhexMake sure this matches the variable name assigned in line 1.
Table 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
Code Snippet 3 (IDA Python Action Handler Class Template)

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
Code Snippet 4 (IDA Python Action Handler Class Adding Breakpoints)

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
Code Snippet 5 (IDA Python Adding the Context Hook)

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.

Figure 1: New context menu item in IDA

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()
Code Snippet 6 (Complete Script)

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()
Code Snippet 7 (Template Script)

Recent Posts

Categories

About

Hosting my custom tools, threat research, and general reverse engineering notes.