Files
0xdea-ghidra-scripts/Haruspex.java
T
2022-04-17 10:22:06 +02:00

125 lines
3.9 KiB
Java

/*
* Haruspex.java - Extract Ghidra decompiler's pseudo-code
* Copyright (c) 2022 Marco Ivaldi <raptor@0xdeadbeef.info>
*
* "Et immolantem haruspex Spurinna monuit, caveret periculum, quod non
* ultra Martias Idus proferretur."
* -- Suetonius
*
* Haruspex is a simple Ghidra script to assist with reverse engineering
* and vulnerability research tasks. It extracts all pseudo-code 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.
*
* See also:
* https://github.com/0xdea/ghidra-scripts/blob/main/Rhabdomancer.java
* https://github.com/0xdea/semgrep-rules
* https://joern.io/blog/joern-supports-binary/
* https://www.s3.eurecom.fr/docs/asiaccs22_mantovani.pdf
*
* Usage:
* - Analyze your target binary and manually add/modify functions if needed
* - Copy the script into your ghidra_scripts directory
* - Open the Script Manager in Ghidra and run the script
* - You can also run it via the Tools > Haruspex menu or the shurtcut "H"
* - Enter an output path in which the pseudo-code will be saved
*
* Tested with Ghidra v10.1.1.
*/
// This script extracts all pseudo-code 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.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.DecompileResults;
public class Haruspex extends GhidraScript
{
List<Function> functions;
DecompInterface decomp;
String outputPath = "/tmp/haruspex.out";
static int TIMEOUT = 60;
@Override
public void run() throws Exception
{
printf("\nHaruspex.java - Extract Ghidra decompiler's pseudo-code\n");
printf("Copyright (c) 2022 Marco Ivaldi <raptor@0xdeadbeef.info>\n\n");
// ask for output directory path
try {
outputPath = askString("Please enter the path of the output directory", "Path:");
} catch (Exception e) {
printf("Output directory not supplied, using default \"%s\".\n", outputPath);
}
// get all functions
functions = new ArrayList<>();
getAllFunctions();
// extract pseudo-code of all functions (using default options)
decomp = new DecompInterface();
if (!decomp.openProgram(currentProgram)) {
printf("Could not initialize the decompiler, exiting.\n\n");
return;
}
printf("Extracting pseudo-code from %d functions...\n\n", functions.size());
functions.forEach(f -> extractPseudoCode(f));
}
// 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()) ) {
functions.add(getFunctionAt(s.getAddress()));
}
}
}
// extract the pseudo-code of a function
// @param func target function
public void extractPseudoCode(Function func)
{
DecompileResults res = decomp.decompileFunction(func, TIMEOUT, monitor);
saveToFile(outputPath, func.getName() + ".c", res.getDecompiledFunction().getC());
}
// save results to file
// @param path name of the output directory
// @param name name of the output file
// @param output content to save to file
public void saveToFile(String path, String name, String output)
{
try {
FileWriter fw = new FileWriter(path + "/" + name);
PrintWriter pw = new PrintWriter(fw);
pw.write(output);
pw.close();
} catch (Exception e) {
printf("Cannot write to output file \"%s\".\n\n", path + "/" + name);
return;
}
}
}