What is QScripts?

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 (ShiftF2) is found under the “File/Script Command…” menu. One writes a snippets and presses AltR to run the script. If the focus is shifted away from that window, but that window is still open, you can press CtrlShiftX” 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 (AltF7) 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:

qscripts-options

  • Allow QScripts execution to be undo-able: this option allows you to undo the script’s side effects by pressing CtrlZ 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 AltShiftF9 hotkey:

QScripts main UI

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:

Activated hello.py

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:

IDA and Sublime Text editor side by side

To stop the script monitor, just select your script and press CtrlD:

Context menu

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:

/reload import importlib; importlib.reload($basename$) myotherscript.py

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.

class plugin_t { public: int version; ///< Should be equal to #IDP_INTERFACE_VERSION int flags; ///< \ref PLUGIN_ /// \defgroup PLUGIN_ Plugin features /// Used by plugin_t::flags //@{ #define PLUGIN_MOD 0x0001 ///< Plugin changes the database. ///< IDA won’t call the plugin if ///< the processor module prohibited any changes. #define PLUGIN_DRAW 0x0002 ///< IDA should redraw everything after calling the plugin. #define PLUGIN_SEG 0x0004 ///< Plugin may be applied only if the current address belongs to a segment #define PLUGIN_UNL 0x0008 ///< Unload the plugin immediately after calling ‘run’. ///< This flag may be set anytime. ///< The kernel checks it after each call to ‘run’ ///< The main purpose of this flag is to ease ///< the debugging of new plugins.

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:

# hello_cpp.py # Give the linker time to finish flushing the binary import time time.sleep( 1) # Load your plugin and pass any arg value you want idaapi.load_and_run_plugin( ‘YOUR_NATIVE_PLUGIN_NAME_GOES_HERE’, 0)

Then also write its dependency file with a trigger file that is the actual compiled plugin binary. For instance:

; hello_cpp.py.deps.qscripts /triggerfile /keep $env:IDASDK$\bin\plugins\hello_cpp.dll

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() and accept_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!