Heimdallr - A way to integrate IDA Pro into Obsidian Notes

by

Robert S

February

2023

tldr: Heimdallr is a IDA Plugin that enables you to open and jump to a location in a IDB using a system wide registered URL handler. You can get it here.

Overview

As a iOS Vulnerability Researcher, I spend a lot of my time in IDA Pro reverse engineering how parts of iOS work. A lot of this effort is spent annotating my IDA database so I can build a picture of how a subsystem works - but Iately I've felt like I've been losing a lot of that effort as I struggle to find my way back to things I've found before.

While previously I've relied on being able to name functions, and having comments within my IDA database to explain what I was thinking at the time, then constructing write-ups retrospectively to explain my findings, the size and scope of iOS projects makes this impossible to do. The constant march of iOS updates also makes it difficult to use an IDA database as a single source of truth, as your findings end up fragmented across multiple databases.

To try and combat that this I've started to "journal" what I've been doing each day in my Obsidian database. This acts as a second layer to properly store the information gathered from reverse engineering, and allows me to draw information in from many places such as:

  • IDA databases
  • Source code
  • Online write-ups
  • Internal write-ups

This allows me to have a single place to hold findings and background knowledge, whether it's a purely speculative research idea, or a full on bug report, with a full outline of where I've gathered all my information from. This is not only great for myself, but for my teammates as well, as I can easily share this information to give them a kick start if they are looking in similar areas.

However, while it's very easy for me to cite online resources, my work held in IDA databases is opaque and difficult to access. For a while I was just copying sections out from the disassembly/pseudocode views, but this quickly runs into a bunch of problems:  

  • Which version of iOS was I looking at here
  • Which database was this work in
  • Where in the file was I looking

This adds a lot of friction when it comes to using these notes, and while I could add some form of shorthand to remind myself where I was looking, this still doesn't fully solve the problem and adds a lot of friction on the note taking end.

To solve this problem, I created Heimdallr - a IDA plugin which acts as a bridge from my notes into an IDA database.

This is done through a ida:// Uniform Resource Identifier (URI) which links to a specific place in an IDA database. For example:
ida://example.i64?offset=0x100003ed0&hash=fea074789acc4a748d2ba0c6d82a0f8f&view=pseudo

This has several key advantages:

  • It's usable from anywhere, whether it be my Obsidian notes, a page on Confluence, or a Slack DM
  • It's specific enough to ensure you're looking at the correct place but generic enough that you can share it without worrying about it breaking
  • It's completely human readable - even if someone didn't have the plugin they would be able to figure out where to go to
  • It's a generic technique that can be expanded to other programs such as Ghidra or Binary Ninja

Design

URI Handling

The first step to building Heimdallr was figuring out how to register a URI handler on MacOS.  StackOverflow as always provided the answer - that URI handlers are registered in the Info.plist file in an Application bundle.


<key>CFBundleURLTypes</key>
<array>
<dict>
	<key>CFBundleURLName</key>
	<string>IDA URL</string>
	<key></key>
	<array>
		<string>ida</string>
	</array>
</dict>
</array>

Given Python is my language of choice I didn't want to implement this in Objective C or Apple Script - this provided a few options:

  1. Add the Info key to IDA and make a plugin to handle the URI
  2. Bundle the Python module into an Application with a tool like py2app
  3. Using AppleScript as a stub to forward requests to a Python script

I eventually went with option 3 as it seemed like the path of least resistance and opened up the possibility to apply similar stubs to Windows and Linux down the line. The stub is extremely simple:

on open location this_URL
	do shell script "/opt/homebrew/bin/heimdallr_client '" & this_URL & "'"
end open location

This can be exported as an Application, and once setup requires no maintenance.

I've yet to figure out how to create stubs for other platforms (mostly due to lack of time to test), but a quick google makes it sound pretty easy for Windows and Ubuntu. Pull Requests for alternative platforms are welcome as the underlying client should be largely platform independent.

Finding the IDB

Once a URL is clicked, the first step is to find out which IDB it refers too, and if we already have it open in an existing IDA instance. I considered using using both absolute file paths or paths relative to a root project directory, but I also wanted the links to be shareable within a team. As such, I decided to use just the database name, then resolve it using the following search pattern to get it's absolute path:

  1. Check which IDBs are already open - it's likely someone will already have it open
  2. Check IDAs recently opened files - it's likely someone will have at least opened it 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 first option is done by a series of JSON files in a standard directory that records which databases are open and what port to use to connect to them.

The second option is done by a proxy of IDAs history file. IDA stores this data in a Windows registry file across all platforms which makes it difficult to access in a straightforward manner. To get around this I simply dumped the information into a history.json file, which makes cross-platform access much simpler.

The final option is done via Python's glob function to recursively look for files matching the name of the database being looked for.

As it's possible (even likely in iOS land) that databases will have the same name, we then check the hash of the input file using a modified open-source library for reading IDBs called pyidbutil. Added is the ability to "lazy load" the sections of the file as needed to optimise the time spent decompressing the file from disk, which improved performance by approximately 4 times for large databases.

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 too.

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 extended to other programs (i.e. - Ghidra) - there's a clear path for expansion in the future

The "where in it to jump to" part is easy with a nice API to jump to addresses:

ida_kernwin.jumpto(addr)

The "what view we want to jump in" part was much more difficult. IDA does have APIs for opening and switching to tabs:

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

However, this API only works if IDA is focused an in the foreground. This leads to this mess:

# 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

All of this comes together to give me exactly what I was looking for - with one hotkey I can copy a section out of any pseudocode or disassembly view and put it into my notes, and with one click at any time I can get immediately back to where I grabbed the snippet from.  This combined with notes gives me a much more searchable way to find where I found that specific constant I'm using, or a route back to an area I'd decided to continue research on later.

We've been using this on my project for a few months now and it has also had surprising utility in scripting. For example, I've used fingerprinting techniques to find functions to investigate, and output the results as a markdown list. This allows me to quickly jump through my results, and add notes to each link about what my initial thoughts are. This makes my attack surface mapping a lot more coherent and usable.

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.

Please click on "Preferences" to confirm your cookie preferences. By default, the essential cookies are always activated. View our Cookie Policy for more information.