Skip to content

Instantly share code, notes, and snippets.

@guedou
Last active March 3, 2022 10:39
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save guedou/a358df609c80d9fdc1ec4c348129005b to your computer and use it in GitHub Desktop.
Save guedou/a358df609c80d9fdc1ec4c348129005b to your computer and use it in GitHub Desktop.
Call the Ghidra decompiler from the command line
// Copyright (C) 2019 Guillaume Valadon <guillaume@valadon.net>
// This program is published under a GPLv2 license
/*
* Decompile a function with Ghidra
*
* analyzeHeadless . Test.gpr -import $BINARY_NAME -postScript GhidraDecompiler.java $FUNCTION_ADDRESS -deleteProject -noanalysis
*
*/
import ghidra.app.decompiler.ClangLine;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.decompiler.DecompiledFunction;
import ghidra.app.decompiler.PrettyPrinter;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.util.headless.HeadlessScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionIterator;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.pcode.HighFunction;
import java.util.ArrayList;
public class GhidraDecompiler extends HeadlessScript {
@Override
public void run() throws Exception {
// Stop after this headless script
setHeadlessContinuationOption(HeadlessContinuationOption.ABORT);
// Get the function address from the script arguments
String[] args = getScriptArgs();
println(String.format("Array length: %d", args.length)); // DEBUG
if (args.length == 0) {
System.err.println("Please specify a function address!");
System.err.println("Note: use c0ffe instead of 0xcoffee");
return;
}
int functionAddress;
try {
functionAddress = Integer.parseInt(args[0], 16);
}
catch (NumberFormatException e) {
System.err.println(String.format("Invalid hex address: %s", args[0]));
return;
}
println(String.format("Address: %x", functionAddress)); // DEBUG
DecompInterface di = new DecompInterface();
println("Simplification style: " + di.getSimplificationStyle()); // DEBUG
println("Debug enables: " + di.debugEnabled());
Function f = this.getFunction(functionAddress);
if (f == null) {
System.err.println(String.format("Function not found at 0x%x", functionAddress));
return;
}
println(String.format("Decompiling %s() at 0x%x", f.getName(), functionAddress));
println("Program: " + di.openProgram(f.getProgram())); // DEBUG
// Decompile with a 5-seconds timeout
DecompileResults dr = di.decompileFunction(f, 5, null);
println("Decompilation completed: " + dr.decompileCompleted()); // DEBUG
DecompiledFunction df = dr.getDecompiledFunction();
println(df.getC());
// Print lines prepend with addresses
PrettyPrinter pp = new PrettyPrinter(f, dr.getCCodeMarkup());
ArrayList<ClangLine> lines = pp.getLines();
for (ClangLine line : lines) {
long minAddress = Long.MAX_VALUE;
long maxAddress = 0;
for (int i = 0; i < line.getNumTokens(); i++) {
if (line.getToken(i).getMinAddress() == null) {
continue;
}
long addr = line.getToken(i).getMinAddress().getOffset();
minAddress = addr < minAddress ? addr : minAddress;
maxAddress = addr > maxAddress ? addr : maxAddress;
}
if (maxAddress == 0) {
println(String.format(" - %s", line.toString()));
} else {
println(String.format("0x%-8x 0x%-8x - %s", minAddress, maxAddress, line.toString()));
}
}
}
protected Function getFunction(int address) {
// Logic from https://github.com/cea-sec/Sibyl/blob/master/ext/ghidra/ExportFunction.java
Listing listing = currentProgram.getListing();
FunctionIterator iter = listing.getFunctions(true);
while (iter.hasNext() && !monitor.isCancelled()) {
Function f = iter.next();
if (f.isExternal()) {
continue;
}
Address entry = f.getEntryPoint();
if (entry != null && entry.getOffset() == address) {
return f;
}
}
return null;
}
}
@skoehler-soocs
Copy link

Hello,
what kind of $FUNCTION_ADDRESS does it expect?

If I try the symbol table address of the C function the post scripts fails with NULL pointer exception ...

INFO GhidraDecompiler.java> Array length: 1 (GhidraScript) INFO GhidraDecompiler.java> Address: 8820cd0 (GhidraScript) INFO GhidraDecompiler.java> Simplification style: decompile (GhidraScript) INFO GhidraDecompiler.java> Debug enables: false (GhidraScript) ERROR REPORT SCRIPT ERROR: /app/Ghidra/ghidra_10.1.2_PUBLIC/custom/scripts/GhidraDecompiler.java : null (HeadlessAnalyzer) java.lang.NullPointerException at GhidraDecompiler.getFunction(GhidraDecompiler.java:103) at GhidraDecompiler.run(GhidraDecompiler.java:60) at ghidra.app.script.GhidraScript.executeNormal(GhidraScript.java:379) at ghidra.app.script.GhidraScript.doExecute(GhidraScript.java:234) at ghidra.app.script.GhidraScript.execute(GhidraScript.java:212) at ghidra.app.util.headless.HeadlessAnalyzer.runScript(HeadlessAnalyzer.java:576) at ghidra.app.util.headless.HeadlessAnalyzer.runScriptsList(HeadlessAnalyzer.java:909) at ghidra.app.util.headless.HeadlessAnalyzer.processWithImport(HeadlessAnalyzer.java:1778) at ghidra.app.util.headless.HeadlessAnalyzer.processLocal(HeadlessAnalyzer.java:445) at ghidra.app.util.headless.AnalyzeHeadless.launch(AnalyzeHeadless.java:121) at ghidra.GhidraLauncher.launch(GhidraLauncher.java:59) at ghidra.Ghidra.main(Ghidra.java:47)

Any ideas? Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment