When to Use / When Not to Use
- Use: Repetitive analysis steps need to be solidified into reusable tools (batch annotation, decryption loops, custom format parsing, special protocol field annotation)
- Use: Need interactive UI (menus/shortcuts/panels) or resident functionality that "automatically executes on project load"
- Use: Team-internal distribution / unified analysis environment across multiple machines (upgrade [[re-ghidra]] / [[re-ida]] scripts to engineered plugins)
- Don't use: One-off analysis tasks (scripts are use-and-discard; the lightweight scripts in [[re-ghidra]] step 4 / [[re-ida]] step 4 are sufficient)
- Don't use: Just rewriting existing logic in another language (confirm the scenario first, see step 1)
Tool Preparation
Static analysis tool development can skip the sandbox; plugins run inside the analysis tool process and do not execute sample code (skip the sandbox, [[re-analyze/platform-tips]] static-first).
Ghidra —— Java Extension / Python Script
- Ghidra installation (JDK 21+, required by Ghidra 11.3+) see [[re-ghidra]] Tool Preparation
- GhidraDev Eclipse plugin (official, GitHub
NationalSecurityAgency/ghidraGhidraDev directory in the repo): requires Eclipse (Linuxapt install eclipse/ Fedoradnf install eclipse-platform/ Archpacman -S eclipse-java;macOSbrew install --cask eclipse-java; Windows official website zip extract); not required——pure Gradle can also build extensions; GhidraDev version and Eclipse version have a compatibility table, if installation fails first check the combination in official release notes - Gradle (optional, for building Java extensions): Debian/Ubuntu
apt install gradle/ Fedoradnf install gradle/ Archpacman -S gradle;macOSbrew install gradle;Windowschoco install gradle; verifygradle --version; the Gradle in distribution repositories may be too old (fails to build Ghidra extensions)——prefer the gradle wrapper bundled with the GhidraDev project (./gradlew) - Verify: Ghidra GUI Script Manager can list scripts;
File > Install Extensionscan see installed extensions
IDA —— idapython Plugin
- IDA installation see [[re-ida]] Tool Preparation (idapython is built-in, no separate installation needed)
- Plugin directory: Windows
%APPDATA%\Hex-Rays\IDA Pro\plugins\(or IDA installation directoryplugins\);Linux/macOS~/.idapro/plugins/ - Verify: after placing the plugin file into the directory, the IDA startup log shows
Loading plugin ...;File > Script commandexecuteprint(idaapi.IDA_SDK_VERSION)output SDK version
Operation Steps
Execute in order; archive sha256 + path for each plugin artifact (source code/build artifact/test samples and output), for reporting and reuse ([[re-ioc]] evidence chain requirement).
-
Scenario identification (batch script vs interactive plugin):
- One-off/simple repetition → script ([[re-ghidra]]'s GhidraScript / [[re-ida]]'s idapython script)
- Resident/needs UI/execute on load/multi-machine distribution → plugin (Ghidra Java Extension / IDA idapython plugin)
- Four criteria, hitting any one means a plugin is warranted: ① needs interactive UI (menus/shortcuts/dialogs) ② automatically executes after project load ③ team multi-machine distribution (unified version) ④ needs integration with external services/APIs
- Record decision: scenario + toolchain + maintenance owner (lay out the maintenance cost from pitfall 4 upfront)
-
Ghidra plugin skeleton (Java extension / program script):
- Python script (lightest):
@categoryheader comment + save to GhidraScripts directory, run via Script Manager (note built-in Jython 2.7, see pitfall 2 and [[re-ghidra]] pitfalls) - Java extension (engineered): GhidraDev
New > Ghidra Module ProjectSelect Extension → ImplementPluginclass (overrideinit/dispose, register the menu action ininit) → Gradlegradle buildExtensionproduces .zip → target machineFile > Install Extensionsinstall - Skeleton key points: for action registration use
ToolAction/AbstractActionto attach to the menu; in a headless environment use-postScriptfor testing ([[re-ghidra]] step 4); for the boundary between scripts and extensions see [[re-ghidra]] pitfall (script vs extension selection) - Extension structure: the build artifact .zip contains extension.properties (declaring the applicable Ghidra version range) — installing it on a version outside the range will be rejected at load time, so note the version when distributing and lock the range
- Verification: after installation trigger the action from the menu; headless
analyzeHeadless ... -postScript MyPluginTest.javaruns through and produces output;gradle buildExtensionafter producing the zip, first check the extension.properties version range before distributing
- Python script (lightest):
-
IDA plugin skeleton (idapython):
# myplugin.py —— 放到 plugins 目录,IDA 启动自动加载 import idaapi class MyPlugin(idaapi.plugin_t): flags = idaapi.PLUGIN_KEEP comment = "demo plugin" help = "" wanted_name = "MyPlugin" wanted_hotkey = "Ctrl-Shift-M" def init(self): idaapi.register_action(idaapi.action_desc_t( "my:action", "My Action", MyAct(), "Ctrl-Shift-M")) return idaapi.PLUGIN_KEEP def run(self, arg): print("hello from MyPlugin") def PLUGIN_ENTRY(): return MyPlugin()- Menu registration:
idaapi.register_action+idaapi.attach_action_to_menu("Edit/...", "my:action"); dialog:ida_kernwin.ask_str/ask_buttons(note that in IDA 9.x some APIs have moved toida_kernwin, see pitfall 1) - Headless testing: for the headless executable name and
-A -Susage see [[re-ida]] (in-scriptida_autoetc. also follow the writing style there) - Verification: startup log has
Loading plugin ... MyPlugin; hotkey-triggered action output is normal
- Menu registration:
-
Reuse with existing scripts (scripts from [[re-ghidra]] / [[re-ida]]):
- Starting materials: the batch rename/decrypt loop script from [[re-ghidra]] step 4, the batch patch/annotate script from [[re-ida]] step 4 — wrap "running it manually once" into "menu-triggered"
- Scenario examples: automatic constant table scanning (the FindCrypt idea from [[re-crypto-id]]), junk instruction removal batch processing (the D-810 idea from [[re-deobfuscate]]), custom PE/ELF format parsing ([[re-format-pe]] / [[re-format-elf]])
- Reuse discipline: externalize volatile logic in scripts (pattern strings/tables/thresholds) as config files or plugin options, don't hardcode them into the code (reduces the maintenance cost of pitfall 4)
- Run the old script once first to verify the baseline output, then wrap it into a plugin — the output before and after the plugin goes live must be identical (regression testing)
-
Testing and distribution:
- Test matrix: multiple tool versions (IDA/Ghidra, see pitfall 1), multiple architecture samples (x86/x64/ARM), signed/unsigned samples; run modifying scripts first on a copy .i64/.idb or a separate project ([[re-ida]] pitfall)
- Read-only verification first: first run a read-only statistics script to confirm expectations, then apply modifying logic ([[re-ida]] pitfall: running a modifying script directly on the original database makes it hard to roll back if it corrupts things)
- Distribution: Ghidra extension .zip (
gradle buildExtension, note the Ghidra version in the README); IDA plugin as a single .py file or packaged, noting the IDA version and Python version; distribution for commercial IDA also has to pass plugin signing (pitfall 3) - Acceptance: install from scratch in a clean environment (new project/new IDB) → trigger → output matches the development environment, only then is release complete
Cross-domain combination
- [[re-binary-core]]: this skill is an extension link in its toolchain (an optional engineering action after decompilation in step 5 of the gateway workflow)
- [[re-ghidra]]: starting point for Ghidra script/extension development (the script in workflow step 4 is the plugin material)
- [[re-ida]]: starting point for idapython plugin development (the script in workflow step 4 is the plugin material; how to write headless tests)
- [[re-deobfuscate]]: Plugin-based deobfuscation scenario (D-810 approach)
- [[re-crypto-id]]: Plugin-based constant table fingerprint scanning (FindCrypt approach)
- [[re-format-pe]] / [[re-format-elf]] / [[re-format-macho]]: Target objects of format parsing plugins
- [[re-binaryninja]]: Binary Ninja scripting API plugin (multi-tool plugin systems can cross-reference each other)
- Reference [[re-analyze/platform-tips]] static-first principle (plugin development and testing require no sandbox)
Common Pitfalls and Traps
- API version differences (broken by Ghidra/IDA upgrades): Symptom—the plugin works normally in an older tool version, but after an upgrade it fails to load/errors at runtime/the menu disappears; Cause—Ghidra 11.x has breaking API changes (such as migration of function management classes), and IDA 9.x moves some
idc/idaapifunctions into reorganized new modules (ida_bytes, etc.); Countermeasure—lock the version in the plugin documentation (state the tested Ghidra/IDA versions), have the test matrix cover old and new versions (Step 5), and after upgrading the tool run read-only scripts before changing code ([[re-ida]] pitfall) - Java environment requirements: Symptom—Ghidra extension build or run reports
UnsupportedClassVersionError, Python script syntax errors; Cause—JDK version mismatch (Ghidra 11.3+ requires JDK 21, see [[re-ghidra]]), the built-in interpreter is Jython 2.7 (Python 2 syntax, does not support f-strings/walrus operator, and cannot import pip packages); Countermeasure—java -versionconfirm 21+ andJAVA_HOMEis correct; write scripts in Python 2-compatible syntax, and when modern Python is needed use ghidra-bridge to connect to external Python 3 ([[re-ghidra]] pitfall) - Plugin signing (IDA commercial edition): Symptom—the plugin is placed in the plugins directory but commercial IDA does not load it (no mention of the plugin in the startup log, no error at all); Cause—commercial IDA performs signature verification on third-party plugins, and unsigned plugins are rejected; Countermeasure—confirm the IDA version (Freeware does not perform signature verification), use Freeware/headless mode for testing during development, and for official distribution go through Hex-Rays' official plugin signing application process; write the "signing status" into the distribution documentation
- Maintenance cost spirals out of control: Symptom—three months after release the plugin cannot keep up with upgrades/new sample forms, nobody fixes it, and it instead slows down analysis; Cause—a plugin is a long-term commitment (both API upgrades and scenario changes must be tracked), whereas a one-off script does not have this cost; Countermeasure—in Step 1 first judge whether it is worth it: simple logic/fast-changing → script; stable and highly reused → plugin; within the plugin, externalize volatile logic (pattern strings/tables/thresholds) into configuration to reduce the amount of code to change; record the version and corresponding tool version with each release
- Modifying plugins directly change the database, and damage is irreversible: Symptom—after a batch rename/patch plugin finishes, IDB annotations are scrambled and hard to undo; Cause—skipping read-only verification and not running on a copy; Countermeasure—first run a read-only statistics script to confirm expectations ([[re-ida]] pitfall), verify modification logic first on a copy .i64/.idb or an independent Ghidra project, and only apply it to the official database after acceptance passes
- Ghidra extension refused due to version range: Symptom—the extension installs into Ghidra with no error but does not appear in the list; Cause—the applicable version range declared in extension.properties does not match the current Ghidra and is rejected by the installer; Countermeasure—first unzip the zip and check the version range in extension.properties (
ghidra.versionfield), and when using GhidraDev to generate the project, create it according to the target version; state the tested Ghidra version in the distribution documentation - Headless/batch environments cannot rely on interactive dialogs: Phenomenon—
analyzeHeadlessor in IDA headless mode ([[re-ida]]), plugin behavior is abnormal/stuck; Cause—batch mode has no UI event loop, ask_* dialog-type APIs and menu-triggered logic are unavailable; Countermeasure—read interactive parameters (paths/thresholds/mode strings) from command-line arguments or a configuration file instead, determine the runtime environment inside the plugin first (whether it is a GUI session) before deciding which branch to take; headless test scripts only verify pure computation logic