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

@@ -1,3 +1,6 @@
#@category CodeCut
#@runtime PyGhidra
#
## Copyright 2022 The Johns Hopkins University Applied Physics Laboratory LLC
## (JHU/APL). All Rights Reserved.
#

View File

@@ -35,11 +35,11 @@ def decompile_user_functions_in_range(
current_program.getAddressFactory().getAddress(end_address_str)
if start_address is None or end_address is None:
print 'Invalid address range specified.'
print('Invalid address range specified.')
return
if start_address >= end_address:
print 'Invalid address range: start address should be less than end address.'
print('Invalid address range: start address should be less than end address.')
return
decompiler = DecompInterface()

View File

@@ -1,3 +1,4 @@
# @category CodeCut
from ghidra.program.model.listing import Function
from ghidra.program.model.symbol import SourceType
@@ -39,6 +40,7 @@ def get_referenced_function_signatures_base(function, monitor):
return signatures
def getFunctionReferences(function, monitor):
refs = set()
instructions = \
@@ -47,9 +49,10 @@ def getFunctionReferences(function, monitor):
for instr in instructions:
flowType = instr.getFlowType()
if flowType.isCall():
target = instr.getOperandReferences(0)[0].getToAddress()
func = \
function.getProgram().getFunctionManager().getFunctionAt(target)
oprefs = instr.getOperandReferences(0)
if not oprefs: continue
target = oprefs[0].getToAddress()
func = function.getProgram().getFunctionManager().getFunctionAt(target)
if func is not None:
refs.add(func)
return refs

View File

@@ -17,7 +17,7 @@ def get_global_variables(program, start_addr, end_addr):
#set.addRange(start_addr, end_addr)
print(start_address, end_address)
print(addrset)
#for symbol in symbol_table.getAllSymbols(False):
for symbol in symbol_table.getSymbols(addrset,SymbolType.LABEL,True):
print(symbol)
@@ -27,30 +27,30 @@ def get_global_variables(program, start_addr, end_addr):
if (program.getListing().getDataAt(symbol.getAddress())):
global_vars.append(symbol)
'''
'''
def is_user_defined(var):
var_name = var.getName()
var_addr = var.getAddress()
if var_name.startswith('__') or var_name.startswith('_'):
return False
if var_name.startswith('imp_') or var_name.startswith('thunk_'):
return False
if var_name.startswith('fde_') or var_name.startswith('cie_'):
return False
if var_name.startswith('completed.0') \
or var_name.startswith('data_start'):
return False
if var_addr.toString().startswith('EXTERNAL:'):
return False
section_name = program.getMemory().getBlock(var_addr).getName()
#if section_name not in ['.data', '.bss']:
# return False
return True
'''

View File

@@ -21,6 +21,8 @@
# under Contract Number N66001-20-C-4024.
#
import sys
print(sys.executable)
import sys
import math

View File

@@ -1,4 +1,6 @@
#@category AMP-Improved
#@category CodeCut
#@runtime PyGhidra
from generate_c import generate_recompilable_c_code
import os
from ghidra.util.task import TaskMonitor

View File

@@ -50,6 +50,7 @@ import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import functioncalls.plugin.FcgProvider;
import generic.jar.ResourceFile;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
@@ -58,9 +59,12 @@ import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.app.plugin.core.symboltree.actions.*;
import ghidra.app.script.GhidraScript;
import ghidra.app.script.GhidraScriptProvider;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.script.GhidraState;
import ghidra.app.services.BlockModelService;
import ghidra.app.services.GoToService;
@@ -88,8 +92,7 @@ import ghidra.program.util.DefinedStringIterator;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.program.util.ProgramChangeRecord;
import ghidra.program.util.ProgramLocation;
import ghidra.jython.GhidraJythonInterpreter;
import ghidra.jython.JythonScript;
import ghidra.program.util.ProgramSelection;
import ghidra.util.HTMLUtilities;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
@@ -1222,7 +1225,7 @@ public class CodeCutGUIPlugin extends ProgramPlugin implements DomainObjectListe
}
}
private class CExporter extends JythonScript{
private class CExporter extends GhidraScript{
Program program = GhidraProgramUtilities.getCurrentProgram(tool);
GhidraState state = new GhidraState(tool, tool.getProject(), program, null, null, null);
String start_addr;
@@ -1234,7 +1237,6 @@ public class CodeCutGUIPlugin extends ProgramPlugin implements DomainObjectListe
this.start_addr = start;
this.end_addr = end;
this.outfile = file.getAbsolutePath();
this.state.addEnvironmentVar("ghidra.python.interpreter", GhidraJythonInterpreter.get());
this.path = this.outfile.substring(0, this.outfile.lastIndexOf("/")+1);
}
@Override
@@ -1245,7 +1247,8 @@ public class CodeCutGUIPlugin extends ProgramPlugin implements DomainObjectListe
String[] args = {start_addr, end_addr, outfile};
try {
//runScript("ghidra2dwarf.py", args);
runScript("range.py",args);
runScript("range.py", args);
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -29,13 +29,14 @@
package codecutguiv2;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import ghidra.app.script.GhidraScriptLoadException;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
@@ -180,7 +181,7 @@ public class ModNamingAnalyzer {
}
String suggestedName = ModNamingLauncher.runFileMode(currentProgram, set, allStrings, monitor);
String suggestedName = ModNamingLauncher.execute(currentProgram, set, allStrings, monitor);
//if name is "unknown" (e.g. modnaming found no repeated strings) don't bother renaming
if (suggestedName.equals("unknown")) {
@@ -245,3 +246,34 @@ public class ModNamingAnalyzer {
}
class ModNamingLauncher extends PyGhidraFileLauncher {
@Override protected String getScriptName() { return "ModNamingRun.py"; }
@Override protected String getInfilePrefix() { return "modnaming_in"; }
@Override protected String getOutfilePrefix() { return "modnaming_out"; }
@Override protected String getInfileSuffix() { return ".txt"; }
@Override protected String getOutfileSuffix() { return ".txt"; }
// No extra args needed
@Override protected String[] getExtraArgs(ghidra.framework.plugintool.PluginTool tool) { return new String[0]; }
// 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 ModNamingLauncher().runFileMode(program, set, inputPayload, monitor);
}
@Override protected String getLaunchFailDialogTitle() { return "Module Naming Failed to Launch"; }
@Override protected String getLaunchFailDialogBodyPrefix() {
return "Module Naming requires the PyGhidra extension.\n\nDetails: ";
}
}

View File

@@ -1,170 +0,0 @@
/* ###
* © 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 codecutguiv2;
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;
public class ModNamingLauncher {
/**
* Runs modnaming.py synchronously with two args: infile and outfile.
* 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 = "ModNamingRun.py"; // must be on Ghidra script path
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(ModNamingLauncher.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)
"Module Naming Failed to Launch", // dialog title
"Module Naming requires the PyGhidra extension.\n\nDetails: " + e.getMessage()
);
throw new GhidraScriptLoadException("Module Naming requires PyGhidra", e);
}
try {
// 2) Prep temp files
inFile = Files.createTempFile("modnaming_in", ".txt");
outFile = Files.createTempFile("modnaming_out", ".txt");
// 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]
script.setScriptArgs(new String[] {
inFile.toString(),
outFile.toString() });
// 4) Run blocking
script.execute(state, monitor, out);
// 5) Read result JSON
String result = Files.readString(outFile, StandardCharsets.UTF_8).trim();
return result;
}
catch (CancelledException e) {
// user cancelled; return null or empty to signal no result
return null;
}
catch (Exception e) {
Msg.warn(ModNamingLauncher.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);
}
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);
}
}

View File

@@ -0,0 +1,176 @@
package codecutguiv2;
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;
Msg.info(getClass(), "tool: " + tool.getName());
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);
}
}

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);
}
}