Last active
September 11, 2024 16:52
-
-
Save guedou/a358df609c80d9fdc1ec4c348129005b to your computer and use it in GitHub Desktop.
Call the Ghidra decompiler from the command line
This file contains 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
// Copyright (C) 2019 Guillaume Valadon <[email protected]> | |
// 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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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