Augmenting IDA UI with your own actions.

Intended audience

Plugin writers, either using the C SDK or IDAPython, who would like to add actions/commands to IDA UI in order to augment its capabilities.

Rationale: before 6.7

APIs galore

Depending on what type of context you were in, various APIs were available to you:

  • Want to add a main menu item?

    add_menu_item(const char *menupath, const char *name, const char *hotkey, int flags, menu_item_callback_t *callback, void *ud);
    del_menu_item(const char *menupath)
    
  • Want to add an action to a list view? (or, as we call them: ‘choosers’)

    add_chooser_command(const char *chooser_caption, const char *cmd_caption, chooser_cb_t *chooser_cb, int menu_index=-1, int icon=-1, int flags=0);
    add_chooser_command(const char *chooser_caption, const char *cmd_caption, chooser_cb_t *chooser_cb, const char *hotkey, int menu_index=-1, int icon=-1, int flags=0);
    

    …or through populating callbacks:

    struct chooser_info_t
    {
        // ...snipped...
        // function is called every time before
        // context menu is shown to fill user_cmds
        prepare_popup_cmds_t *prepare_popup_cmds;
    };
    
  • Want to add/manage the items that show up in the context menu for a custom code viewer you have created? (that does not work for the ‘core’ viewers like ‘IDA View-A’!)

    set_custom_viewer_popup_menu(TCustomControl *custom_viewer, TPopupMenu *menu);
    add_custom_viewer_popup_item(TCustomControl *custom_viewer, const char *title, const char *hotkey, menu_item_callback_t *cb, void *ud);
    
  • Want to add/manage the items that show up in the context menu for a custom graph viewer you have created? (again, doesn’t work with ‘core’ viewers)

    viewer_add_menu_item(graph_viewer_t *gv, const char *title, menu_item_callback_t *callback, void *ud, const char *hotkey, int flags);
    
  • Want to add an item to the Output window’s context menu?

    add_output_popup(char *n, menu_item_callback_t *c, void *u);
    

Drawbacks

Note that not all of those functions take the same set of parameters, or even the same type for seemingly-related ones (e.g., chooser_cb_t *chooser_cb VS menu_item_callback_t *callback)

It is also not possible to specify some attributes of the commands in some cases. E.g.,

  • add_menu_item() doesn’t allow to specify an icon
  • add_output_popup() doesn’t accept a shortcut

And it’s just plain impossible to do some things. E.g.,

  • augment IDA View’s context menu with your own actions
    EDIT: This is incorrect, as the ‘view_popup’ notification could be used for this. However, this is still limited to IDA View-A, and cannot be used for other widgets.
  • remove an item that was previously added to the Output window’s context menu through add_output_popup().
  • in some cases, actions shortcuts would do nothing if the context menu wasn’t currently displayed (i.e., showing the context menu was necessary to enabled the hotkeys)
  • adding custom toolbar buttons was impossible.

These inconsistencies/lack of features were making writing extensions difficult, as the wealth of APIs was somewhat confusing and it was easy to hit the limitations.

In addition, there was no really good way of enabling/disabling your actions, or changing their properties after adding them.

Thus, we decided a refactoring of those APIs was in order.

The new actions API

Borrowing some concepts from Qt itself (upon which IDA’s graphical interface relies) we have decided to redesign the actions API in a hopefully more streamlined manner.

Key aspects

  • An action first needs to be registered. Once registered, it can be triggered using its shortcut (if one was specified), but it doesn’t appear anywhere in the UI just yet.
  • Once registered, it is possible attach/detach that action to/from things:
    • Main menus
    • Toolbars
    • Context menus
  • The same action can be used in multiple places.
  • An action has a “handler”, which is a small structure consisting of two callbacks:
    • an ‘activate’ callback, called when the action was triggered (i.e., user selected a menu item, or entered a shortcut, or pressed a toolbar item, …)
    • an ‘update’ callback, which is responsible for two things:
      • Declaring whether the action is currently enabled, and when it should be queried again for that information.
      • Possibly updating the action’s state (e.g., text, icon, …) (although that’s rarely needed.)

What follows is a series of short examples, using the IDAPython bindings for actions. Note that the IDAPython API is purposely similar to the C SDK API.

Registering an action

    # 1) Create the handler class
    class MyHandler(idaapi.action_handler_t):
        def __init__(self):
            idaapi.action_handler_t.__init__(self)

        # Say hello when invoked.
        def activate(self, ctx):
            print "Hello!"
            return 1

        # This action is always available.
        def update(self, ctx):
            return idaapi.AST_ENABLE_ALWAYS


    # 2) Describe the action
    action_desc = idaapi.action_desc_t(
        'my:action',   # The action name. This acts like an ID and must be unique
        'Say hello!',  # The action text.
        MyHandler(),   # The action handler.
        'Ctrl+H',      # Optional: the action shortcut
        'Says hello',  # Optional: the action tooltip (available in menus/toolbar)
        199)           # Optional: the action icon (shows when in menus/toolbars)

    # 3) Register the action
    idaapi.register_action(action_desc)

From there on, the action is created and, although it has not yet been attached to menus, toolbars or context menus, it can already be invoked by pressing Ctrl+H:

This is slightly more code than before (and in this case, it is explicitely verbose), but regardless of the context/widget in which you want to use that action, it will always be the same API.

Note: In this example, we used icon number 199, which corresponds to a stock icon. It is perfectly possible to define your own icons by using the API function load_custom_icon(). See kernwin.hpp for more information.

Inserting the action into the menu

    idaapi.attach_action_to_menu(
        'Edit/Other/Manual instruction...', # The relative path of where to add the action
        'my:action',                        # The action ID (see above)
        idaapi.SETMENU_APP)                 # We want to append the action after the 'Manual instruction...'

Inserting the action into a toolbar

    idaapi.attach_action_to_toolbar(
        "AnalysisToolBar",  # The toolbar name
        'my:action')        # The action ID

Inserting the action into a widget’s context menu

There are two ways to add actions to a widget’s context menu:

  • Permanently: the action will be there every time the context menu is shown
  • On the fly: you can attach an action as the context menu is being populated, and the action will be displayed in this invocation of the context menu, but not others (unless you attach it again, obviously.)

Attaching an action permanently

To permanently attach an action to a widget’s context menu:

    # Create a widget, or retrieve a pointer to it.
    form = idaapi.get_current_tform()
    idaapi.attach_action_to_popup(form, None, "my:action", None)

Notice that:

  • We used get_current_tform(), to retrieve the widget that currently has the keyboard focus.
  • We passed None as second parameter to attach_action_to_popup(). That means we want to attach the action to that widget permanently.

Pros:

  • Trivial to use.
  • This is what you typically want to use when you create your own widgets

Cons:

  • Only feasible when you can get a reference (i.e., pointer) to the widget.

Attaching an action on-the-fly

To attach an action to a context menu when it is being created, you need to listen to a UI notification, and use the same attach_action_to_popup function:

    class Hooks(idaapi.UI_Hooks):
        def populating_tform_popup(self, form, popup):
            # You can attach here.
            pass

        def finish_populating_tform_popup(self, form, popup):
            # Or here, after the popup is done being populated by its owner.

            # We will attach our action to the context menu
            # for the 'Functions window' widget.
            # The action will be be inserted in a submenu of
            # the context menu, named 'Others'.
            if idaapi.get_tform_type(form) == idaapi.BWN_FUNCS:
                idaapi.attach_action_to_popup(form, popup, "my:action", "Others/")

    hooks = Hooks()
    hooks.hook()

Notice that:

  • This time, the 2nd parameter is not ‘None’. That instructs the function to attach the action for this invocation only.
  • We know that we are currently in the “Functions window”, by checking the type of the current form against BWN_FUNCS. Many other BWN_* values are available; see kernwin.hpp for a list of those!
  • As an alternative, we could check the form’s title, using get_tform_title(form).

Pros:

  • Finer-grained control.
  • You typically want to use this when you cannot easily retrieve a reference (i.e., pointer) to the widget.

Cons:

  • More difficult to use: requires UI hooks.

A word about the update callback

As stated above, the update callback is responsible not only for possibly updating some of the actions properties (See the update_action_* functions in kernwin.hpp), but also for stating whether the action is currently available or not, and when update should be queried again.

Per contract, update should return one of the following values (again, from kernwin.hpp):

    AST_ENABLE_ALWAYS     // enable action and do not call action_handler_t::update() anymore
    AST_ENABLE_FOR_IDB    // enable action for the current idb. Call action_handler_t::update() when a database is opened/closed
    AST_ENABLE_FOR_FORM   // enable action for the current form. Call action_handler_t::update() when a form gets/loses focus
    AST_ENABLE            // enable action - call action_handler_t::update() when anything changes

    AST_DISABLE_ALWAYS    // disable action and do not call action_handler_t::action() anymore
    AST_DISABLE_FOR_IDB   // analog of ::AST_ENABLE_FOR_IDB
    AST_DISABLE_FOR_FORM  // analog of ::AST_ENABLE_FOR_FORM
    AST_DISABLE           // analog of ::AST_ENABLE

Thus, not only does update tell IDA whether the action is available or not, but it also tells IDA the “time span” during which the action will be (un)available.

For example, returning AST_ENABLE_FOR_FORM will tell IDA: “this action is available for the current widget; don’t call update again until the user switches to another widget.”

The AST_* values really represent various levels of “granularity”, where

  • AST_ENABLE_ALWAYS is the coarser level: the action will be available all the time, whatever happens.
  • AST_ENABLE is the finest level: whenever the user moves the cursor, switches to another form, triggers another action, …, update will be called again.

“Dynamic” actions

In most cases, you will want your action to be registered in IDA, and be available through menus, shortcuts, etc…

But there can be “one-shot” situations where you really want to add an action to a context menu only once, and you really don’t want to go through the trouble of registering & deregistering the action.

For those “one-shot” situations, although they are rare, we have created a helper:

    class Hooks(idaapi.UI_Hooks):
        def finish_populating_tform_popup(self, form, popup):
            tft = idaapi.get_tform_type(form)
            if tft == idaapi.BWN_EXPORTS:

                # Define a silly handler.
                class MyHandler(idaapi.action_handler_t):
                    def activate(self, ctx):
                        print "Hello from exports"
                    def update(self, ctx):
                        return idaapi.AST_ENABLE_ALWAYS

                # Note the 'None' as action name (1st parameter).
                # That's because the action will be deleted immediately
                # after the context menu is hidden anyway, so there's
                # really no need giving it a valid ID.
                desc = idaapi.action_desc_t(None, 'My dynamic action', MyHandler())
                idaapi.attach_dynamic_action_to_popup(form, popup, desc, None)

    hooks = Hooks()
    hooks.hook()

The main difference When using attach_dynamic_action_to_popup is that the action will be available only during the time the context menu is displayed. Once the context menu is hidden, the action will be unregistered & destroyed.

Removing action from things

  • detach_action_from_menu
  • detach_action_from_toolbar
  • unregister_action

Older APIs: Backward compatibility

All the previous API is still supported. We have fairly good test coverage of those, and therefore are quite confident the backward compatibility is working.

If your plugin suddenly stops working properly in 6.7, that’s a bug. Should that happen, please let us know about it and we’ll do our best to address it.

Extending IDAPython in IDA 6.5: Be careful about the GIL

Target audience

You may want to read this if you have been writing an IDA C++ plugin, that itself uses the CPython runtime.

Prior art

In 2010, Elias Bachaalany wrote a blog post about extending IDAPython: http://www.hexblog.com/?p=126

Note that this is not about writing your own plugins in Python. Rather, that blog post instruct on how you may want to write a plugin that, itself, links against libpython27, so that it interacts nicely with IDAPython (which links against the same libpython27 library).

Before 6.5

The following plugin body was enough to have a working C++ plugin, that will execute a simple Python statement:

void idaapi run(int)
{
PyRun_SimpleString("print \"Hello from plugin\"");
}

Notes about the previous example

It is worth pointing out that, whenever one wants to execute any CPython code (such as the ‘PyRun_SimpleString’ call above), one must have acquired what is called in CPython-land as the Global Interpreter Lock, or GIL: https://wiki.python.org/moin/GlobalInterpreterLock.
It may seem strange, therefore, that the code above even works, since there’s nothing that even remotely looks like a “GIL-acquiring” sequence of instructions.

The reason the code above used to work is because IDAPython was not releasing the GIL: it was acquiring it at startup-time, and never releasing it. Ever.

IDA 6.5 & later versions

In IDA 6.5, many efforts have been made to improve the use of multithreading in IDAPython: The IDAPython plugin acquires the GIL just in time to execute CPython code and then releases it, as it should. This will permit a reliable use of threading in IDAPython (in some circumstances, when using multithreading, IDA < 6.5 could freeze because of the GIL not being released.)

Starting with 6.5, such a plugin would require to be written in the following manner:

void idaapi run(int)
{
PyGILState_STATE state = PyGILState_Ensure(); // Acquire the GIL
PyRun_SimpleString("print \"Hello from plugin\"");
PyGILState_Release(state); // Release the GIL
}

Note the use of the CPython-defined PyGILState_* helpers.

Existing plugins

Unfortunately, we couldn’t do anything in this case to ensure backwards-compatibility: existing plugins will have to be adapted & recompiled. Sorry about this, but we just could not find a reasonable solution to maintain bw-compat in this case; hence this blog post.

So far, we have received a grand total of 1 report for this issue, so this doesn’t appear to impact many of our users. But in case you are running into problems with your previously-compiled CPython-aware plugin, hopefully this will have explained why.

Loading your own modules from your IDAPython scripts with idaapi.require()

TL;DR

If you were using import to import your own “currently-in-development” modules from your IDAPython scripts, you may want to use idaapi.require(), starting with IDA 6.5.

Rationale

When using IDAPython scripts, users were sometimes facing the following issue

Specifically:

  • User loads script
  • Script imports user’s module mymodule
  • Script ends
  • User modifies code of mymodule (Note: the module is modified, not the script)
  • User reloads script
  • Modifications to mymodule aren’t taken into consideration.

While that’s perfectly understandable (the python runtime doesn’t have to reload mymodule if it has been compiled & loaded already), this is somewhat of an annoyance for users that were importing modules that were often modified.

IDA <= 6.4: Ensuring a user-specified module gets reloaded, by destroying it.

Up until IDA 6.4, the IDAPython plugin would do some magic after you have run your user script.
(click “expand all” to reveal the diff)

The sequence becomes:

  • User loads script
  • Script imports user’s module mymodule
  • Script ends
  • [module mymodule is deleted]
  • User modifies code of mymodule
  • User reloads script
  • Modifications to mymodule are taken into consideration, since module was deleted.

Unfortunately we have to stop doing this because:

  • That prevents us from using python-based hooks to be used after the script is finished (see below).
  • That goes against the rest of the python philosophy (i.e., modifications to objects are not reverted), and is therefore unexpected.

Issues with hooks.

Imagine you have the following script, dbghooks.py:

from idaapi import *
import mydbghelpers

class MyHooks(DBG_Hooks):

  def __init__(self):
    ...

  def dbg_bpt(self, tid, ea):
    mydbghelpers.do_something()
    return 0

  def dbg_step_into(self):
    ...

hooks = MyHooks()
hooks.hook()
  • User loads script
  • Scripts imports mydbghelpers
  • Script creates instance of MyHooks, and hooks it into IDA’s debugger APIs
  • Script ends
  • [module mydbghelpers is deleted]
  • User runs debugger, and a breakpoint is hit. Two things can happen:
    • The hook fails executing
    • IDA crashes (that can happen if the form from mydbghelpers import * was used)

IDA > 6.4: Introducing idaapi.require()

Everywhere else in python, when you modify a runtime object, those changes will remain visible.

We decided it would be better to not go against that standard behaviour anymore, and provide a helper to achieve the same results as what was achieved before with the deletion of user modules.

You can now import & re-import of a module with: idaapi.require(name)

Here is its definition:

def require(modulename):
    if modulename in sys.modules.keys():
        reload(sys.modules[modulename])
    else:
        import importlib
        import inspect
        m = importlib.import_module(modulename)
        frame_obj, filename, line_number, function_name, lines, index = inspect.stack()[1]
        importer_module = inspect.getmodule(frame_obj)
        if importer_module is None: # No importer module; called from command line
            importer_module = sys.modules['__main__']
        setattr(importer_module, modulename, m)
        sys.modules[modulename] = m

Example

The example debugger hooks script above becomes:

from idaapi import *
idaapi.require("mydbghelpers")

class MyHooks(DBG_Hooks):

  def __init__(self):
    ...

  def dbg_bpt(self, tid, ea):
    mydbghelpers.do_something()
    return 0

  def dbg_step_into(self):
    ...

hooks = MyHooks()
hooks.hook()

I.e., only the second line changes.

Calling IDA APIs from IDAPython with ctypes

IDAPython provides wrappers for a big chunk of IDA SDK. Still, there are some APIs that are not wrapped because of SWIG limitations or just because we didn’t get to them yet. Recently, I needed to test the get_loader_name() API which is not available in IDAPython but I didn’t want to write a full plugin just for one call. For such cases it’s often possible to use the ctypes module to call the function manually.

The IDA APIs are provided by the kernel dynamic library. In Windows, it’s called ida.wll (or ida64.wll), in Linux libida[64].so and on OS X libida[64].dylib. ctypes provides a nice feature that dynamically creates a callable wrapper for a DLL export by treating it as an attribute of a special class instance. Here’s how to get that instance under the three platforms supported by IDA:

import ctypes
idaname = "ida64" if __EA64__ else "ida"
if sys.platform == "win32":
    dll = ctypes.windll[idaname + ".wll"]
elif sys.platform == "linux2":
    dll = ctypes.cdll["lib" + idaname + ".so"]
elif sys.platform == "darwin":
    dll = ctypes.cdll["lib" + idaname + ".dylib"]

We use “windll” because IDA APIs use stdcall calling convention on Windows (check the definition of idaapi in pro.h).

Now we just need to call our function just as if it was an attribute of the “dll” object. But first we need to prepare the arguments. Here’s the declaration from loader.hpp:

idaman ssize_t ida_export get_loader_name(char *buf, size_t bufsize);

ctypes provides a convenience functions for creating character buffers:

buf = ctypes.create_string_buffer(256)

And now we can call the function:

dll.get_loader_name(buf, 256)

To retrieve the contents of the buffer as a Python byte string, just use its .raw attribute. The complete script now looks like this:

import ctypes
idaname = "ida64" if __EA64__ else "ida"
if sys.platform == "win32":
    dll = ctypes.windll[idaname + ".wll"]
elif sys.platform == "linux2":
    dll = ctypes.cdll["lib" + idaname + ".so"]
elif sys.platform == "darwin":
    dll = ctypes.cdll["lib" + idaname + ".dylib"]
buf = ctypes.create_string_buffer(256)
dll.get_loader_name(buf, 256)
print "loader:", buf.raw

ctypes offers many means to interface with C code, so you can use it to call almost any IDA API.

Precompiled PySide binaries for IDA Pro

In a previous blog post we mentioned that it is possible to use IDA Pro with PySide (Python + Qt) after applying some minor code patches to PySide.

For convenience purposes, we precompiled the PySide libraries that work with IDA Pro 6.0+ and Python 2.6/2.7. Below is a brief explanation on how to install and use those binaries.

Edit: 2012-06-29 updated links for IDA 6.3/Python 2.7

Continue reading Precompiled PySide binaries for IDA Pro

VirusTotal plugin for IDA Pro

In this blog post, we are going to illustrate how to use some of the new UI features introduced in IDA Pro 6.1 (embedded choosers, custom icons, etc…) by writing a VirusTotal reporting and file submission plugin for IDA Pro. The plugin will allow you to get reports from VirusTotal based on the input file MD5 or a file of your choice. The plugin will offer to upload the file if the file was not analyzed before.

vt_ui_dlg

Continue reading VirusTotal plugin for IDA Pro