This is a guest entry written by Elias Bachaalany. 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 him.
ida-qscripts or QScripts is a productivity plugin for better/faster scripting and coding workflow/experience for IDA.
IDA provides two methods to script: script files and script snippets.
- Script snippets (Shift–F2) is found under the “File/Script Command…” menu. One writes a snippets and presses Alt–R to run the script. If the focus is shifted away from that window, but that window is still open, you can press Ctrl–Shift–X” to execute the last focused snippet.
This workflow is fast because you just press a hotkey and run your code. - Script files: The other way to script for IDA is to write script files and run them in IDA. This workflow is not ideal for quick prototyping because one must edit the script, save it, switch to IDA’s “Recent Scripts” window (Alt–F7) and double-click again on that script to re-run it.
Requiring the user to switch back and forth between the IDE and IDA each time to test the new script is not fun and that’s where ida-qscripts comes into play.
For native programming (writing plugins using the C++ SDK), the development workflow is equally slow. You have to build your plugin, deploy it in the plugins folder, and often times restart IDA for your native plugin’s new version to be loaded.
With QScripts, all you need to do is activate a script and it will be monitored for changes. When the script (its dependencies or trigger file) is modified, then qscripts will automatically re-run the script for you.
QScripts features and options
The following are some of the important features and options in QScripts:
- Allow QScripts execution to be undo-able: this option allows you to undo the script’s side effects by pressing Ctrl–Z in IDA. This option is not enabled by default. Use this option if your plugin or script modifies the database and there’s possibility of incorrectly modifying or patching the IDB.
- Script monitor interval: this adjusts the file modification time polling frequency of the scripts on disk. Unfortunately, because IDA SDK does not have file change notification APIs, we use basic timers to poll and query file modification times.
- Clear the output window: Automatically issues a
msg_clear()
call before the script is executed. - Dependencies support: QScripts understands dependencies (Python packages for example). If your main script or any of its dependencies are changed, then everything is reloaded correctly. You do not need to restart IDA to have your other Python dependencies refreshed for example.
- Custom trigger file: it is possible to specify and monitor a trigger file. This file can be anything, when it changes, your script is re-run. This mechanism is used to support native development (C++ plugins).
Installing QScripts
QScripts is an open-source project hosted at https://github.com/0xeb/ida-qscripts. Grab a build from the releases page and place it in your plugins folder. Alternatively, just build it from sources.
QScripts and script development
Install QScripts and open it with the default Alt–Shift–F9 hotkey:
If the window is empty, it means that you never ran scripts before. Just press Ins to browse a script and load it. Double-click on the script to activate it. Below, we can see hello.py activated:
Any changes to this file will cause qscripts to run it automatically. Like this, you don’t have to leave your editor. The ideal setup is to have IDA and your editor side by side:
To stop the script monitor, just select your script and press Ctrl–D:
Dependencies
If your scripts have dependencies, then you must create and configure an additional file with the same name and extension as your input script with the additional .deps.qscripts suffix.
For example, hello.py‘s dependency file should be called hello.py.deps.qscripts:
This tells qscripts that hello.py depends on myotherscript.py. If myotherscript.py also has its own myotherscript.py.deps.qscripts then hello.py will inherit its dependent’s dependencies.
QScripts and native plugins development
If you prefer to prototype in C++ using the full power of the IDA SDK, then qscripts also lets you stay in your favorite IDE and edit/build/run the plugin continuously. Check the video below for a demo.
First, let’s discuss some background information to explain how hot-reloading works in IDA.
Background
IDA plugins use a plugin descriptor structure called plugin_t
. It has the flags
field which can have the PLUGIN_UNL
flag that allows hot-reloading of the plugins in a edit/build/run fashion.
When IDA runs, the plugin’s binary (*.so, 64*.dll, *.dll
) will be loaded and held by IDA. Once you invoke the plugin the first time, its run()
will be invoked, then IDA will unload the plugin. This will be your chance to re-compile the modified plugin before re-invoking it in IDA (from the menu) or programmatically via the load_and_run_plugin()
.
Using QScripts
Now that we know how hot-reloading works in IDA, we can leverage qscripts and its trigger files feature to allow native plugins development and prototyping in a convenient manner.
To begin, create a simple Python script with the following contents:
Then also write its dependency file with a trigger file that is the actual compiled plugin binary. For instance:
Finally, activate hello_cpp.py from qscripts.
Now, everytime the compiled plugin binary changes, the Python script will ask IDA to load and run it again!
Native templates to work with
Conveniently, qscripts ships with a bunch of templates that lets you focus on the code rather than on the mechanics/plumbing code of a plugin:
- plugin_template: this template hides the plugin plumbing code in driver.cpp and exposes main.cpp where you implement your plugin logic in a simple function.
- plugin_triton: this is a template for writing a plugin that uses the Triton DBA framework.
- loader_template: this template is a mock file loader module for IDA. It has a mock
load_file()
andaccept_file()
callbacks. Implement and quickly test these methods before you transfer this code to a real file loader module.
That’s all, happy and productive coding!