mirror of
https://github.com/JHUAPL/CodeCut.git
synced 2026-01-09 14:58:02 -05:00
changed everything to use pyGhidra
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
#@category CodeCut
|
||||
#@runtime PyGhidra
|
||||
#
|
||||
## Copyright 2022 The Johns Hopkins University Applied Physics Laboratory LLC
|
||||
## (JHU/APL). All Rights Reserved.
|
||||
#
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
'''
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
# under Contract Number N66001-20-C-4024.
|
||||
#
|
||||
|
||||
import sys
|
||||
print(sys.executable)
|
||||
|
||||
import sys
|
||||
import math
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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: ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
176
codecut-gui/src/main/java/codecutguiv2/PyGhidraFileLauncher.java
Normal file
176
codecut-gui/src/main/java/codecutguiv2/PyGhidraFileLauncher.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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: ";
|
||||
}
|
||||
}
|
||||
|
||||
174
deepcut-ghidra/src/main/java/deepcut/PyGhidraFileLauncher.java
Normal file
174
deepcut-ghidra/src/main/java/deepcut/PyGhidraFileLauncher.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user