QScripts is productivity tool and an alternative to IDA's "Recent scripts" (Alt-F9) and "Execute Scripts" (Shift-F2) facilities. QScripts allows you to develop and run any supported scripting language (*.py; *.idc, etc.) from the comfort of your own favorite text editor as soon as you save the active script, the trigger file or any of its dependencies. QScripts also supports hot-reloading of native plugins (loaders, processor modules, and plugins) using trigger files, enabling rapid development of compiled IDA addons.
Video tutorials on the AllThingsIDA YouTube channel:
- Boost your IDA programming efficiency tenfold using the ida-qscripts productivity plugin
- Scripting concepts and productivity tips for IDAPython & IDC
- An exercise in netnodes with the snippet manager plugin
Invoke QScripts from the plugins menu, or its default hotkey Alt-Shift-F9.
When it runs, the scripts list might be empty. Just press Ins
and select a script to add, or press Del
to delete a script from the list.
QScripts shares the same scripts list as IDA's Recent Scripts
window.
To execute a script, just press ENTER
or double-click it. After running a script once, it will become the active script (shown in bold).
An active script will then be monitored for changes. If you modify the script in your favorite text editor and save it, then QScripts will execute the script for you automatically in IDA.
To deactivate the script monitor, just press Ctrl-D
or right-click and choose Deactivate script monitor
from the QScripts window. When an active script becomes inactive, it will be shown in italics.
Inside QScripts window:
Alt-Shift-F9
: Open QScripts windowENTER
or double-click: Activate and execute selected scriptShift-Enter
: Execute selected script without activating itIns
: Add a new script to the listDel
: Remove a script from the listCtrl-E
: Open options dialogCtrl-D
: Deactivate script monitor
From anywhere in IDA:
Alt-Shift-X
: Re-execute the last active script or notebook cell
Press Ctrl+E
or right-click and select Options
to configure QScripts:
- Clear message window before execution: clear the message log before re-running the script. Very handy if you to have a fresh output log each time.
- Show file name when execution: display the name of the file that is automatically executed
- Execute the unload script function: A special function, if defined in the global scope (usually by your active script), called
__quick_unload_script
will be invoked before reloading the script. This gives your script a chance to do some cleanup (for example to unregister some hotkeys) - Script monitor interval: controls the refresh rate of the script change monitor. Ideally 500ms is a good amount of time to pick up script changes.
- Allow QScripts execution to be undo-able: The executed script's side effects can be reverted with IDA's Undo.
QScripts offers a feature that allows automatic re-execution of the active script when any of its dependent scripts, undergo modifications.
To leverage the automatic dependency tracking feature, create a file named identically to your active script, appending .deps.qscripts
to its name. This file should contain paths to dependent scripts, along with any necessary reload directives.
Alternatively, you can place a .deps
file (without the .qscripts
suffix) within a .qscripts
subfolder, located alongside your active script.
Example locations for script.py
:
script.py.deps.qscripts
(same directory).qscripts/script.py.deps.qscripts
(local folder).qscripts/script.py.deps
(local folder, alternate name)
The dependency index file supports comments and directives:
# Python-style comments
; Semicolon comments
// C-style comments
/reload import importlib; import $basename$; importlib.reload($basename$);
dependency1.py
dependency2.py
For projects involving Python, QScripts can automatically reload any changed dependent Python scripts. Include a /reload
directive in your .deps.qscripts
file, followed by the appropriate Python reload syntax.
/reload import importlib; import $basename$; importlib.reload($basename$);
t2.py
# This is a comment
t3.py
The t1.py.deps.qscripts
configuration enables the following behavior:
- Script Auto-Execution: Changes to
t1.py
trigger its automatic re-execution within the IDA environment. - Dependency Reload: Modifications to the dependency index file (
t1.py.deps.qscripts
) lead to the reloading of specified dependencies, followed by the re-execution of the active script. - Dependency Script Changes: Any alteration in a dependency script file causes the active script to re-execute. If a reload directive is present, the modified dependency files are also reloaded. In our cases, if either or both of
t2.py
andt3.py
are modified,t1.py
is re-executed and the modified dependencies are reloaded as well.
Note: If a dependent script possesses its own .deps.qscripts
file, QScripts recursively integrates all linked dependencies into the active script's dependencies. However, specific directives (e.g., reload
) within these recursive dependencies are disregarded.
See also:
Directives must appear at the beginning of a line and start with /
. Arguments follow the directive name.
Executes the specified code snippet when a dependency is modified, before re-executing the main script. The code is executed in the context of the dependency's language interpreter.
/reload import importlib; import $basename$; importlib.reload($basename$);
Specifies a package base directory for Python package dependencies. This path is used in conjunction with $pkgmodname$
and $pkgparentmodname$
variables.
/pkgbase C:\projects\mypackage
Configures QScripts to execute the script when the specified trigger file is created or modified, rather than when the script itself changes. This is particularly useful for hot-reloading compiled native plugins.
Options:
/keep
: Preserve the trigger file after execution (default behavior is to delete it)
/triggerfile /keep C:\temp\build_done.flag
Enables notebook mode where a directory of scripts is treated as a collection of cells. When any file matching the cell pattern is saved, that cell is executed.
/notebook My Analysis Notebook
Specifies a regular expression pattern to identify notebook cell files. The default pattern is \d{4}.*\.py$
.
/notebook.cells_re ^\d{4}_.+\.py$
Controls the behavior when the notebook is activated:
exec_none
: Display the notebook title but do not execute any scriptsexec_main
: Execute the main script fileexec_all
: Execute all notebook cells in order
/notebook.activate exec_all
Variables are expanded when encountered in file paths or reload directives. Use the syntax $variable$
.
$basename$
: The base name (without extension) of the current dependency file$env:VariableName$
: The value of the environment variableVariableName
$pkgbase$
: The package base directory (set via/pkgbase
)$pkgmodname$
: The module name derived from the dependency file path relative to$pkgbase$
, with path separators replaced by dots (e.g.,pkg.submodule.file
)$pkgparentmodname$
: The parent module name (e.g.,pkg.submodule
)$ext$
: The platform-specific plugin extension (e.g.,64.dll
,.so
,64.dylib
)
# Reload a Python module
/reload import importlib; import $basename$; importlib.reload($basename$);
# Use environment variable in path
$env:SCRIPTS_DIR$/helper.py
# Package reloading
/pkgbase C:\myproject\src
/reload import importlib; import $pkgmodname$; importlib.reload($pkgmodname$);
src/utils/helper.py
QScripts can monitor a directory of script files as if they were notebook cells. When you save any file matching the cell pattern, that cell is executed.
Example notebook configuration for notebook.py.deps.qscripts
:
/notebook Data Analysis Notebook
/notebook.cells_re ^\d{4}_.+\.py$
/notebook.activate exec_none
/reload import importlib; import $basename$; importlib.reload($basename$);
shared_utils.py
With this configuration:
- Files like
0010_load_data.py
,0020_process.py
,0030_visualize.py
are recognized as cells - Saving any cell executes only that cell
- The notebook title is displayed when activated
- Changes to
shared_utils.py
cause the last-executed cell to re-run
See also:
Sometimes you don't want to trigger QScripts when your scripts are saved, instead you want your own trigger condition.
One way to achieve a custom trigger is by using the /triggerfile
directive:
/triggerfile createme.tmp
; Dependencies...
dep1.py
This tells QScripts to wait until the trigger file createme.tmp
is created (or modified) before executing your script. Now, any time you want to execute the active script, just create (or modify) the trigger file.
You may pass the /keep
option so QScripts does not delete your trigger file:
/triggerfile /keep dont_del_me.info
QScripts can be controlled programmatically from scripts or other plugins.
# Open QScripts window
idaapi.load_and_run_plugin("qscripts", 0)
# Execute the last selected script
idaapi.load_and_run_plugin("qscripts", 1)
# Activate the script monitor
idaapi.load_and_run_plugin("qscripts", 2)
# Deactivate the script monitor
idaapi.load_and_run_plugin("qscripts", 3)
QScripts registers the following actions that can be invoked programmatically:
qscripts:deactivatemonitor
- Deactivate the script monitorqscripts:execselscript
- Execute the selected script without activating itqscripts:execscriptwithundo
- Re-execute the last active script or notebook cellqscripts:executenotebook
- Execute all cells in the active notebook
# Re-execute the active script
idaapi.process_ui_action("qscripts:execscriptwithundo")
# Execute all notebook cells
idaapi.process_ui_action("qscripts:executenotebook")
QScripts supports hot-reloading of compiled native plugins (IDA plugins, loaders, and processor modules) using trigger files. This enables rapid iterative development of native IDA addons without restarting IDA.
The native plugin must be designed to support unloading:
- Plugins: Set the
PLUGIN_UNL
flag to allow IDA to unload the plugin after each invocation - Loaders: Loaders are naturally unloadable
- Processor modules: Use appropriate cleanup in module termination
The hot-reload workflow uses trigger files to detect when a new binary is available:
- Create a loader script that invokes your native plugin
- Configure a dependency file with
/triggerfile
pointing to the compiled binary - Build your native plugin
- QScripts detects the binary change and executes the loader script
- The loader script invokes the newly compiled plugin
For a plugin with the PLUGIN_UNL
flag (like the IDA SDK hello
sample):
Step 1: Create a loader script load_hello.py
:
# Optionally clear the screen
idaapi.msg_clear()
# Load your plugin and pass any arg value you want
idaapi.load_and_run_plugin('hello', 0)
# Optionally, do post work, etc.
# ...
Step 2: Create the dependency file load_hello.py.deps.qscripts
:
/triggerfile /keep C:\<ida_dir>\plugins\hello$ext$
Step 3: Activate load_hello.py
in QScripts (press ENTER
on it)
Step 4: Build or rebuild the plugin in your IDE
The moment the compilation succeeds, the new binary will be detected (since it is the trigger file) and your loader script will use IDA's load_and_run_plugin()
to run the plugin again.
For loaders, the workflow is similar but uses load_file()
instead:
Loader script test_loader.py
:
idaapi.msg_clear()
# Unload the old loader if needed
# (IDA handles this automatically for loaders)
# Load a test file with your loader
idaapi.load_file("C:\\testfiles\\myformat.bin", 0)
Dependency file test_loader.py.deps.qscripts
:
/triggerfile /keep C:\<ida_dir>\loaders\myloader$ext$
The $ext$
variable automatically expands to the platform-specific plugin extension:
- Windows 64-bit:
64.dll
- Windows 32-bit:
.dll
- Linux 64-bit:
64.so
- Linux 32-bit:
.so
- macOS:
64.dylib
or.dylib
This allows your dependency files to work across platforms without modification.
Please check the native addons examples in test_addons for complete working examples of hot-reloading plugins, loaders, and processor modules.
QScripts uses idacpp and is built using ida-cmake.
If you don't want to build from sources, then there are release pre-built for MS Windows.
QScripts is written in C++ with IDA's SDK and therefore it should be deployed like a regular plugin. Copy the plugin binaries to either of those locations:
<IDA_install_folder>/plugins
%APPDATA%\Hex-Rays/plugins
Since the plugin uses IDA's SDK and no other OS specific functions, the plugin should be compilable for macOS and Linux just fine. I only provide MS Windows binaries. Please check the releases page.
QScripts ships with a simple Snippet Manager plugin to allow you to manage script snippets.