Latest available version: IDA and decompilers v8.4.240320 see all releases
Hex-Rays logo State-of-the-art binary code analysis tools
email icon

This is a guest entry written by Robert from Interrupt Labs. His views and opinions are his own and not those of Hex-Rays. Any technical or maintenance issues regarding the code herein should be directed to the author.

When reverse engineering in IDA, I find it useful to take notes on my findings to help re-enforce my understanding of a program and make the information more searchable to me and my teammates. However, due to the constant update cycles of modern software, this knowledge base gradually drifts out of date with reality. This makes it difficult to find your way back to the source of this knowledge, as especially on large platforms such as iOS, it can be impossible to find precisely which IDB you grabbed a code snippet from.

Heimdallr is an open-source IDA plugin that enables the creation of deep links into an IDA database. This allows a reverse engineer to copy out a segment of code that includes a link back to exactly where it came from. This means that if you need to check your work later or just want to shoot it over to a teammate to get their thoughts, they can jump to exactly that location with one click.

Heimdallr is made up of two parts – the heimdallr-ida IDA plugin and the heimdallr-client Python script. The client identifies which IDA database the URL is referring to and issues a request to the heimdallr-ida plugin to jump to the requested location and view. The plugin adds the hotkeys that allow the links to be easily generated within IDA and runs a server which allows the client to change the current position and view within a database.

Heimdallr can be obtained here: https://github.com/interruptlabs/heimdallr-ida

Using Heimdallr

Heimdallr registers two hotkeys for grabbing information out of IDA. This information is put directly into your clipboard so you can save it anywhere.

  • ctrl+alt+n puts a link to the current IDA cursor into the clipboard
    This is useful for giving a teammate the location of what you’re looking at so they can jump over and give you feedback.

  • ctrl+shift+n puts the currently selected snippet disassembly or decompilation, with a link below it in the markdown format into the clipboard

    ```
    if ( *(_DWORD *)a1 !=13 )
    
    {
      v13 = (const char *)sub_1000A4254();
      sub_10021AFE8("E475: Invalid argument: %s", v13);
      return 0LL;
    }
    ``` [vim.i64:0x1001656a8]

    This is useful for taking notes where you want to reference what your analysis was based on but keep all the relevant information in line with your notes.

Getting you back

When you click a link, the system will run heimdallr-client, which takes the URL and attempts to find an IDA instance that has the database open. If no current instance matches, heimdallr-client will look through IDA's recently opened files to identify the required file. If that fails, the user can configure a common directory for heimdallr-client to search for IDBs.

This strategy was chosen over absolute file paths as it allows many users to share links between themselves, without the need to have a common file directory structure, and without compromising how quickly a link can be opened. The link format was also carefully designed to be human-readable – so that even if the reader doesn't have the plugin installed, they can easily use the information to get back to the same spot.

Once an IDA instance is found that contains the relevant database, heimdallr-client will connect to that specific IDA instance using a gRPC server spun up by the plugin for each database that is open. This allows the client to issue a request to the heimdallr-ida plugin to bring the window to the foreground and jump to the requested location and view within the database. This will bring anyone back to the exact area you were looking at, with the cursor at the top of the exported area, with one click.

Using Heimdallr in Scripts

You can also use Heimdallr in your scripts to output a markdown-based report of your results. For example:

import heimdallr_utils.plugin as heimdallr
import idautils, idc


status_str = 0x0000000100236D7A
results = []

for item in idautils.DataRefsTo(status_str):
    results.append((idc.get_func_name(item), item))

with open("output.md", "w") as fd:
    for func_name, ea in results:
        fd.write(f"""
# {func_name}

[Goto Result]({heimdallr.create_link(ea)})

""")

Outputs the file:


# sub_100166808

[Goto Result](ida://vim.i64?offset=0x10016688c&hash=5e8723f4e8d8f8bb30ca3b0da9d46317&view=disasm)

# sub_1001A4184

[Goto Result](ida://vim.i64?offset=0x1001a41f4&hash=5e8723f4e8d8f8bb30ca3b0da9d46317&view=disasm)

This simple technique makes it a lot easier to navigate through what your script has found while providing a convenient place to annotate your thoughts on each result.

Under the Hood

Resolving the URL

Heimdallr works by registering a system-wide URL handler which is invoked whenever an ida:// URL is clicked. On MacOS, we can do this through an Apple Script, and (unfortunately), on other platforms we use a minimal electron application to grab the URL events. The heimdallr-client then pulls out the filename, hash, offset, and view from the URL, and uses it to figure out which IDA instances the request refers to, and to issue a request to that instance to jump to the offset and view.

The IDA database is found using three stages:

  1. Check which IDBs are already open – it's likely, someone will already have the database open
  2. Check IDAs recently opened files – it's likely someone will have at least opened the database recently
  3. Search a user-configurable list of paths for a matching file name – a last resort that allows people to maintain independent project structures

The IDA database is identified firstly by the name, and secondly by the MD5 hash of the input file obtained from ida_nalt.retrieve_input_file_md() to avoid name collisions, where the database name is the same but the input file is different. This allows colleagues to collaborate across different databases, so long as the input file was the same, without a link being misdirected to the wrong place.

Controlling IDA over gRPC

Once we find the IDA instance with the relevant IDB (or we open one), the final step is to tell IDA what view we want to jump in, and where in it to jump to.

I played around with manually implementing a REST server using sockets, or using a platform-specific IPC mechanism like XPC, but ultimately settled on gRPC for a few reasons:

  1. It provides a bunch of resiliency for free
  2. It's cross-platform meaning I don't need to handle those edge cases
  3. It's cross-language meaning if I wanted to extend the concept to other programs – there's a clear path for expansion in the future

The gRPC element allows heimdallr-client to multiplex between many open IDA instances, allowing you to switch between multiple binaries within your notes effortlessly. The server exposes a minimal subset of the IDA API to the heimdallr-client over a locally bound random port. This is started in a background thread when an IDA instance opens, and the connection information is stored in a directory within the IDA User directory and deleted when the instance closes.

% cat ~/.config/heimdallr/rpc_endpoints/62083 

{"pid": 62083, "address": "127.0.0.1:51013", "file_name": "vim.i64", "file_hash": "5e8723f4e8d8f8bb30ca3b0da9d46317"}

This allows the heimdallr-client to statelessly identify the currently open IDA instances, and obtain the gRPC port for the instances the URL was referring to. The client will then form a gRPC request to jump to the desired offset and view.

IDA offers several APIs to automate the current position within the database. For example, the cursor position is easily controlled with this API.

ida_kernwin.jumpto(addr)

One of my goals with this project was to enable the links to refer to specifically the disassembly or decompiler view. This was a lot more difficult to handle in a fully cross-platform manner. IDA does have APIs for opening and switching to different views and tabs:

target_view = ida_kernwin.find_widget(name)
ida_kernwin.activate_widget(target_view, True)

However, this API only works if IDA is focused in the foreground, and the experience is much better if clicking the link brings IDA to the foreground. This required abusing complicated PyQt internals to work reliably:

# https://www.riverbankcomputing.com/static/Docs/PyQt5/
qtwidget = ida_kernwin.PluginForm.TWidgetToPyQtWidget(ida_kernwin.get_current_viewer())
window = qtwidget.window()

# UnMinimize
WindowMinimized =  0x00000001 # https://www.riverbankcomputing.com/static/Docs/PyQt5/api/qtcore/qt.html#WindowState
cur_state = window.windowState()
new_state = cur_state & (~WindowMinimized)
window.setWindowState(new_state)

# Switch desktop / give keyboard control
window.show() 
window.raise_() # Bring to front (MacOS)
window.activateWindow() # Bring to front (Windows)

Conclusion

While intricate, this solution gives me exactly what I wanted – the ability to get back to what I was looking at 6 months ago with one click. There are plenty of other ways to annotate this information in notes, but I found that doing so added a lot of friction to both creating my notes and using them. The "one-click" nature of this solution makes it much easier for me to record this information and ensures that my notes are specific around what they are relevant to.

Going forward, I'd love to add support for IDA Teams to remove the need to have an existing database and improve platform support. If you've got any idea on how to implement these, open a PR/Issue on our GitHub, or reach out on Twitter.

Get the Heimdallr plugin: https://github.com/interruptlabs/heimdallr-ida