mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-30 00:54:49 +08:00
factor GhidraScriptInfoManager out of GhidraScriptUtil
- move bundlehost to ghidrascriptmgrplugin
This commit is contained in:
@@ -54,9 +54,7 @@ public class BundleHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (felix != null) {
|
disposeFramework();
|
||||||
forceStopFramework();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<ResourceFile, GhidraBundle> bp2gb = new HashMap<>();
|
HashMap<ResourceFile, GhidraBundle> bp2gb = new HashMap<>();
|
||||||
@@ -503,14 +501,16 @@ public class BundleHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void forceStopFramework() {
|
void disposeFramework() {
|
||||||
try {
|
if (felix != null) {
|
||||||
felix.stop();
|
try {
|
||||||
felix.waitForStop(5000);
|
felix.stop();
|
||||||
felix = null;
|
felix.waitForStop(5000);
|
||||||
}
|
felix = null;
|
||||||
catch (BundleException | InterruptedException e) {
|
}
|
||||||
e.printStackTrace();
|
catch (BundleException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +518,7 @@ public class BundleHost {
|
|||||||
Task t = new Task("killing OSGi framework", false, false, true, true) {
|
Task t = new Task("killing OSGi framework", false, false, true, true) {
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
forceStopFramework();
|
disposeFramework();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -599,10 +599,10 @@ public class BundleHost {
|
|||||||
|
|
||||||
List<BundleHostListener> listeners = new ArrayList<>();
|
List<BundleHostListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
void fireBundleBuilt(GhidraBundle sbi) {
|
void fireBundleBuilt(GhidraBundle gb) {
|
||||||
synchronized (listeners) {
|
synchronized (listeners) {
|
||||||
for (BundleHostListener l : listeners) {
|
for (BundleHostListener l : listeners) {
|
||||||
l.bundleBuilt(sbi);
|
l.bundleBuilt(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -140,6 +140,11 @@ public class GhidraSourceBundle extends GhidraBundle {
|
|||||||
Collectors.joining());
|
Collectors.joining());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String parseImps(ResourceFile javaSource) {
|
||||||
|
// XXX don't use @imports, use an annotation
|
||||||
|
return GhidraScriptUtil.newScriptInfo(javaSource).getImports();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update buildReqs based on \@imports tag in java files from the default package
|
* update buildReqs based on \@imports tag in java files from the default package
|
||||||
*
|
*
|
||||||
@@ -155,8 +160,7 @@ public class GhidraSourceBundle extends GhidraBundle {
|
|||||||
// this might be the earliest need for ScriptInfo, so allow construction.
|
// this might be the earliest need for ScriptInfo, so allow construction.
|
||||||
|
|
||||||
// NB: ScriptInfo will update field values if lastModified has changed since last time they were computed
|
// NB: ScriptInfo will update field values if lastModified has changed since last time they were computed
|
||||||
ScriptInfo si = GhidraScriptUtil.getScriptInfo(rf);
|
String imps = parseImps(rf);
|
||||||
String imps = si.getImports();
|
|
||||||
if (imps != null && !imps.isEmpty()) {
|
if (imps != null && !imps.isEmpty()) {
|
||||||
List<BundleRequirement> reqs;
|
List<BundleRequirement> reqs;
|
||||||
try {
|
try {
|
||||||
|
|||||||
+7
-6
@@ -33,8 +33,7 @@ import docking.actions.KeyBindingUtils;
|
|||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
import docking.widgets.table.GTable;
|
import docking.widgets.table.GTable;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.*;
|
||||||
import ghidra.app.script.ScriptInfo;
|
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
@@ -51,6 +50,7 @@ class GhidraScriptActionManager {
|
|||||||
|
|
||||||
private GhidraScriptComponentProvider provider;
|
private GhidraScriptComponentProvider provider;
|
||||||
private GhidraScriptMgrPlugin plugin;
|
private GhidraScriptMgrPlugin plugin;
|
||||||
|
private GhidraScriptInfoManager infoManager;
|
||||||
private DockingAction refreshAction;
|
private DockingAction refreshAction;
|
||||||
private DockingAction bundleStatusAction;
|
private DockingAction bundleStatusAction;
|
||||||
private DockingAction newAction;
|
private DockingAction newAction;
|
||||||
@@ -65,10 +65,11 @@ class GhidraScriptActionManager {
|
|||||||
private DockingAction helpAction;
|
private DockingAction helpAction;
|
||||||
private Map<ResourceFile, ScriptAction> actionMap = new HashMap<>();
|
private Map<ResourceFile, ScriptAction> actionMap = new HashMap<>();
|
||||||
|
|
||||||
GhidraScriptActionManager(GhidraScriptComponentProvider provider,
|
GhidraScriptActionManager(GhidraScriptComponentProvider provider, GhidraScriptMgrPlugin plugin,
|
||||||
GhidraScriptMgrPlugin plugin) {
|
GhidraScriptInfoManager infoManager) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.infoManager = infoManager;
|
||||||
createActions();
|
createActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ class GhidraScriptActionManager {
|
|||||||
void restoreScriptsThatAreInTool(SaveState saveState) {
|
void restoreScriptsThatAreInTool(SaveState saveState) {
|
||||||
String[] array = saveState.getStrings(SCRIPT_ACTIONS_KEY, new String[0]);
|
String[] array = saveState.getStrings(SCRIPT_ACTIONS_KEY, new String[0]);
|
||||||
for (String filename : array) {
|
for (String filename : array) {
|
||||||
ScriptInfo info = GhidraScriptUtil.findScriptByName(filename);
|
ScriptInfo info = infoManager.findScriptByName(filename);
|
||||||
if (info != null) { // the file may have been deleted from disk
|
if (info != null) { // the file may have been deleted from disk
|
||||||
provider.getActionManager().createAction(info.getSourceFile());
|
provider.getActionManager().createAction(info.getSourceFile());
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ class GhidraScriptActionManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ResourceFile scriptFile = action.getScript();
|
ResourceFile scriptFile = action.getScript();
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(scriptFile);
|
ScriptInfo info = infoManager.getExistingScriptInfo(scriptFile);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
Msg.showError(this, provider.getComponent(), "Bad state?",
|
Msg.showError(this, provider.getComponent(), "Bad state?",
|
||||||
"action associated with a script that has no info");
|
"action associated with a script that has no info");
|
||||||
|
|||||||
+30
-38
@@ -71,6 +71,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
private RootNode scriptRoot;
|
private RootNode scriptRoot;
|
||||||
private GTree scriptCategoryTree;
|
private GTree scriptCategoryTree;
|
||||||
private DraggableScriptTable scriptTable;
|
private DraggableScriptTable scriptTable;
|
||||||
|
private GhidraScriptInfoManager infoManager;
|
||||||
private GhidraScriptTableModel tableModel;
|
private GhidraScriptTableModel tableModel;
|
||||||
private BundleStatusComponentProvider bundleStatusComponentProvider;
|
private BundleStatusComponentProvider bundleStatusComponentProvider;
|
||||||
private TaskListener taskListener = new ScriptTaskListener();
|
private TaskListener taskListener = new ScriptTaskListener();
|
||||||
@@ -102,23 +103,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
final private RefreshingBundleHostListener refreshingBundleHostListener =
|
final private RefreshingBundleHostListener refreshingBundleHostListener =
|
||||||
new RefreshingBundleHostListener();
|
new RefreshingBundleHostListener();
|
||||||
|
|
||||||
static private int loaded = 0;
|
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
|
||||||
|
|
||||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin) {
|
|
||||||
super(plugin.getTool(), "Script Manager", plugin.getName());
|
super(plugin.getTool(), "Script Manager", plugin.getName());
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.bundleHost = bundleHost;
|
||||||
if (loaded == 0) {
|
this.infoManager = new GhidraScriptInfoManager();
|
||||||
bundleHost = new BundleHost();
|
|
||||||
GhidraScriptUtil.initialize(bundleHost);
|
|
||||||
bundleHost.addGhidraBundle(GhidraScriptUtil.getUserScriptDirectory(), true, false);
|
|
||||||
bundleHost.addGhidraBundles(GhidraScriptUtil.getSystemScriptPaths(), true, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bundleHost = GhidraScriptUtil.getBundleHost();
|
|
||||||
}
|
|
||||||
loaded += 1;
|
|
||||||
|
|
||||||
bundleStatusComponentProvider =
|
bundleStatusComponentProvider =
|
||||||
new BundleStatusComponentProvider(plugin.getTool(), plugin.getName(), bundleHost);
|
new BundleStatusComponentProvider(plugin.getTool(), plugin.getName(), bundleHost);
|
||||||
@@ -133,7 +123,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
build();
|
build();
|
||||||
|
|
||||||
plugin.getTool().addComponentProvider(this, false);
|
plugin.getTool().addComponentProvider(this, false);
|
||||||
actionManager = new GhidraScriptActionManager(this, plugin);
|
actionManager = new GhidraScriptActionManager(this, plugin, infoManager);
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +161,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
scriptCategoryTree.getSelectionModel().setSelectionMode(
|
scriptCategoryTree.getSelectionModel().setSelectionMode(
|
||||||
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||||
|
|
||||||
tableModel = new GhidraScriptTableModel(this);
|
tableModel = new GhidraScriptTableModel(this, infoManager);
|
||||||
|
|
||||||
scriptTable = new DraggableScriptTable(this, tableModel);
|
scriptTable = new DraggableScriptTable(this, tableModel);
|
||||||
scriptTable.setName("SCRIPT_TABLE");
|
scriptTable.setName("SCRIPT_TABLE");
|
||||||
@@ -292,11 +282,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
tableFilterPanel.dispose();
|
tableFilterPanel.dispose();
|
||||||
actionManager.dispose();
|
actionManager.dispose();
|
||||||
bundleStatusComponentProvider.dispose();
|
bundleStatusComponentProvider.dispose();
|
||||||
|
|
||||||
loaded -= 1;
|
|
||||||
if (loaded == 0) {
|
|
||||||
GhidraScriptUtil.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BundleHost getBundleHost() {
|
public BundleHost getBundleHost() {
|
||||||
@@ -307,6 +292,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
return actionManager;
|
return actionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GhidraScriptInfoManager getInfoManager() {
|
||||||
|
return infoManager;
|
||||||
|
}
|
||||||
|
|
||||||
Map<ResourceFile, GhidraScriptEditorComponentProvider> getEditorMap() {
|
Map<ResourceFile, GhidraScriptEditorComponentProvider> getEditorMap() {
|
||||||
return editorMap;
|
return editorMap;
|
||||||
}
|
}
|
||||||
@@ -410,6 +399,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
renameFile.delete();
|
renameFile.delete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
infoManager.removeMetadata(script);
|
||||||
|
|
||||||
if (actionManager.hasScriptAction(script)) {
|
if (actionManager.hasScriptAction(script)) {
|
||||||
KeyStroke ks = actionManager.getKeyBinding(script);
|
KeyStroke ks = actionManager.getKeyBinding(script);
|
||||||
actionManager.removeAction(script);
|
actionManager.removeAction(script);
|
||||||
@@ -417,9 +408,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
action.setKeyBindingData(new KeyBindingData(ks));
|
action.setKeyBindingData(new KeyBindingData(ks));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert !GhidraScriptUtil.containsMetadata(
|
assert !infoManager.containsMetadata(
|
||||||
renameFile) : "renamed script already has metadata";
|
renameFile) : "renamed script already has metadata";
|
||||||
GhidraScriptUtil.getScriptInfo(renameFile);
|
infoManager.getScriptInfo(renameFile);
|
||||||
|
|
||||||
tableModel.switchScript(script, renameFile);
|
tableModel.switchScript(script, renameFile);
|
||||||
setSelectedScript(renameFile);
|
setSelectedScript(renameFile);
|
||||||
@@ -496,6 +487,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
if (removeScript(script)) {
|
if (removeScript(script)) {
|
||||||
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(script);
|
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(script);
|
||||||
if (provider.deleteScript(script)) {
|
if (provider.deleteScript(script)) {
|
||||||
|
infoManager.removeMetadata(script);
|
||||||
restoreSelection(script);
|
restoreSelection(script);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -563,8 +555,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
// create the ScriptInfo object now, before the TableModelEvent handlers
|
// create the ScriptInfo object now, before the TableModelEvent handlers
|
||||||
// attempt to use it.
|
// attempt to use it.
|
||||||
assert !GhidraScriptUtil.containsMetadata(newFile) : "new source already has metadata?";
|
assert !infoManager.containsMetadata(newFile) : "new source already has metadata?";
|
||||||
GhidraScriptUtil.getScriptInfo(newFile);
|
infoManager.getScriptInfo(newFile);
|
||||||
|
|
||||||
tableModel.insertScript(newFile);
|
tableModel.insertScript(newFile);
|
||||||
|
|
||||||
@@ -710,8 +702,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
if (sb instanceof GhidraSourceBundle) {
|
if (sb instanceof GhidraSourceBundle) {
|
||||||
GhidraSourceBundle gsb = (GhidraSourceBundle) sb;
|
GhidraSourceBundle gsb = (GhidraSourceBundle) sb;
|
||||||
for (ResourceFile sf : gsb.getNewSources()) {
|
for (ResourceFile sf : gsb.getNewSources()) {
|
||||||
if (GhidraScriptUtil.containsMetadata(sf)) {
|
if (infoManager.containsMetadata(sf)) {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(sf);
|
ScriptInfo info = infoManager.getExistingScriptInfo(sf);
|
||||||
BuildFailure e = gsb.getErrors(sf);
|
BuildFailure e = gsb.getErrors(sf);
|
||||||
info.setCompileErrors(e != null);
|
info.setCompileErrors(e != null);
|
||||||
}
|
}
|
||||||
@@ -753,7 +745,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void performRefresh() {
|
private void performRefresh() {
|
||||||
GhidraScriptUtil.clearMetadata();
|
infoManager.clearMetadata();
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,7 +779,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
removeScript(file);
|
removeScript(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
GhidraScriptUtil.refreshDuplicates();
|
infoManager.refreshDuplicates();
|
||||||
refreshScriptData();
|
refreshScriptData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,7 +800,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
// new ScriptInfo objects are created on performRefresh, e.g. on startup. Other
|
// new ScriptInfo objects are created on performRefresh, e.g. on startup. Other
|
||||||
// refresh operations might have old infos.
|
// refresh operations might have old infos.
|
||||||
// assert !GhidraScriptUtil.containsMetadata(scriptFile): "info already exists for script during refresh";
|
// assert !GhidraScriptUtil.containsMetadata(scriptFile): "info already exists for script during refresh";
|
||||||
ScriptInfo info = GhidraScriptUtil.getScriptInfo(scriptFile);
|
ScriptInfo info = infoManager.getScriptInfo(scriptFile);
|
||||||
String[] categoryPath = info.getCategory();
|
String[] categoryPath = info.getCategory();
|
||||||
scriptRoot.insert(categoryPath);
|
scriptRoot.insert(categoryPath);
|
||||||
}
|
}
|
||||||
@@ -823,7 +815,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
for (ResourceFile script : scripts) {
|
for (ResourceFile script : scripts) {
|
||||||
// First get the ScriptInfo object and refresh, which will ensure any
|
// First get the ScriptInfo object and refresh, which will ensure any
|
||||||
// info data (ie: script icons) will be reloaded.
|
// info data (ie: script icons) will be reloaded.
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
info.refresh();
|
info.refresh();
|
||||||
|
|
||||||
ScriptAction scriptAction = actionManager.get(script);
|
ScriptAction scriptAction = actionManager.get(script);
|
||||||
@@ -851,7 +843,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
// note: turn String[] to List<String> to use hashing
|
// note: turn String[] to List<String> to use hashing
|
||||||
Set<List<String>> categories = new HashSet<>();
|
Set<List<String>> categories = new HashSet<>();
|
||||||
for (ScriptInfo info : GhidraScriptUtil.getScriptInfoIterable()) {
|
for (ScriptInfo info : infoManager.getScriptInfoIterable()) {
|
||||||
String[] path = info.getCategory();
|
String[] path = info.getCategory();
|
||||||
List<String> category = Arrays.asList(path);
|
List<String> category = Arrays.asList(path);
|
||||||
for (int i = 1; i <= category.size(); i++) {
|
for (int i = 1; i <= category.size(); i++) {
|
||||||
@@ -938,7 +930,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
editorMap.put(newScript, editor);
|
editorMap.put(newScript, editor);
|
||||||
editorMap.remove(oldScript);
|
editorMap.remove(oldScript);
|
||||||
// create corresponding info before inserting in table
|
// create corresponding info before inserting in table
|
||||||
GhidraScriptUtil.getScriptInfo(newScript);
|
infoManager.getScriptInfo(newScript);
|
||||||
tableModel.insertScript(newScript);
|
tableModel.insertScript(newScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,7 +945,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actionManager.removeAction(script);
|
actionManager.removeAction(script);
|
||||||
GhidraScriptUtil.removeMetadata(script);
|
infoManager.removeMetadata(script);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,7 +1004,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
// the selected script has been changed, update the description panel
|
// the selected script has been changed, update the description panel
|
||||||
updateDescriptionPanel();
|
updateDescriptionPanel();
|
||||||
|
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
updateCategoryTree(info.getCategory());
|
updateCategoryTree(info.getCategory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1029,7 +1021,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> transform(ResourceFile script) {
|
public List<String> transform(ResourceFile script) {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
list.clear();
|
list.clear();
|
||||||
list.add(info.getName());
|
list.add(info.getName());
|
||||||
list.add(info.getDescription());
|
list.add(info.getDescription());
|
||||||
@@ -1063,7 +1055,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
String text = "Error! no script info!";
|
String text = "Error! no script info!";
|
||||||
ResourceFile script = getSelectedScript();
|
ResourceFile script = getSelectedScript();
|
||||||
if (script != null) {
|
if (script != null) {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
text = info.getToolTipText();
|
text = info.getToolTipText();
|
||||||
}
|
}
|
||||||
@@ -1200,7 +1192,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean acceptsRow(ResourceFile script) {
|
public boolean acceptsRow(ResourceFile script) {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
String[] category = getSelectedCategoryPath();
|
String[] category = getSelectedCategoryPath();
|
||||||
|
|
||||||
if (category == null) { // root node
|
if (category == null) { // root node
|
||||||
|
|||||||
+18
-1
@@ -25,6 +25,8 @@ import ghidra.app.plugin.PluginCategoryNames;
|
|||||||
import ghidra.app.plugin.ProgramPlugin;
|
import ghidra.app.plugin.ProgramPlugin;
|
||||||
import ghidra.app.plugin.core.eclipse.EclipseConnection;
|
import ghidra.app.plugin.core.eclipse.EclipseConnection;
|
||||||
import ghidra.app.plugin.core.eclipse.EclipseIntegrationOptionsPlugin;
|
import ghidra.app.plugin.core.eclipse.EclipseIntegrationOptionsPlugin;
|
||||||
|
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||||
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
import ghidra.app.script.GhidraState;
|
import ghidra.app.script.GhidraState;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
@@ -50,16 +52,31 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
|
|||||||
|
|
||||||
final private GhidraScriptComponentProvider provider;
|
final private GhidraScriptComponentProvider provider;
|
||||||
|
|
||||||
|
static private int loaded = 0;
|
||||||
|
final private BundleHost bundleHost;
|
||||||
|
|
||||||
public GhidraScriptMgrPlugin(PluginTool tool) {
|
public GhidraScriptMgrPlugin(PluginTool tool) {
|
||||||
super(tool, true, true, true);
|
super(tool, true, true, true);
|
||||||
|
if (loaded == 0) {
|
||||||
|
bundleHost = new BundleHost();
|
||||||
|
GhidraScriptUtil.initialize(bundleHost, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bundleHost = GhidraScriptUtil.getBundleHost();
|
||||||
|
}
|
||||||
|
loaded += 1;
|
||||||
|
|
||||||
provider = new GhidraScriptComponentProvider(this);
|
provider = new GhidraScriptComponentProvider(this, bundleHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispose() {
|
protected void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
provider.dispose();
|
provider.dispose();
|
||||||
|
loaded -= 1;
|
||||||
|
if (loaded == 0) {
|
||||||
|
GhidraScriptUtil.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+11
-8
@@ -23,7 +23,7 @@ import javax.swing.*;
|
|||||||
|
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptInfoManager;
|
||||||
import ghidra.app.script.ScriptInfo;
|
import ghidra.app.script.ScriptInfo;
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
@@ -44,10 +44,13 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
|
|
||||||
private GhidraScriptComponentProvider provider;
|
private GhidraScriptComponentProvider provider;
|
||||||
private List<ResourceFile> scriptList = new ArrayList<>();
|
private List<ResourceFile> scriptList = new ArrayList<>();
|
||||||
|
final private GhidraScriptInfoManager infoManager;
|
||||||
|
|
||||||
GhidraScriptTableModel(GhidraScriptComponentProvider provider) {
|
GhidraScriptTableModel(GhidraScriptComponentProvider provider,
|
||||||
|
GhidraScriptInfoManager infoManager) {
|
||||||
super(provider.getTool());
|
super(provider.getTool());
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
this.infoManager = infoManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,7 +108,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
scriptList.add(script);
|
scriptList.add(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fireTableRowsInserted(rowStart, rowStart + scriptFiles.size()-1);
|
fireTableRowsInserted(rowStart, rowStart + scriptFiles.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeScript(ResourceFile script) {
|
void removeScript(ResourceFile script) {
|
||||||
@@ -238,7 +241,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||||
|
|
||||||
ResourceFile file = (ResourceFile) data.getRowObject();
|
ResourceFile file = (ResourceFile) data.getRowObject();
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(file);
|
ScriptInfo info = infoManager.getExistingScriptInfo(file);
|
||||||
|
|
||||||
label.setText(null);
|
label.setText(null);
|
||||||
label.setToolTipText(null);
|
label.setToolTipText(null);
|
||||||
@@ -279,7 +282,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
@Override
|
@Override
|
||||||
public ImageIcon getValue(ResourceFile rowObject, Settings settings, Object data,
|
public ImageIcon getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||||
ServiceProvider sp) throws IllegalArgumentException {
|
ServiceProvider sp) throws IllegalArgumentException {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||||
if (info.isCompileErrors() || info.isDuplicate()) {
|
if (info.isCompileErrors() || info.isDuplicate()) {
|
||||||
return ERROR_IMG;
|
return ERROR_IMG;
|
||||||
}
|
}
|
||||||
@@ -329,7 +332,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
@Override
|
@Override
|
||||||
public String getValue(ResourceFile rowObject, Settings settings, Object data,
|
public String getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||||
ServiceProvider sp) throws IllegalArgumentException {
|
ServiceProvider sp) throws IllegalArgumentException {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||||
return info.getDescription();
|
return info.getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +404,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
@Override
|
@Override
|
||||||
public KeyBindingsInfo getValue(ResourceFile rowObject, Settings settings, Object data,
|
public KeyBindingsInfo getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||||
ServiceProvider sp) throws IllegalArgumentException {
|
ServiceProvider sp) throws IllegalArgumentException {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||||
KeyStroke actionKeyStroke = provider.getActionManager().getKeyBinding(rowObject);
|
KeyStroke actionKeyStroke = provider.getActionManager().getKeyBinding(rowObject);
|
||||||
boolean isActionBinding = false;
|
boolean isActionBinding = false;
|
||||||
KeyStroke keyBinding = info.getKeyBinding();
|
KeyStroke keyBinding = info.getKeyBinding();
|
||||||
@@ -459,7 +462,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
@Override
|
@Override
|
||||||
public String getValue(ResourceFile rowObject, Settings settings, Object data,
|
public String getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||||
ServiceProvider sp) throws IllegalArgumentException {
|
ServiceProvider sp) throws IllegalArgumentException {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||||
return getCategoryString(info);
|
return getCategoryString(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import ghidra.app.script.*;
|
|||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
public class SaveDialog extends DialogComponentProvider implements ListSelectionListener {
|
public class SaveDialog extends DialogComponentProvider implements ListSelectionListener {
|
||||||
private GhidraScriptComponentProvider componentProvider;
|
protected GhidraScriptComponentProvider componentProvider;
|
||||||
private GhidraScriptProvider provider;
|
private GhidraScriptProvider provider;
|
||||||
|
|
||||||
private List<ResourceFile> paths;
|
private List<ResourceFile> paths;
|
||||||
@@ -49,7 +49,8 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
|
|||||||
public SaveDialog(Component parent, String title,
|
public SaveDialog(Component parent, String title,
|
||||||
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
|
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
|
||||||
HelpLocation help) {
|
HelpLocation help) {
|
||||||
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(), scriptFile, help);
|
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(),
|
||||||
|
scriptFile, help);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaveDialog(Component parent, String title,
|
public SaveDialog(Component parent, String title,
|
||||||
@@ -161,7 +162,7 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String getDuplicateNameErrorMessage(String name) {
|
protected String getDuplicateNameErrorMessage(String name) {
|
||||||
ScriptInfo existingInfo = GhidraScriptUtil.getExistingScriptInfo(name);
|
ScriptInfo existingInfo = componentProvider.getInfoManager().getExistingScriptInfo(name);
|
||||||
if (existingInfo != null) {
|
if (existingInfo != null) {
|
||||||
// make sure the script has not been deleted
|
// make sure the script has not been deleted
|
||||||
ResourceFile sourceFile = existingInfo.getSourceFile();
|
ResourceFile sourceFile = existingInfo.getSourceFile();
|
||||||
|
|||||||
+4
-6
@@ -15,20 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.script;
|
package ghidra.app.plugin.core.script;
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
|
||||||
import ghidra.util.HelpLocation;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
class SaveNewScriptDialog extends SaveDialog {
|
class SaveNewScriptDialog extends SaveDialog {
|
||||||
|
|
||||||
SaveNewScriptDialog(Component parent, String title,
|
SaveNewScriptDialog(Component parent, String title,
|
||||||
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
|
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
|
||||||
HelpLocation help) {
|
HelpLocation help) {
|
||||||
super(parent, title, componentProvider, scriptFile, help);
|
super(parent, title, componentProvider, scriptFile, help);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +35,7 @@ class SaveNewScriptDialog extends SaveDialog {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String getDuplicateNameErrorMessage(String name) {
|
protected String getDuplicateNameErrorMessage(String name) {
|
||||||
if (GhidraScriptUtil.alreadyExists(name)) {
|
if (componentProvider.getInfoManager().alreadyExists(name)) {
|
||||||
return "Duplicate script name.";
|
return "Duplicate script name.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import javax.swing.KeyStroke;
|
|||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptInfoManager;
|
||||||
import ghidra.app.script.ScriptInfo;
|
import ghidra.app.script.ScriptInfo;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
@@ -30,6 +30,7 @@ class ScriptAction extends DockingAction {
|
|||||||
private static final String SCRIPT_GROUP = "_SCRIPT_GROUP_";
|
private static final String SCRIPT_GROUP = "_SCRIPT_GROUP_";
|
||||||
|
|
||||||
private GhidraScriptMgrPlugin plugin;
|
private GhidraScriptMgrPlugin plugin;
|
||||||
|
private GhidraScriptInfoManager infoManager;
|
||||||
private ResourceFile script;
|
private ResourceFile script;
|
||||||
|
|
||||||
/** Signals that the keybinding value has been set by the user from the GUI (used for persistence) */
|
/** Signals that the keybinding value has been set by the user from the GUI (used for persistence) */
|
||||||
@@ -38,6 +39,7 @@ class ScriptAction extends DockingAction {
|
|||||||
ScriptAction(GhidraScriptMgrPlugin plugin, ResourceFile script) {
|
ScriptAction(GhidraScriptMgrPlugin plugin, ResourceFile script) {
|
||||||
super(script.getName(), plugin.getName());
|
super(script.getName(), plugin.getName());
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.infoManager = plugin.getProvider().getInfoManager();
|
||||||
this.script = script;
|
this.script = script;
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
|
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
|
||||||
@@ -90,7 +92,7 @@ class ScriptAction extends DockingAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check to see if we have a fallback value
|
// check to see if we have a fallback value
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
KeyStroke metadataKeyStroke = info.getKeyBinding();
|
KeyStroke metadataKeyStroke = info.getKeyBinding();
|
||||||
if (metadataKeyStroke == null) {
|
if (metadataKeyStroke == null) {
|
||||||
// there is no fallback value; the current keybinding data is what we want
|
// there is no fallback value; the current keybinding data is what we want
|
||||||
@@ -105,7 +107,7 @@ class ScriptAction extends DockingAction {
|
|||||||
// we have a user defined keybinding if the keystroke for the action differs from
|
// we have a user defined keybinding if the keystroke for the action differs from
|
||||||
// that which is defined in the metadata of the script
|
// that which is defined in the metadata of the script
|
||||||
KeyStroke actionKeyStroke = keyBindingData.getKeyBinding();
|
KeyStroke actionKeyStroke = keyBindingData.getKeyBinding();
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
KeyStroke metadataKeyBinding = info.getKeyBinding();
|
KeyStroke metadataKeyBinding = info.getKeyBinding();
|
||||||
isUserDefinedKeyBinding = !SystemUtilities.isEqual(actionKeyStroke, metadataKeyBinding);
|
isUserDefinedKeyBinding = !SystemUtilities.isEqual(actionKeyStroke, metadataKeyBinding);
|
||||||
}
|
}
|
||||||
@@ -119,7 +121,7 @@ class ScriptAction extends DockingAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void refresh() {
|
void refresh() {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||||
KeyStroke stroke = info.getKeyBinding();
|
KeyStroke stroke = info.getKeyBinding();
|
||||||
if (!isUserDefinedKeyBinding) {
|
if (!isUserDefinedKeyBinding) {
|
||||||
setKeyBindingData(new KeyBindingData(stroke));
|
setKeyBindingData(new KeyBindingData(stroke));
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.script;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for managing script directories and ScriptInfo objects.
|
||||||
|
*/
|
||||||
|
public class GhidraScriptInfoManager {
|
||||||
|
|
||||||
|
private Map<ResourceFile, ScriptInfo> scriptFileToInfoMap = new HashMap<>();
|
||||||
|
private Map<String, List<ResourceFile>> scriptNameToFilesMap = new HashMap<>();
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
clearMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear ScriptInfo metadata cached by GhidraScriptUtil
|
||||||
|
*/
|
||||||
|
public void clearMetadata() {
|
||||||
|
scriptFileToInfoMap.clear();
|
||||||
|
scriptNameToFilesMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the ScriptInfo object for the specified file
|
||||||
|
* @param scriptFile the script file
|
||||||
|
*/
|
||||||
|
public void removeMetadata(ResourceFile scriptFile) {
|
||||||
|
scriptFileToInfoMap.remove(scriptFile);
|
||||||
|
|
||||||
|
String name = scriptFile.getName();
|
||||||
|
List<ResourceFile> files = scriptNameToFilesMap.get(name);
|
||||||
|
if (files != null) {
|
||||||
|
Iterator<ResourceFile> iter = files.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
ResourceFile rFile = iter.next();
|
||||||
|
if (scriptFile.equals(rFile)) {
|
||||||
|
iter.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
scriptNameToFilesMap.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all scripts
|
||||||
|
* @return an iterable over all script info objects
|
||||||
|
*/
|
||||||
|
public Iterable<ScriptInfo> getScriptInfoIterable() {
|
||||||
|
return () -> scriptFileToInfoMap.values().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the script info object for the specified script file,
|
||||||
|
* construct a new one if necessary.
|
||||||
|
*
|
||||||
|
* Only call this method if you expect to be creating ScriptInfo objects.
|
||||||
|
* Prefer getExistingScriptInfo instead.
|
||||||
|
*
|
||||||
|
* @param scriptFile the script file
|
||||||
|
* @return the script info object for the specified script file
|
||||||
|
*/
|
||||||
|
public ScriptInfo getScriptInfo(ResourceFile scriptFile) {
|
||||||
|
ScriptInfo info = scriptFileToInfoMap.get(scriptFile);
|
||||||
|
if (info != null) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
info = GhidraScriptUtil.newScriptInfo(scriptFile);
|
||||||
|
scriptFileToInfoMap.put(scriptFile, info);
|
||||||
|
String name = scriptFile.getName();
|
||||||
|
|
||||||
|
List<ResourceFile> matchingFiles =
|
||||||
|
scriptNameToFilesMap.computeIfAbsent(name, (n) -> new ArrayList<>());
|
||||||
|
matchingFiles.add(scriptFile);
|
||||||
|
markAnyDuplicates(matchingFiles);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a ScriptInfo object exists for
|
||||||
|
* the specified script file.
|
||||||
|
* @param scriptFile the script file
|
||||||
|
* @return true if a ScriptInfo object exists
|
||||||
|
*/
|
||||||
|
public boolean containsMetadata(ResourceFile scriptFile) {
|
||||||
|
return scriptFileToInfoMap.containsKey(scriptFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScriptInfo getExistingScriptInfo(ResourceFile script) {
|
||||||
|
ScriptInfo info = scriptFileToInfoMap.get(script);
|
||||||
|
if (info == null) {
|
||||||
|
String s = (script.exists() ? "" : "non") + "existing script" + script.toString() +
|
||||||
|
" is missing expected ScriptInfo";
|
||||||
|
System.err.println(s);
|
||||||
|
Msg.showError(GhidraScriptInfoManager.class, null, "ScriptInfo lookup", s);
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the existing script info for the given name. The script environment limits
|
||||||
|
* scripts such that names are unique. If this method returns a non-null value, then the
|
||||||
|
* name given name is taken.
|
||||||
|
*
|
||||||
|
* @param scriptName the name of the script for which to get a ScriptInfo
|
||||||
|
* @return a ScriptInfo matching the given name; null if no script by that name is known to
|
||||||
|
* the script manager
|
||||||
|
*/
|
||||||
|
public ScriptInfo getExistingScriptInfo(String scriptName) {
|
||||||
|
List<ResourceFile> matchingFiles = scriptNameToFilesMap.get(scriptName);
|
||||||
|
if (matchingFiles == null || matchingFiles.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return scriptFileToInfoMap.get(matchingFiles.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks through all of the current {@link ScriptInfo}s to see if one already exists with
|
||||||
|
* the given name.
|
||||||
|
* @param scriptName The name to check
|
||||||
|
* @return true if the name is not taken by an existing {@link ScriptInfo}.
|
||||||
|
*/
|
||||||
|
public boolean alreadyExists(String scriptName) {
|
||||||
|
return getExistingScriptInfo(scriptName) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markAnyDuplicates(List<ResourceFile> files) {
|
||||||
|
boolean isDuplicate = files.size() > 1;
|
||||||
|
files.forEach(f -> scriptFileToInfoMap.get(f).setDuplicate(isDuplicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates every known script's duplicate value.
|
||||||
|
*/
|
||||||
|
public void refreshDuplicates() {
|
||||||
|
scriptNameToFilesMap.values().forEach(files -> {
|
||||||
|
boolean isDuplicate = files.size() > 1;
|
||||||
|
files.forEach(file -> scriptFileToInfoMap.get(file).setDuplicate(isDuplicate));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given name to find a matching script. This method only works because of the
|
||||||
|
* limitation that all script names in Ghidra must be unique. If the given name has multiple
|
||||||
|
* script matches, then a warning will be logged.
|
||||||
|
*
|
||||||
|
* @param name The name for which to find a script
|
||||||
|
* @return The ScriptInfo that has the given name
|
||||||
|
*/
|
||||||
|
public ScriptInfo findScriptByName(String name) {
|
||||||
|
List<ResourceFile> matchingFiles = scriptNameToFilesMap.get(name);
|
||||||
|
if (matchingFiles != null && !matchingFiles.isEmpty()) {
|
||||||
|
ScriptInfo info = scriptFileToInfoMap.get(matchingFiles.get(0));
|
||||||
|
if (matchingFiles.size() > 1) {
|
||||||
|
Msg.warn(GhidraScriptInfoManager.class, "Found duplicate scripts for name: " +
|
||||||
|
name + ". Binding to script: " + info.getSourceFile());
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceFile file = GhidraScriptUtil.findScriptFileInPaths(
|
||||||
|
GhidraScriptUtil.getBundleHost().getBundlePaths(), name);
|
||||||
|
if (file == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getExistingScriptInfo(file); // this will cache the created info
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,19 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.script;
|
package ghidra.app.script;
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
|
||||||
import ghidra.util.classfinder.ExtensionPoint;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: ALL GhidraScriptProvider CLASSES MUST END IN "ScriptProvider". If not,
|
* NOTE: ALL GhidraScriptProvider CLASSES MUST END IN "ScriptProvider". If not,
|
||||||
* the ClassSearcher will not find them.
|
* the ClassSearcher will not find them.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class GhidraScriptProvider implements ExtensionPoint,
|
public abstract class GhidraScriptProvider
|
||||||
Comparable<GhidraScriptProvider> {
|
implements ExtensionPoint, Comparable<GhidraScriptProvider> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@@ -59,11 +59,7 @@ public abstract class GhidraScriptProvider implements ExtensionPoint,
|
|||||||
* @return true if the script was completely deleted and cleaned up
|
* @return true if the script was completely deleted and cleaned up
|
||||||
*/
|
*/
|
||||||
public boolean deleteScript(ResourceFile scriptSource) {
|
public boolean deleteScript(ResourceFile scriptSource) {
|
||||||
boolean deleted = !scriptSource.exists() || scriptSource.delete();
|
return !scriptSource.exists() || scriptSource.delete();
|
||||||
if (deleted) {
|
|
||||||
GhidraScriptUtil.removeMetadata(scriptSource);
|
|
||||||
}
|
|
||||||
return deleted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -44,17 +44,13 @@ public class GhidraScriptUtil {
|
|||||||
*/
|
*/
|
||||||
public static String USER_SCRIPTS_DIR = buildUserScriptsDirectory();
|
public static String USER_SCRIPTS_DIR = buildUserScriptsDirectory();
|
||||||
|
|
||||||
private static Map<ResourceFile, ScriptInfo> scriptFileToInfoMap = new HashMap<>();
|
|
||||||
|
|
||||||
private static Map<String, List<ResourceFile>> scriptNameToFilesMap = new HashMap<>();
|
|
||||||
|
|
||||||
static BundleHost _bundleHost;
|
static BundleHost _bundleHost;
|
||||||
|
|
||||||
public static BundleHost getBundleHost() {
|
public static BundleHost getBundleHost() {
|
||||||
return _bundleHost;
|
return _bundleHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void initialize(BundleHost bundleHost) {
|
private static void setBundleHost(BundleHost bundleHost) {
|
||||||
if (_bundleHost != null) {
|
if (_bundleHost != null) {
|
||||||
throw new RuntimeException("GhidraScriptUtil initialized multiple times!");
|
throw new RuntimeException("GhidraScriptUtil initialized multiple times!");
|
||||||
}
|
}
|
||||||
@@ -70,15 +66,14 @@ public class GhidraScriptUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initialization for headless runs.
|
* initialize state of GhidraScriptUtil with user, system paths, and optional extra system paths.
|
||||||
*
|
*
|
||||||
* @param bundleHost the host to use
|
* @param bundleHost the host to use
|
||||||
* @param extraSystemPaths additional system paths for this run, can be null
|
* @param extraSystemPaths additional system paths for this run, can be null
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static void initialize(BundleHost bundleHost, List<String> extraSystemPaths) {
|
public static void initialize(BundleHost bundleHost, List<String> extraSystemPaths) {
|
||||||
initialize(bundleHost);
|
setBundleHost(bundleHost);
|
||||||
|
|
||||||
if (extraSystemPaths != null) {
|
if (extraSystemPaths != null) {
|
||||||
for (String path : extraSystemPaths) {
|
for (String path : extraSystemPaths) {
|
||||||
bundleHost.addGhidraBundle(new ResourceFile(path), true, true);
|
bundleHost.addGhidraBundle(new ResourceFile(path), true, true);
|
||||||
@@ -90,19 +85,13 @@ public class GhidraScriptUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void dispose() {
|
public static void dispose() {
|
||||||
clearMetadata();
|
if (_bundleHost != null) {
|
||||||
_bundleHost = null;
|
_bundleHost.dispose();
|
||||||
|
_bundleHost = null;
|
||||||
|
}
|
||||||
providers = null;
|
providers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* clear ScriptInfo metadata cached by GhidraScriptUtil
|
|
||||||
*/
|
|
||||||
public static void clearMetadata() {
|
|
||||||
scriptFileToInfoMap.clear();
|
|
||||||
scriptNameToFilesMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of the current script directories.
|
* Returns a list of the current script directories.
|
||||||
* @return a list of the current script directories
|
* @return a list of the current script directories
|
||||||
@@ -112,6 +101,20 @@ public class GhidraScriptUtil {
|
|||||||
Collectors.toList());
|
Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ResourceFile getSourceDirectoryContaining(ResourceFile sourceFile) {
|
||||||
|
String sourcePath = sourceFile.getAbsolutePath();
|
||||||
|
for (ResourceFile sourceDir : getScriptSourceDirectories()) {
|
||||||
|
if (sourcePath.startsWith(sourceDir.getAbsolutePath() + File.separatorChar)) {
|
||||||
|
return sourceDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceFile findScriptByName(String scriptName) {
|
||||||
|
return findScriptFileInPaths(getScriptSourceDirectories(), scriptName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User's home scripts directory. Some tests may override the default using the
|
* User's home scripts directory. Some tests may override the default using the
|
||||||
* SystemUtilities.USER_SCRIPTS_DIR system property.
|
* SystemUtilities.USER_SCRIPTS_DIR system property.
|
||||||
@@ -224,130 +227,6 @@ public class GhidraScriptUtil {
|
|||||||
return name.substring(0, pos);
|
return name.substring(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the ScriptInfo object for the specified file
|
|
||||||
* @param scriptFile the script file
|
|
||||||
*/
|
|
||||||
public static void removeMetadata(ResourceFile scriptFile) {
|
|
||||||
scriptFileToInfoMap.remove(scriptFile);
|
|
||||||
|
|
||||||
String name = scriptFile.getName();
|
|
||||||
List<ResourceFile> files = scriptNameToFilesMap.get(name);
|
|
||||||
if (files != null) {
|
|
||||||
Iterator<ResourceFile> iter = files.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
ResourceFile rFile = iter.next();
|
|
||||||
if (scriptFile.equals(rFile)) {
|
|
||||||
iter.remove();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (files.isEmpty()) {
|
|
||||||
scriptNameToFilesMap.remove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get all scripts
|
|
||||||
* @return an iterable over all script info objects
|
|
||||||
*/
|
|
||||||
public static Iterable<ScriptInfo> getScriptInfoIterable() {
|
|
||||||
return () -> scriptFileToInfoMap.values().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the script info object for the specified script file,
|
|
||||||
* construct a new one if necessary.
|
|
||||||
*
|
|
||||||
* Only call this method if you expect to be creating ScriptInfo objects.
|
|
||||||
* Prefer getExistingScriptInfo instead.
|
|
||||||
*
|
|
||||||
* @param scriptFile the script file
|
|
||||||
* @return the script info object for the specified script file
|
|
||||||
*/
|
|
||||||
public static ScriptInfo getScriptInfo(ResourceFile scriptFile) {
|
|
||||||
ScriptInfo info = scriptFileToInfoMap.get(scriptFile);
|
|
||||||
if (info != null) {
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
GhidraScriptProvider gsp = getProvider(scriptFile);
|
|
||||||
info = new ScriptInfo(gsp, scriptFile);
|
|
||||||
scriptFileToInfoMap.put(scriptFile, info);
|
|
||||||
String name = scriptFile.getName();
|
|
||||||
|
|
||||||
List<ResourceFile> matchingFiles =
|
|
||||||
scriptNameToFilesMap.computeIfAbsent(name, (n) -> new ArrayList<>());
|
|
||||||
matchingFiles.add(scriptFile);
|
|
||||||
markAnyDuplicates(matchingFiles);
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if a ScriptInfo object exists for
|
|
||||||
* the specified script file.
|
|
||||||
* @param scriptFile the script file
|
|
||||||
* @return true if a ScriptInfo object exists
|
|
||||||
*/
|
|
||||||
public static boolean containsMetadata(ResourceFile scriptFile) {
|
|
||||||
return scriptFileToInfoMap.containsKey(scriptFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ScriptInfo getExistingScriptInfo(ResourceFile script) {
|
|
||||||
ScriptInfo info = scriptFileToInfoMap.get(script);
|
|
||||||
if (info == null) {
|
|
||||||
String s = (script.exists() ? "" : "non") + "existing script" + script.toString() +
|
|
||||||
" is missing info we thought was there";
|
|
||||||
System.err.println(s);
|
|
||||||
Msg.showError(GhidraScriptUtil.class, null, "ScriptInfo lookup", s);
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the existing script info for the given name. The script environment limits
|
|
||||||
* scripts such that names are unique. If this method returns a non-null value, then the
|
|
||||||
* name given name is taken.
|
|
||||||
*
|
|
||||||
* @param scriptName the name of the script for which to get a ScriptInfo
|
|
||||||
* @return a ScriptInfo matching the given name; null if no script by that name is known to
|
|
||||||
* the script manager
|
|
||||||
*/
|
|
||||||
public static ScriptInfo getExistingScriptInfo(String scriptName) {
|
|
||||||
List<ResourceFile> matchingFiles = scriptNameToFilesMap.get(scriptName);
|
|
||||||
if (matchingFiles == null || matchingFiles.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return scriptFileToInfoMap.get(matchingFiles.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks through all of the current {@link ScriptInfo}s to see if one already exists with
|
|
||||||
* the given name.
|
|
||||||
* @param scriptName The name to check
|
|
||||||
* @return true if the name is not taken by an existing {@link ScriptInfo}.
|
|
||||||
*/
|
|
||||||
public static boolean alreadyExists(String scriptName) {
|
|
||||||
return getExistingScriptInfo(scriptName) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void markAnyDuplicates(List<ResourceFile> files) {
|
|
||||||
boolean isDuplicate = files.size() > 1;
|
|
||||||
files.forEach(f -> scriptFileToInfoMap.get(f).setDuplicate(isDuplicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates every known script's duplicate value.
|
|
||||||
*/
|
|
||||||
public static void refreshDuplicates() {
|
|
||||||
scriptNameToFilesMap.values().forEach(files -> {
|
|
||||||
boolean isDuplicate = files.size() > 1;
|
|
||||||
files.forEach(file -> scriptFileToInfoMap.get(file).setDuplicate(isDuplicate));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all Ghidra script providers
|
* Returns a list of all Ghidra script providers
|
||||||
*
|
*
|
||||||
@@ -435,6 +314,10 @@ public class GhidraScriptUtil {
|
|||||||
return new ResourceFile(parentDirctory, className);
|
return new ResourceFile(parentDirctory, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ScriptInfo newScriptInfo(ResourceFile file) {
|
||||||
|
return new ScriptInfo(getProvider(file), file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fixup name issues, such as package parts in the name and inner class names.
|
* Fixup name issues, such as package parts in the name and inner class names.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -457,8 +340,8 @@ public class GhidraScriptUtil {
|
|||||||
return path + ".java";
|
return path + ".java";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if the given filename exists in any of the given directories */
|
static ResourceFile findScriptFileInPaths(
|
||||||
private static ResourceFile findScriptFileInPaths(Collection<ResourceFile> scriptDirectories,
|
Collection<ResourceFile> scriptDirectories,
|
||||||
String filename) {
|
String filename) {
|
||||||
|
|
||||||
String validatedName = fixupName(filename);
|
String validatedName = fixupName(filename);
|
||||||
@@ -474,33 +357,6 @@ public class GhidraScriptUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the given name to find a matching script. This method only works because of the
|
|
||||||
* limitation that all script names in Ghidra must be unique. If the given name has multiple
|
|
||||||
* script matches, then a warning will be logged.
|
|
||||||
*
|
|
||||||
* @param name The name for which to find a script
|
|
||||||
* @return The ScriptInfo that has the given name
|
|
||||||
*/
|
|
||||||
public static ScriptInfo findScriptByName(String name) {
|
|
||||||
List<ResourceFile> matchingFiles = scriptNameToFilesMap.get(name);
|
|
||||||
if (matchingFiles != null && !matchingFiles.isEmpty()) {
|
|
||||||
ScriptInfo info = scriptFileToInfoMap.get(matchingFiles.get(0));
|
|
||||||
if (matchingFiles.size() > 1) {
|
|
||||||
Msg.warn(GhidraScriptUtil.class, "Found duplicate scripts for name: " + name +
|
|
||||||
". Binding to script: " + info.getSourceFile());
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceFile file = findScriptFileInPaths(_bundleHost.getBundlePaths(), name);
|
|
||||||
if (file == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getExistingScriptInfo(file); // this will cache the created info
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only used by GhidraScriptAnalyzerAdapter */
|
/* only used by GhidraScriptAnalyzerAdapter */
|
||||||
/**
|
/**
|
||||||
* Runs the specified script with the specified state
|
* Runs the specified script with the specified state
|
||||||
@@ -537,31 +393,4 @@ public class GhidraScriptUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
private static void updateAvailableScriptFilesForDirectory(List<ResourceFile> scriptAccumulator,
|
|
||||||
ResourceFile directory) {
|
|
||||||
ResourceFile[] files = directory.listFiles();
|
|
||||||
if (files == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ResourceFile scriptFile : files) {
|
|
||||||
if (scriptFile.isFile() && hasScriptProvider(scriptFile)) {
|
|
||||||
scriptAccumulator.add(scriptFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* used only by RecipeEditorDialog
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static List<ResourceFile> getAllScripts() {
|
|
||||||
List<ResourceFile> scriptList = new ArrayList<>();
|
|
||||||
for (ResourceFile dirPath : _bundleHost.getBundlePaths()) {
|
|
||||||
updateAvailableScriptFilesForDirectory(scriptList, dirPath);
|
|
||||||
}
|
|
||||||
return scriptList;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) {
|
public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) {
|
||||||
ResourceFile sourceDir = getSourceDirectoryContaining(sourceFile);
|
ResourceFile sourceDir = GhidraScriptUtil.getSourceDirectoryContaining(sourceFile);
|
||||||
if (sourceDir == null) {
|
if (sourceDir == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -67,8 +67,6 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
@Override
|
@Override
|
||||||
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
||||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||||
// in headless operation, ScriptInfo objects can be created here
|
|
||||||
ScriptInfo info = GhidraScriptUtil.getScriptInfo(sourceFile);
|
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = loadClass(sourceFile, writer);
|
Class<?> clazz = loadClass(sourceFile, writer);
|
||||||
Object object;
|
Object object;
|
||||||
@@ -83,16 +81,13 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
String message = "Not a valid Ghidra script: " + sourceFile.getName();
|
String message = "Not a valid Ghidra script: " + sourceFile.getName();
|
||||||
writer.println(message);
|
writer.println(message);
|
||||||
Msg.error(this, message);
|
Msg.error(this, message);
|
||||||
info.setCompileErrors(true);
|
|
||||||
return null; // class is not GhidraScript
|
return null; // class is not GhidraScript
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||||
info.setCompileErrors(true);
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
info.setCompileErrors(true);
|
|
||||||
throw new ClassNotFoundException("", e);
|
throw new ClassNotFoundException("", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,16 +104,6 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
return clazz;
|
return clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceFile getSourceDirectoryContaining(ResourceFile sourceFile) {
|
|
||||||
String sourcePath = sourceFile.getAbsolutePath();
|
|
||||||
for (ResourceFile sourceDir : GhidraScriptUtil.getScriptSourceDirectories()) {
|
|
||||||
if (sourcePath.startsWith(sourceDir.getAbsolutePath() + File.separatorChar)) {
|
|
||||||
return sourceDir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createNewScript(ResourceFile newScript, String category) throws IOException {
|
public void createNewScript(ResourceFile newScript, String category) throws IOException {
|
||||||
String scriptName = newScript.getName();
|
String scriptName = newScript.getName();
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class ScriptInfo {
|
|||||||
* @param provider the script provider (for example, java or python)
|
* @param provider the script provider (for example, java or python)
|
||||||
* @param sourceFile the script source file
|
* @param sourceFile the script source file
|
||||||
*/
|
*/
|
||||||
public ScriptInfo(GhidraScriptProvider provider, ResourceFile sourceFile) {
|
ScriptInfo(GhidraScriptProvider provider, ResourceFile sourceFile) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.sourceFile = sourceFile;
|
this.sourceFile = sourceFile;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,13 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.framework.analysis;
|
package ghidra.framework.analysis;
|
||||||
|
|
||||||
|
import org.jdom.Element;
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
import ghidra.app.script.ScriptInfo;
|
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
|
|
||||||
import org.jdom.Element;
|
|
||||||
|
|
||||||
class AnalyzerInfo implements Comparable<AnalyzerInfo> {
|
class AnalyzerInfo implements Comparable<AnalyzerInfo> {
|
||||||
static final String XML_ELEMENT_NAME = "ANALYZER";
|
static final String XML_ELEMENT_NAME = "ANALYZER";
|
||||||
static final String CLASS_NAME = "CLASS_NAME";
|
static final String CLASS_NAME = "CLASS_NAME";
|
||||||
@@ -48,10 +46,12 @@ class AnalyzerInfo implements Comparable<AnalyzerInfo> {
|
|||||||
// first make all One-Shot Analyzers sort before all other types.
|
// first make all One-Shot Analyzers sort before all other types.
|
||||||
AnalyzerType myType = analyzer.getAnalysisType();
|
AnalyzerType myType = analyzer.getAnalysisType();
|
||||||
AnalyzerType otherType = o.analyzer.getAnalysisType();
|
AnalyzerType otherType = o.analyzer.getAnalysisType();
|
||||||
if (myType == AnalyzerType.ONE_SHOT_ANALYZER && otherType != AnalyzerType.ONE_SHOT_ANALYZER) {
|
if (myType == AnalyzerType.ONE_SHOT_ANALYZER &&
|
||||||
|
otherType != AnalyzerType.ONE_SHOT_ANALYZER) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (myType != AnalyzerType.ONE_SHOT_ANALYZER && otherType == AnalyzerType.ONE_SHOT_ANALYZER) {
|
if (myType != AnalyzerType.ONE_SHOT_ANALYZER &&
|
||||||
|
otherType == AnalyzerType.ONE_SHOT_ANALYZER) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,13 +125,13 @@ class AnalyzerInfo implements Comparable<AnalyzerInfo> {
|
|||||||
return startPhase;
|
return startPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AnalyzerInfo createInfoForWrappedAnalzyer(AnalysisRecipe recipe, Element element) {
|
public static AnalyzerInfo createInfoForWrappedAnalzyer(AnalysisRecipe recipe,
|
||||||
|
Element element) {
|
||||||
String scriptName = element.getAttributeValue("SCRIPT_NAME");
|
String scriptName = element.getAttributeValue("SCRIPT_NAME");
|
||||||
String type = element.getAttributeValue("ANALYZER_TYPE");
|
String type = element.getAttributeValue("ANALYZER_TYPE");
|
||||||
AnalyzerType analyzerType = AnalyzerType.valueOf(type);
|
AnalyzerType analyzerType = AnalyzerType.valueOf(type);
|
||||||
int priority = Integer.parseInt(element.getAttributeValue("PRIORITY"));
|
int priority = Integer.parseInt(element.getAttributeValue("PRIORITY"));
|
||||||
ScriptInfo scriptInfo = GhidraScriptUtil.findScriptByName(scriptName);
|
ResourceFile file = GhidraScriptUtil.findScriptByName(scriptName);
|
||||||
ResourceFile file = scriptInfo.getSourceFile();
|
|
||||||
Analyzer analyzer = new GhidraScriptAnalyzerAdapter(file, analyzerType, priority);
|
Analyzer analyzer = new GhidraScriptAnalyzerAdapter(file, analyzerType, priority);
|
||||||
return new AnalyzerInfo(recipe, analyzer, true);
|
return new AnalyzerInfo(recipe, analyzer, true);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ public class GhidraScriptAnalyzerAdapter extends AbstractAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String getDescription(ResourceFile file) {
|
private static String getDescription(ResourceFile file) {
|
||||||
return GhidraScriptUtil.getScriptInfo(file).getDescription();
|
return GhidraScriptUtil.newScriptInfo(file).getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrintWriter(PrintWriter writer) {
|
public void setPrintWriter(PrintWriter writer) {
|
||||||
|
|||||||
+4
-5
@@ -26,7 +26,6 @@ import docking.widgets.label.GLabel;
|
|||||||
import docking.widgets.textfield.IntegerTextField;
|
import docking.widgets.textfield.IntegerTextField;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
import ghidra.app.script.ScriptInfo;
|
|
||||||
import ghidra.app.services.AnalyzerType;
|
import ghidra.app.services.AnalyzerType;
|
||||||
import ghidra.util.layout.HorizontalLayout;
|
import ghidra.util.layout.HorizontalLayout;
|
||||||
import ghidra.util.layout.VerticalLayout;
|
import ghidra.util.layout.VerticalLayout;
|
||||||
@@ -36,8 +35,9 @@ public class GhidraScriptSelectionDialog extends ListSelectionDialog<ResourceFil
|
|||||||
private IntegerTextField priorityField;
|
private IntegerTextField priorityField;
|
||||||
|
|
||||||
public GhidraScriptSelectionDialog() {
|
public GhidraScriptSelectionDialog() {
|
||||||
super("Create Script Based Analyzer", "Script Name:", GhidraScriptUtil.getAllScripts(),
|
super("Create Script Based Analyzer", "Script Name:",
|
||||||
new ScriptNameConverter(), new ScriptDescriptionConverter());
|
GhidraScriptUtil.getScriptSourceDirectories(), new ScriptNameConverter(),
|
||||||
|
new ScriptDescriptionConverter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,8 +116,7 @@ public class GhidraScriptSelectionDialog extends ListSelectionDialog<ResourceFil
|
|||||||
private static class ScriptDescriptionConverter implements DataToStringConverter<ResourceFile> {
|
private static class ScriptDescriptionConverter implements DataToStringConverter<ResourceFile> {
|
||||||
@Override
|
@Override
|
||||||
public String getString(ResourceFile resourceFile) {
|
public String getString(ResourceFile resourceFile) {
|
||||||
ScriptInfo info = GhidraScriptUtil.getScriptInfo(resourceFile);
|
return GhidraScriptUtil.newScriptInfo(resourceFile).getDescription();
|
||||||
return info.getDescription();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -231,13 +231,12 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void assertScriptManagerKnowsAbout(ResourceFile script) {
|
protected void assertScriptManagerKnowsAbout(ResourceFile script) {
|
||||||
assertTrue(GhidraScriptUtil.containsMetadata(script));
|
assertTrue(provider.getInfoManager().containsMetadata(script));
|
||||||
assertNull(provider.getActionManager().get(script));
|
assertNull(provider.getActionManager().get(script));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertScriptManagerForgotAbout(ResourceFile script) {
|
protected void assertScriptManagerForgotAbout(ResourceFile script) {
|
||||||
|
assertFalse(provider.getInfoManager().containsMetadata(script));
|
||||||
assertFalse(GhidraScriptUtil.containsMetadata(script));
|
|
||||||
assertNull(provider.getActionManager().get(script));
|
assertNull(provider.getActionManager().get(script));
|
||||||
assertNull(provider.getEditorMap().get(script));
|
assertNull(provider.getEditorMap().get(script));
|
||||||
}
|
}
|
||||||
@@ -989,7 +988,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||||||
|
|
||||||
// destroy any NewScriptxxx files...and Temp ones too
|
// destroy any NewScriptxxx files...and Temp ones too
|
||||||
BundleStatusComponentProvider bundleStatusComponentProvider =
|
BundleStatusComponentProvider bundleStatusComponentProvider =
|
||||||
(BundleStatusComponentProvider) TestUtils.getInstanceField("bundleStatusComponentProvider", provider);
|
(BundleStatusComponentProvider) TestUtils.getInstanceField(
|
||||||
|
"bundleStatusComponentProvider", provider);
|
||||||
List<ResourceFile> paths = bundleStatusComponentProvider.getModel().getEnabledPaths();
|
List<ResourceFile> paths = bundleStatusComponentProvider.getModel().getEnabledPaths();
|
||||||
|
|
||||||
for (ResourceFile path : paths) {
|
for (ResourceFile path : paths) {
|
||||||
@@ -1161,7 +1161,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected ResourceFile findScript(String name) {
|
protected ResourceFile findScript(String name) {
|
||||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(name);
|
ScriptInfo info = provider.getInfoManager().getExistingScriptInfo(name);
|
||||||
assertNotNull("Cannot find script by the given name: " + name, info);
|
assertNotNull("Cannot find script by the given name: " + name, info);
|
||||||
return info.getSourceFile();
|
return info.getSourceFile();
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-11
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
|
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||||
import ghidra.app.script.*;
|
import ghidra.app.script.*;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
@@ -32,8 +34,6 @@ import ghidra.util.Msg;
|
|||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
public class WindowsResourceReferenceAnalyzer extends AbstractAnalyzer {
|
public class WindowsResourceReferenceAnalyzer extends AbstractAnalyzer {
|
||||||
private static final String NAME = "WindowsResourceReference";
|
private static final String NAME = "WindowsResourceReference";
|
||||||
private static final String DESCRIPTION =
|
private static final String DESCRIPTION =
|
||||||
@@ -81,23 +81,21 @@ public class WindowsResourceReferenceAnalyzer extends AbstractAnalyzer {
|
|||||||
PluginTool tool = analysisManager.getAnalysisTool();
|
PluginTool tool = analysisManager.getAnalysisTool();
|
||||||
Project project = findProject(tool);
|
Project project = findProject(tool);
|
||||||
|
|
||||||
GhidraState state =
|
GhidraState state = new GhidraState(tool, project, program,
|
||||||
new GhidraState(tool, project, program, new ProgramLocation(program,
|
new ProgramLocation(program, set.getMinAddress()), new ProgramSelection(set), null);
|
||||||
set.getMinAddress()), new ProgramSelection(set), null);
|
|
||||||
try {
|
try {
|
||||||
ScriptInfo scriptInfo = GhidraScriptUtil.findScriptByName(scriptName);
|
ResourceFile sourceFile = GhidraScriptUtil.findScriptByName(scriptName);
|
||||||
if (scriptInfo == null) {
|
if (sourceFile == null) {
|
||||||
throw new IllegalAccessException("Couldn't find script");
|
throw new IllegalAccessException("Couldn't find script");
|
||||||
}
|
}
|
||||||
GhidraScriptProvider provider =
|
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(sourceFile);
|
||||||
GhidraScriptUtil.getProvider(scriptInfo.getSourceFile());
|
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
throw new IllegalAccessException("Couldn't find script provider");
|
throw new IllegalAccessException("Couldn't find script provider");
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintWriter writer = getOutputMsgStream(tool);
|
PrintWriter writer = getOutputMsgStream(tool);
|
||||||
|
|
||||||
GhidraScript script = provider.getScriptInstance(scriptInfo.getSourceFile(), writer);
|
GhidraScript script = provider.getScriptInstance(sourceFile, writer);
|
||||||
script.set(state, monitor, writer);
|
script.set(state, monitor, writer);
|
||||||
|
|
||||||
// This code was added so the analyzer won't print script messages to console
|
// This code was added so the analyzer won't print script messages to console
|
||||||
|
|||||||
+7
@@ -23,6 +23,7 @@ import java.util.ArrayList;
|
|||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.framework.task.GScheduledTask;
|
import ghidra.framework.task.GScheduledTask;
|
||||||
@@ -77,6 +78,12 @@ public class AnalysisManagerTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
||||||
program = programBuilder.getProgram();
|
program = programBuilder.getProgram();
|
||||||
analyzers = new ArrayList<>();
|
analyzers = new ArrayList<>();
|
||||||
|
GhidraScriptUtil.initialize(new BundleHost(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
GhidraScriptUtil.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+12
-6
@@ -21,11 +21,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.junit.Before;
|
import org.junit.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||||
import ghidra.app.script.GhidraScriptUtil;
|
import ghidra.app.script.GhidraScriptUtil;
|
||||||
import ghidra.app.script.ScriptInfo;
|
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
@@ -47,6 +47,12 @@ public class AnalysisRecipeTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
||||||
program = programBuilder.getProgram();
|
program = programBuilder.getProgram();
|
||||||
analyzers = new ArrayList<>();
|
analyzers = new ArrayList<>();
|
||||||
|
GhidraScriptUtil.initialize(new BundleHost(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
GhidraScriptUtil.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,9 +94,9 @@ public class AnalysisRecipeTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
analyzers.add(analyzer1);
|
analyzers.add(analyzer1);
|
||||||
analyzers.add(analyzer2);
|
analyzers.add(analyzer2);
|
||||||
recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
||||||
ScriptInfo info = GhidraScriptUtil.findScriptByName("HelloWorldScript.java");
|
ResourceFile sourceFile = GhidraScriptUtil.findScriptByName("HelloWorldScript.java");
|
||||||
assertNotNull(info);
|
assertNotNull(sourceFile);
|
||||||
recipe.addScriptAnalyzer(info.getSourceFile(), AnalyzerType.INSTRUCTION_ANALYZER, 15);
|
recipe.addScriptAnalyzer(sourceFile, AnalyzerType.INSTRUCTION_ANALYZER, 15);
|
||||||
AnalysisPhase lastPhase = recipe.getLastPhase();
|
AnalysisPhase lastPhase = recipe.getLastPhase();
|
||||||
AnalysisPhase firstPhase = recipe.createPhase();
|
AnalysisPhase firstPhase = recipe.createPhase();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user