Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
respond to button clicks in IDA Pro
import re
import collections
import idaapi
import ida_kernwin
class button_hooks_t(ida_kernwin.View_Hooks):
def __init__(self, v):
respond to "button" clicks for the given widget.
h = button_hooks_t(ida_kernwin.find_widget("IDA View-A"))
h.add_button(0x401050, "click me!", lambda ea, x, y: print("clicked!"))
renders to:
.text:00401050 push ebp ; ˂click me!˃
.text:00401051 mov ebp, esp
self.v = v
self.callbacks = collections.defaultdict(lambda: list())
def view_click(self, view, event):
# only respond to click events for the view we're targetting.
if view != self.v:
# only care about addresses that we're tracking.
ea = idaapi.get_screen_ea()
if ea not in self.callbacks:
for cb in self.callbacks[ea]:
cb(ea, event.x, event.y)
# note, these are not < and > characters.
# these are actually unicode lookalikes.
# this makes it less likely that someone accidentally types these in as comments.
CLEAR_RE = re.compile(r'˂[^˃]*˃ ')
def Button(tag, callback):
def _handle(ea, x, y):
# like:
# .text:00401050 push ebp ; <foo>
line = idaapi.tag_remove(ida_kernwin.get_curline())
# col: the index of the position within the line.
# row: the index of the row, within the current view. *not* global row.
_, col, row = ida_kernwin.get_cursor()
# everything after the cursor, until the end of button
after = line[col:].partition(button_hooks_t.BUTTON_END_SYMBOL)[0]
# everything from start of button until the cursor
before = line[:col].rpartition(button_hooks_t.BUTTON_START_SYMBOL)[2]
# this should be the tag contents (or junk)
found_tag = before + after
if tag == found_tag:
callback(ea, x, y)
return _handle
def add_button(self, ea, name, callback):
add a "button" as a comment at the given address.
when the button is clicked by the user, invoke the given callback.
the button looks like `<name>`; however, those are not < and > characters - they're unicode lookalikes.
tag = button_hooks_t.BUTTON_START_SYMBOL + name + button_hooks_t.BUTTON_END_SYMBOL
existing_cmt = idaapi.get_cmt(ea, False) or ''
if existing_cmt:
existing_cmt += ' '
if tag not in existing_cmt:
idaapi.set_cmt(ea, existing_cmt + tag, False)
self.callbacks[ea].append(button_hooks_t.Button(name, callback))
def clear_buttons(self):
remove the "buttons" from comments written into the IDB.
for ea in self.callbacks.keys():
idaapi.set_cmt(ea, button_hooks_t.CLEAR_RE.sub(idaapi.get_cmt(ea, False), ''), False)
self.callbacks = collections.defaultdict(lambda: list())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.