Hack of the day #2: Command-Line Interface helpers
The “command-line input” (CLI), situated at the bottom of IDA’s window, is a very powerful tool to quickly execute commands in the language that is currently selected.
Typically, that language will be
Python, and one can use helpers such as
idc.here() to retrieve the address of the cursor location.
However, when some debuggers such as
WinDbg are used, the CLI can be switched to one specific to the debugger being used, thereby providing a way to input commands that will be sent the debugger backend.
Alas, when one is debugging using
GDB (for example), Python-specific helpers such as
idc.here() are not available in that CLI anymore.
That means users will have to typically copy information from the listings, and then paste it into the CLI, which is very tedious in addition to being error-prone.
A first approach
An experienced IDA user recently came up to us with this issue, and suggested that we implement some “variable substitution”, before the text is sent to the backend (be it a debugger, or Python)
For example, the markers:
$!would be replaced with the current address,
$[with the address of the beginning of the current selection,
$]with the address of the end of the current selection
Where the first approach falls short
We were very enthusiastic about this idea at first, but we quickly realized that this would open a can of worms, which we didn’t feel comfortable opening.
Here are some of the reasons:
- It’s unclear how things such as an address should be represented. Should it be
#XXXXXXXX, or even decimal? Depending on who will receive the text to execute, this matters
- Whatever markers (such as
$!) we support, it will never meet all the needs of all our users. It’s probably better if whatever solution we bring, doesn’t rely on a hard-coded set of substitutions.
- Should expansion take place in string literals?
All-in-all, we decided that it might get very messy, very quickly, and that this first approach of implementing expension in IDA itself, is probably not the strongest idea.
However, the idea is just too good to give up about entirely, and perhaps we can come up with something “lighter”, that could be implemented in IDA 7.2 already (and even before, in fact), and would be helpful most of the time.
A second approach
IDA ships with
PyQt5, a set of Python Qt bindings which lets us take advantage of pretty much all the features offered by Qt.
For example, it’s possible to place a “filter” on top of the CLI’s input field, that will perform the expansion, in-place.
The benefits of this are approach are:
- it will already work in existing IDA releases
- users can easily extend the set of markers that are recognized
- it’s written in Python, thus won’t require recompilation when improved
- since the expansion is performed in-place, it’s clear what is going to be sent to the backend
What follows, is a draft of how this could be done. It currently:
- only expands
$!into the current address, and
- formats addresses as
Perhaps someone will find this useful, and improve on it… (don’t hesitate to contact us at [email protected] for suggestions!)
import re from PyQt5 import QtCore, QtGui, QtWidgets import ida_kernwin dock = ida_kernwin.find_widget("Output window") if dock: py_dock = ida_kernwin.PluginForm.FormToPyQtWidget(dock) line_edit = py_dock.findChild(QtWidgets.QLineEdit) if line_edit: try: line_edit.removeEventFilter(kpf) except: pass class filter_t(QtCore.QObject): def eventFilter(self, obj, event): if event.type() == QtCore.QEvent.KeyRelease: self.expand_markers(obj) return QtCore.QObject.eventFilter(self, obj, event) def expand_markers(self, obj): text = obj.text() ea = ida_kernwin.get_screen_ea() exp_text = re.sub(r"\$!", "0x%x" % ea, text) if exp_text != text: obj.setText(exp_text) kpf = filter_t() line_edit.installEventFilter(kpf) print("All set")
Update (April 25th, 2019)
Elias Bachaalany has a follow-up blog post about this topic: http://0xeb.net/2019/04/climacros-ida-productivity-tool/