changed everything to use pyGhidra

This commit is contained in:
Vivian M
2025-09-18 16:00:39 -04:00
parent b657cf2782
commit bbc19924e3
21 changed files with 452 additions and 351 deletions

View File

@@ -30,11 +30,13 @@ import java.io.FileNotFoundException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScriptLoadException;
import ghidra.app.services.AbstractAnalyzer;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.services.AnalyzerType;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.Application;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
@@ -104,7 +106,7 @@ public class DeepCutAnalyzer extends AbstractAnalyzer {
// 2) Run DeepCut via the launcher (blocking, file-IO args under the hood)
String cutsJson="";
try {
cutsJson = DeepCutLauncher.runFileMode(program, set, inputJson, monitor);
cutsJson = DeepCutLauncher.execute(program, set, inputJson, monitor);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -158,4 +160,7 @@ public class DeepCutAnalyzer extends AbstractAnalyzer {
}
function.setParentNamespace(ns);
}
}
}

View File

@@ -1,176 +1,47 @@
/* ###
* © 2022 The Johns Hopkins University Applied Physics Laboratory LLC
* (JHU/APL).
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL
* MAKES NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF
* THE MATERIALS, INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL
* VIABILITY, AND DISCLAIMS ALL WARRANTIES IN THE MATERIAL, WHETHER
* EXPRESS OR IMPLIED, INCLUDING (BUT NOT LIMITED TO) ANY AND ALL IMPLIED
* WARRANTIES OF PERFORMANCE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, AND NON-INFRINGEMENT OF INTELLECTUAL PROPERTY OR OTHER THIRD
* PARTY RIGHTS. ANY USER OF THE MATERIAL ASSUMES THE ENTIRE RISK AND
* LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL JHU/APL BE LIABLE
* TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT, CONSEQUENTIAL,
* SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO USE,
* THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST
* PROFITS.
*
* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*
* HAVE A NICE DAY.
*/
package deepcut;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.script.GhidraScript;
import ghidra.app.script.GhidraScriptLoadException;
import ghidra.app.script.GhidraScriptProvider;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.script.GhidraState;
import ghidra.app.services.ConsoleService;
import ghidra.framework.Application;
import ghidra.framework.model.Project;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class DeepCutLauncher {
public class DeepCutLauncher extends PyGhidraFileLauncher {
/**
* Runs DeepCutRun.py synchronously with three args: infile, outfile, modelfile.
* Writes the provided JSON string to a temp infile, runs the script, reads the temp outfile,
* and returns its contents. Best-effort temp cleanup.
* @throws IllegalAccessException
* @throws FileNotFoundException
*/
public static String runFileMode(Program program,
AddressSetView set,
String inputJson,
TaskMonitor monitor) throws IllegalAccessException, FileNotFoundException, GhidraScriptLoadException {
final String scriptName = "DeepCutRun.py"; // must be on Ghidra script path
@Override protected String getScriptName() { return "DeepCutRun.py"; }
@Override protected String getInfilePrefix() { return "deepcut_in"; }
@Override protected String getOutfilePrefix() { return "deepcut_out"; }
@Override protected String getInfileSuffix() { return ".json"; }
@Override protected String getOutfileSuffix() { return ".json"; }
AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(program);
PluginTool tool = aam.getAnalysisTool();
Project project = tool != null ? tool.getProject() : null;
GhidraState state = new GhidraState(
tool,
project,
program,
new ProgramLocation(program, set.getMinAddress()),
new ProgramSelection(set),
null
);
PrintWriter out = getOut(tool);
PrintWriter err = getErr(tool);
Path inFile = null, outFile = null;
// 1) Locate script
ResourceFile sourceFile = GhidraScriptUtil.findScriptByName(scriptName);
if (sourceFile == null) {
throw new IllegalAccessException("Couldn't find script: " + scriptName);
}
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(sourceFile);
if (provider == null) {
throw new IllegalAccessException("Couldn't find script provider for: " + scriptName);
}
ResourceFile pythonFile = Application.getModuleDataFile("model_weights.p");
if (pythonFile == null) {
throw new IllegalAccessException("Couldn't find weights file for: " + scriptName);
}
Msg.info(DeepCutLauncher.class, "Chosen provider: " + provider.getClass().getName() + " (runtime=" + provider.getRuntimeEnvironmentName() + ")");
GhidraScript script;
try {
script = provider.getScriptInstance(sourceFile, err);
}
catch(GhidraScriptLoadException e) {
Msg.showError(
null, // parent object (your plugin/analyzer/launcher)
null, // parent component (null = center on tool)
"DeepCut Failed to Launch", // dialog title
"DeepCut requires the PyGhidra extension.\n\nDetails: " + e.getMessage()
);
throw new GhidraScriptLoadException("DeepCut requires PyGhidra", e);
}
try {
// 2) Prep temp files
inFile = Files.createTempFile("deepcut_in", ".json");
outFile = Files.createTempFile("deepcut_out", ".json");
// ensure delete on JVM exit as a fallback
inFile.toFile().deleteOnExit();
outFile.toFile().deleteOnExit();
// write input JSON
Files.writeString(inFile, (inputJson == null ? "" : inputJson) + "\n", StandardCharsets.UTF_8);
// 3) Pass args: [infile, outfile, modelfile]
script.setScriptArgs(new String[] {
inFile.toString(),
outFile.toString(),
pythonFile.toString() });
// 4) Run blocking
script.execute(state, monitor, out);
// 5) Read result JSON
String result = Files.readString(outFile, StandardCharsets.UTF_8).trim();
return result;
@Override
protected String[] getExtraArgs(ghidra.framework.plugintool.PluginTool tool) throws Exception {
// derive the correct module name at runtime
String moduleName = Application.getMyModuleRootDirectory().getName();
// Locate model weights packaged with the extension/module
ResourceFile weights = Application.getModuleDataFile(moduleName, "model_weights.p");
if (weights == null) {
throw new IllegalAccessException("Couldn't find weights file for: " + getScriptName());
}
catch (CancelledException e) {
// user cancelled; return null or empty to signal no result
return null;
}
catch (Exception e) {
Msg.warn(DeepCutLauncher.class, "Error running script " + scriptName + ": " + e.getMessage(), e);
return null;
}
finally {
// best-effort cleanup
try { if (inFile != null) Files.deleteIfExists(inFile); } catch (IOException ignored) {}
try { if (outFile != null) Files.deleteIfExists(outFile); } catch (IOException ignored) {}
}
}
private static PrintWriter getOut(PluginTool tool) {
if (tool != null) {
ConsoleService console = tool.getService(ConsoleService.class);
if (console != null) return console.getStdOut();
}
return new PrintWriter(System.out);
return new String[] { weights.toString() };
}
private static PrintWriter getErr(PluginTool tool) {
if (tool != null) {
ConsoleService console = tool.getService(ConsoleService.class);
if (console != null) return console.getStdErr();
}
return new PrintWriter(System.err);
// Convenience static method to preserve your old call-site signature
public static String execute(Program program,
AddressSetView set,
String inputPayload,
TaskMonitor monitor)
throws IllegalAccessException, FileNotFoundException, GhidraScriptLoadException {
return new DeepCutLauncher().runFileMode(program, set, inputPayload, monitor);
}
@Override protected String getLaunchFailDialogTitle() { return "DeepCut Failed to Launch"; }
@Override protected String getLaunchFailDialogBodyPrefix() {
return "DeepCut requires the PyGhidra extension.\n\nDetails: ";
}
}

View File

@@ -0,0 +1,174 @@
package deepcut;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.script.GhidraScript;
import ghidra.app.script.GhidraScriptLoadException;
import ghidra.app.script.GhidraScriptProvider;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.script.GhidraState;
import ghidra.app.services.ConsoleService;
import ghidra.framework.model.Project;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Base class for launching PyGhidra scripts in "file mode":
* - writes input payload to a temp infile
* - runs a named script with args [infile, outfile, ...extraArgs]
* - reads output payload from the outfile
*
* Subclasses override:
* - script name
* - temp file prefixes/suffixes (optional)
* - extra args (optional)
* - UI strings for error dialogs (optional)
*/
public abstract class PyGhidraFileLauncher {
/** Provide the script file name that must be on the Ghidra script path. */
protected abstract String getScriptName();
/** Prefix for the temp input file (default: "in_"). */
protected String getInfilePrefix() { return "in_"; }
/** Suffix/extension for the temp input file (default: ".json"). */
protected String getInfileSuffix() { return ".json"; }
/** Prefix for the temp output file (default: "out_"). */
protected String getOutfilePrefix() { return "out_"; }
/** Suffix/extension for the temp output file (default: ".json"). */
protected String getOutfileSuffix() { return ".json"; }
/**
* Return any extra script args after [infile, outfile]. Default: none.
* If this is used, the subclass must be defined in its own .java file
* in order for Ghidra the correctly find the module data file
* (yes this is strange)
*/
protected String[] getExtraArgs(PluginTool tool) throws Exception { return new String[0]; }
/** Title and message for the “PyGhidra required” dialog. */
protected String getLaunchFailDialogTitle() { return "Script Failed to Launch"; }
protected String getLaunchFailDialogBodyPrefix() { return "This feature requires the PyGhidra extension.\n\nDetails: "; }
/**
* Run the script synchronously and return the outfile contents (trimmed), or null on cancel/error.
*/
public String runFileMode(Program program,
AddressSetView set,
String inputPayload,
TaskMonitor monitor)
throws IllegalAccessException, FileNotFoundException, GhidraScriptLoadException {
final String scriptName = getScriptName();
AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(program);
PluginTool tool = aam.getAnalysisTool();
Project project = tool != null ? tool.getProject() : null;
GhidraState state = new GhidraState(
tool,
project,
program,
new ProgramLocation(program, set.getMinAddress()),
new ProgramSelection(set),
null
);
PrintWriter out = getOut(tool);
PrintWriter err = getErr(tool);
Path inFile = null, outFile = null;
// 1) Locate script
ResourceFile sourceFile = GhidraScriptUtil.findScriptByName(scriptName);
if (sourceFile == null) {
throw new IllegalAccessException("Couldn't find script: " + scriptName);
}
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(sourceFile);
if (provider == null) {
throw new IllegalAccessException("Couldn't find script provider for: " + scriptName);
}
Msg.info(getClass(), "Chosen provider: " + provider.getClass().getName() +
" (runtime=" + provider.getRuntimeEnvironmentName() + ")");
GhidraScript script;
try {
script = provider.getScriptInstance(sourceFile, err);
} catch (GhidraScriptLoadException e) {
ghidra.util.Msg.showError(
null, null,
getLaunchFailDialogTitle(),
getLaunchFailDialogBodyPrefix() + e.getMessage()
);
throw e;
}
try {
// 2) Prep temp files
inFile = Files.createTempFile(getInfilePrefix(), getInfileSuffix());
outFile = Files.createTempFile(getOutfilePrefix(), getOutfileSuffix());
inFile.toFile().deleteOnExit();
outFile.toFile().deleteOnExit();
// write input payload
Files.writeString(inFile, (inputPayload == null ? "" : inputPayload) + "\n", StandardCharsets.UTF_8);
// 3) Assemble args: [infile, outfile, ...extra]
String[] extras = getExtraArgs(tool);
String[] args = new String[2 + extras.length];
args[0] = inFile.toString();
args[1] = outFile.toString();
System.arraycopy(extras, 0, args, 2, extras.length);
script.setScriptArgs(args);
// 4) Run blocking
script.execute(state, monitor, out);
// 5) Read result payload
return Files.readString(outFile, StandardCharsets.UTF_8).trim();
}
catch (CancelledException e) {
return null;
}
catch (Exception e) {
Msg.warn(getClass(), "Error running script " + scriptName + ": " + e.getMessage(), e);
return null;
}
finally {
try { if (inFile != null) Files.deleteIfExists(inFile); } catch (IOException ignored) {}
try { if (outFile != null) Files.deleteIfExists(outFile); } catch (IOException ignored) {}
}
}
// ----- console helpers -----
protected static PrintWriter getOut(PluginTool tool) {
if (tool != null) {
ConsoleService console = tool.getService(ConsoleService.class);
if (console != null) return console.getStdOut();
}
return new PrintWriter(System.out);
}
protected static PrintWriter getErr(PluginTool tool) {
if (tool != null) {
ConsoleService console = tool.getService(ConsoleService.class);
if (console != null) return console.getStdErr();
}
return new PrintWriter(System.err);
}
}