Created
February 14, 2026 17:34
-
-
Save axlan/c38f7b01e54ab9f7cdefea51cd1dc9ed to your computer and use it in GitHub Desktop.
Ghidra script to dump functions that match a certain name pattern.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Export decompiled code for all functions matching a name regex | |
| # @category Export | |
| # @author Claude | |
| # | |
| # Usage: | |
| # - Run from Ghidra Script Manager (GUI mode) | |
| # - Or headless: analyzeHeadless ... -postScript export_decompiled.py "my_regex" "/tmp/out" | |
| # | |
| # Script arguments (optional): | |
| # ARG 0: regex pattern (default: ".*" — matches everything) | |
| # ARG 1: num params to filter for (default: -1 - don't filter) | |
| # ARG 2: output path (default: <project_dir>/decompiled_export.c) | |
| import re | |
| import os | |
| from ghidra.app.decompiler import DecompInterface, DecompileOptions | |
| from ghidra.util.task import ConsoleTaskMonitor | |
| # ── Configuration ───────────────────────────────────────────────────────────── | |
| def get_args(): | |
| """ | |
| Pull arguments from script args (headless) or fall back to sensible defaults. | |
| In GUI mode you can also hard-code values here. | |
| """ | |
| args = getScriptArgs() | |
| pattern = args[0] if len(args) > 0 else ".*" | |
| num_params = int(args[1]) if len(args) > 1 else -1 | |
| default_out = os.path.join( | |
| str(currentProgram.getDomainFile().getParent().getPathname()).lstrip("/"), | |
| "decompiled_export.c" | |
| ) | |
| out_path = args[2] if len(args) > 2 else default_out | |
| return pattern, num_params, out_path | |
| # ── Decompiler setup ────────────────────────────────────────────────────────── | |
| def setup_decompiler(program): | |
| iface = DecompInterface() | |
| opts = DecompileOptions() | |
| iface.setOptions(opts) | |
| iface.openProgram(program) | |
| return iface | |
| # ── Main ────────────────────────────────────────────────────────────────────── | |
| def main(): | |
| pattern, num_params, out_path = get_args() | |
| try: | |
| regex = re.compile(pattern) | |
| except re.error as e: | |
| print("[!] Invalid regex '{}': {}".format(pattern, e)) | |
| return | |
| print("[*] Pattern : {}".format(pattern)) | |
| print("[*] Output : {}".format(out_path)) | |
| fm = currentProgram.getFunctionManager() | |
| monitor = ConsoleTaskMonitor() | |
| decomp = setup_decompiler(currentProgram) | |
| timeout_secs = 30 # per-function decompile timeout | |
| matched = 0 | |
| failed = 0 | |
| results = [] | |
| all_functions = list(fm.getFunctions(True)) # True = forward iteration | |
| total = len(all_functions) | |
| print("[*] Scanning {} functions…".format(total)) | |
| for i, func in enumerate(all_functions): | |
| name = func.getName() | |
| if not regex.search(name): | |
| continue | |
| if num_params >= 0 and num_params != func.getParameterCount(): # change to whatever you need | |
| continue | |
| matched += 1 | |
| addr = func.getEntryPoint() | |
| print(" [{}/{}] Decompiling: {} @{}".format(i + 1, total, name, addr)) | |
| result = decomp.decompileFunction(func, timeout_secs, monitor) | |
| if result and result.decompileCompleted(): | |
| code = result.getDecompiledFunction().getC() | |
| results.append( | |
| "/* ===== {} @ {} ===== */\n{}\n".format(name, addr, code) | |
| ) | |
| else: | |
| msg = result.getErrorMessage() if result else "unknown error" | |
| print(" [!] Failed: {}".format(msg)) | |
| results.append( | |
| "/* ===== {} @ {} — DECOMPILE FAILED: {} ===== */\n\n".format( | |
| name, addr, msg | |
| ) | |
| ) | |
| failed += 1 | |
| decomp.dispose() | |
| # ── Write output ────────────────────────────────────────────────────────── | |
| if not results: | |
| print("[!] No functions matched pattern '{}'".format(pattern)) | |
| return | |
| # Ensure output directory exists | |
| out_dir = os.path.dirname(out_path) | |
| if out_dir and not os.path.exists(out_dir): | |
| os.makedirs(out_dir) | |
| header = ( | |
| "/*\n" | |
| " * Decompiled export\n" | |
| " * Program : {}\n" | |
| " * Pattern : {}\n" | |
| " * Matched : {}\n" | |
| " * Failed : {}\n" | |
| " */\n\n" | |
| ).format(currentProgram.getName(), pattern, matched, failed) | |
| with open(out_path, "w") as f: | |
| f.write(header) | |
| f.write("\n".join(results)) | |
| print("\n[+] Done. {} matched, {} failed.".format(matched, failed)) | |
| print("[+] Written to: {}".format(out_path)) | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment