Skip to content

Instantly share code, notes, and snippets.

@nstarke
Created April 13, 2026 15:40
Show Gist options
  • Select an option

  • Save nstarke/a8ef4edcc461e3c949d5884f2d666278 to your computer and use it in GitHub Desktop.

Select an option

Save nstarke/a8ef4edcc461e3c949d5884f2d666278 to your computer and use it in GitHub Desktop.
HarusHeadless - Haruspex Ghidra Script for Headless Mode
/*
* HarusHeadless.java - Extract pseudocode from Ghidra's decompiler
* Copyright (c) 2022-2026 Marco Ivaldi <raptor@0xdeadbeef.info>
*
* Headless-mode update:
* - accepts output directory via scriptArgs[0]
* - still falls back to askString() in GUI mode
*/
// This script extracts all pseudocode generated by the Ghidra decompiler
// in a format that should be suitable to be imported into an IDE, such as
// VS Code, or parsed by static analysis tools, such as Semgrep.
// @author Marco Ivaldi <raptor@0xdeadbeef.info>
// @category VulnDev
// @keybinding H
// @menupath Tools.Haruspex
// @toolbar
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.symbol.*;
import ghidra.program.model.listing.*;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileOptions;
import ghidra.app.decompiler.DecompileResults;
public class HarusHeadless extends GhidraScript
{
List<Function> functions;
DecompileOptions options;
DecompInterface decomp;
String outputPath = "/tmp/haruspex.out";
String baseOutputPath = outputPath;
static final int TIMEOUT = 60;
@Override
public void run() throws Exception
{
printf("\nHaruspex.java - Extract pseudocode from Ghidra's decompiler\n");
printf("Copyright (c) 2022-2026 Marco Ivaldi <raptor@0xdeadbeef.info>\n\n");
resolveOutputPath();
outputPath = new File(baseOutputPath, currentProgram.getName()).getAbsolutePath();
File outDir = new File(outputPath);
if (!outDir.exists() && !outDir.mkdirs()) {
printf("Could not create output directory \"%s\", exiting.\n\n", outputPath);
return;
}
if (!outDir.isDirectory()) {
printf("Output path \"%s\" is not a directory, exiting.\n\n", outputPath);
return;
}
functions = new ArrayList<>();
getAllFunctions();
decomp = new DecompInterface();
options = new DecompileOptions();
decomp.setOptions(options);
decomp.toggleCCode(true);
decomp.toggleSyntaxTree(true);
decomp.setSimplificationStyle("decompile");
if (!decomp.openProgram(currentProgram)) {
printf("Could not initialize the decompiler, exiting.\n\n");
return;
}
printf("Saving pseudocode to: %s\n", outputPath);
printf("Extracting pseudocode from %d functions...\n\n", functions.size());
functions.forEach(f -> extractPseudoCode(f));
}
private void resolveOutputPath()
{
String[] args = getScriptArgs();
if (args != null && args.length > 0 && args[0] != null && !args[0].trim().isEmpty()) {
baseOutputPath = args[0].trim();
printf("Using base output directory from scriptArgs: \"%s\"\n", baseOutputPath);
return;
}
try {
baseOutputPath = askString("Output directory path", "Enter the base path of the output directory:");
}
catch (Exception e) {
printf("Output directory not supplied, using default base path \"%s\".\n", baseOutputPath);
}
}
// collect all Function objects into a global ArrayList
public void getAllFunctions()
{
SymbolTable st = currentProgram.getSymbolTable();
SymbolIterator si = st.getSymbolIterator();
while (si.hasNext()) {
Symbol s = si.next();
if (s.getSymbolType() == SymbolType.FUNCTION && !s.isExternal()) {
Function fun = getFunctionAt(s.getAddress());
if (fun != null && !fun.isThunk()) {
functions.add(fun);
}
}
}
}
// extract the pseudocode of a function
public void extractPseudoCode(Function func)
{
DecompileResults res = decomp.decompileFunction(func, TIMEOUT, monitor);
if (res.getDecompiledFunction() != null) {
String fileName = func.getName() + "@" + func.getEntryPoint() + ".c";
saveToFile(outputPath, fileName, res.getDecompiledFunction().getC());
}
else {
printf("Can't decompile %s\n\n", func.getName());
}
}
// save results to file
public void saveToFile(String path, String name, String output)
{
File outFile = new File(path, name);
try (FileWriter fw = new FileWriter(outFile);
PrintWriter pw = new PrintWriter(fw)) {
pw.write(output);
}
catch (Exception e) {
printf("Cannot write to output file \"%s\".\n\n", outFile.getAbsolutePath());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment