mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 05:52:25 +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() {
|
||||
if (felix != null) {
|
||||
forceStopFramework();
|
||||
}
|
||||
disposeFramework();
|
||||
}
|
||||
|
||||
HashMap<ResourceFile, GhidraBundle> bp2gb = new HashMap<>();
|
||||
@@ -503,14 +501,16 @@ public class BundleHost {
|
||||
}
|
||||
}
|
||||
|
||||
void forceStopFramework() {
|
||||
try {
|
||||
felix.stop();
|
||||
felix.waitForStop(5000);
|
||||
felix = null;
|
||||
}
|
||||
catch (BundleException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
void disposeFramework() {
|
||||
if (felix != null) {
|
||||
try {
|
||||
felix.stop();
|
||||
felix.waitForStop(5000);
|
||||
felix = null;
|
||||
}
|
||||
catch (BundleException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ public class BundleHost {
|
||||
Task t = new Task("killing OSGi framework", false, false, true, true) {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
forceStopFramework();
|
||||
disposeFramework();
|
||||
}
|
||||
|
||||
};
|
||||
@@ -599,10 +599,10 @@ public class BundleHost {
|
||||
|
||||
List<BundleHostListener> listeners = new ArrayList<>();
|
||||
|
||||
void fireBundleBuilt(GhidraBundle sbi) {
|
||||
void fireBundleBuilt(GhidraBundle gb) {
|
||||
synchronized (listeners) {
|
||||
for (BundleHostListener l : listeners) {
|
||||
l.bundleBuilt(sbi);
|
||||
l.bundleBuilt(gb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -140,6 +140,11 @@ public class GhidraSourceBundle extends GhidraBundle {
|
||||
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
|
||||
*
|
||||
@@ -155,8 +160,7 @@ public class GhidraSourceBundle extends GhidraBundle {
|
||||
// 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
|
||||
ScriptInfo si = GhidraScriptUtil.getScriptInfo(rf);
|
||||
String imps = si.getImports();
|
||||
String imps = parseImps(rf);
|
||||
if (imps != null && !imps.isEmpty()) {
|
||||
List<BundleRequirement> reqs;
|
||||
try {
|
||||
|
||||
+7
-6
@@ -33,8 +33,7 @@ import docking.actions.KeyBindingUtils;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.util.*;
|
||||
@@ -51,6 +50,7 @@ class GhidraScriptActionManager {
|
||||
|
||||
private GhidraScriptComponentProvider provider;
|
||||
private GhidraScriptMgrPlugin plugin;
|
||||
private GhidraScriptInfoManager infoManager;
|
||||
private DockingAction refreshAction;
|
||||
private DockingAction bundleStatusAction;
|
||||
private DockingAction newAction;
|
||||
@@ -65,10 +65,11 @@ class GhidraScriptActionManager {
|
||||
private DockingAction helpAction;
|
||||
private Map<ResourceFile, ScriptAction> actionMap = new HashMap<>();
|
||||
|
||||
GhidraScriptActionManager(GhidraScriptComponentProvider provider,
|
||||
GhidraScriptMgrPlugin plugin) {
|
||||
GhidraScriptActionManager(GhidraScriptComponentProvider provider, GhidraScriptMgrPlugin plugin,
|
||||
GhidraScriptInfoManager infoManager) {
|
||||
this.provider = provider;
|
||||
this.plugin = plugin;
|
||||
this.infoManager = infoManager;
|
||||
createActions();
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ class GhidraScriptActionManager {
|
||||
void restoreScriptsThatAreInTool(SaveState saveState) {
|
||||
String[] array = saveState.getStrings(SCRIPT_ACTIONS_KEY, new String[0]);
|
||||
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
|
||||
provider.getActionManager().createAction(info.getSourceFile());
|
||||
}
|
||||
@@ -129,7 +130,7 @@ class GhidraScriptActionManager {
|
||||
continue;
|
||||
}
|
||||
ResourceFile scriptFile = action.getScript();
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(scriptFile);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(scriptFile);
|
||||
if (info == null) {
|
||||
Msg.showError(this, provider.getComponent(), "Bad state?",
|
||||
"action associated with a script that has no info");
|
||||
|
||||
+30
-38
@@ -71,6 +71,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
private RootNode scriptRoot;
|
||||
private GTree scriptCategoryTree;
|
||||
private DraggableScriptTable scriptTable;
|
||||
private GhidraScriptInfoManager infoManager;
|
||||
private GhidraScriptTableModel tableModel;
|
||||
private BundleStatusComponentProvider bundleStatusComponentProvider;
|
||||
private TaskListener taskListener = new ScriptTaskListener();
|
||||
@@ -102,23 +103,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
final private RefreshingBundleHostListener refreshingBundleHostListener =
|
||||
new RefreshingBundleHostListener();
|
||||
|
||||
static private int loaded = 0;
|
||||
|
||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin) {
|
||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
|
||||
super(plugin.getTool(), "Script Manager", plugin.getName());
|
||||
|
||||
this.plugin = plugin;
|
||||
|
||||
if (loaded == 0) {
|
||||
bundleHost = new BundleHost();
|
||||
GhidraScriptUtil.initialize(bundleHost);
|
||||
bundleHost.addGhidraBundle(GhidraScriptUtil.getUserScriptDirectory(), true, false);
|
||||
bundleHost.addGhidraBundles(GhidraScriptUtil.getSystemScriptPaths(), true, true);
|
||||
}
|
||||
else {
|
||||
bundleHost = GhidraScriptUtil.getBundleHost();
|
||||
}
|
||||
loaded += 1;
|
||||
this.bundleHost = bundleHost;
|
||||
this.infoManager = new GhidraScriptInfoManager();
|
||||
|
||||
bundleStatusComponentProvider =
|
||||
new BundleStatusComponentProvider(plugin.getTool(), plugin.getName(), bundleHost);
|
||||
@@ -133,7 +123,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
build();
|
||||
|
||||
plugin.getTool().addComponentProvider(this, false);
|
||||
actionManager = new GhidraScriptActionManager(this, plugin);
|
||||
actionManager = new GhidraScriptActionManager(this, plugin, infoManager);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
@@ -171,7 +161,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
scriptCategoryTree.getSelectionModel().setSelectionMode(
|
||||
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
|
||||
tableModel = new GhidraScriptTableModel(this);
|
||||
tableModel = new GhidraScriptTableModel(this, infoManager);
|
||||
|
||||
scriptTable = new DraggableScriptTable(this, tableModel);
|
||||
scriptTable.setName("SCRIPT_TABLE");
|
||||
@@ -292,11 +282,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
tableFilterPanel.dispose();
|
||||
actionManager.dispose();
|
||||
bundleStatusComponentProvider.dispose();
|
||||
|
||||
loaded -= 1;
|
||||
if (loaded == 0) {
|
||||
GhidraScriptUtil.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public BundleHost getBundleHost() {
|
||||
@@ -307,6 +292,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
return actionManager;
|
||||
}
|
||||
|
||||
GhidraScriptInfoManager getInfoManager() {
|
||||
return infoManager;
|
||||
}
|
||||
|
||||
Map<ResourceFile, GhidraScriptEditorComponentProvider> getEditorMap() {
|
||||
return editorMap;
|
||||
}
|
||||
@@ -410,6 +399,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
renameFile.delete();
|
||||
return;
|
||||
}
|
||||
infoManager.removeMetadata(script);
|
||||
|
||||
if (actionManager.hasScriptAction(script)) {
|
||||
KeyStroke ks = actionManager.getKeyBinding(script);
|
||||
actionManager.removeAction(script);
|
||||
@@ -417,9 +408,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
action.setKeyBindingData(new KeyBindingData(ks));
|
||||
}
|
||||
|
||||
assert !GhidraScriptUtil.containsMetadata(
|
||||
assert !infoManager.containsMetadata(
|
||||
renameFile) : "renamed script already has metadata";
|
||||
GhidraScriptUtil.getScriptInfo(renameFile);
|
||||
infoManager.getScriptInfo(renameFile);
|
||||
|
||||
tableModel.switchScript(script, renameFile);
|
||||
setSelectedScript(renameFile);
|
||||
@@ -496,6 +487,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
if (removeScript(script)) {
|
||||
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(script);
|
||||
if (provider.deleteScript(script)) {
|
||||
infoManager.removeMetadata(script);
|
||||
restoreSelection(script);
|
||||
}
|
||||
else {
|
||||
@@ -563,8 +555,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
|
||||
// create the ScriptInfo object now, before the TableModelEvent handlers
|
||||
// attempt to use it.
|
||||
assert !GhidraScriptUtil.containsMetadata(newFile) : "new source already has metadata?";
|
||||
GhidraScriptUtil.getScriptInfo(newFile);
|
||||
assert !infoManager.containsMetadata(newFile) : "new source already has metadata?";
|
||||
infoManager.getScriptInfo(newFile);
|
||||
|
||||
tableModel.insertScript(newFile);
|
||||
|
||||
@@ -710,8 +702,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
if (sb instanceof GhidraSourceBundle) {
|
||||
GhidraSourceBundle gsb = (GhidraSourceBundle) sb;
|
||||
for (ResourceFile sf : gsb.getNewSources()) {
|
||||
if (GhidraScriptUtil.containsMetadata(sf)) {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(sf);
|
||||
if (infoManager.containsMetadata(sf)) {
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(sf);
|
||||
BuildFailure e = gsb.getErrors(sf);
|
||||
info.setCompileErrors(e != null);
|
||||
}
|
||||
@@ -753,7 +745,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private void performRefresh() {
|
||||
GhidraScriptUtil.clearMetadata();
|
||||
infoManager.clearMetadata();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -787,7 +779,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
removeScript(file);
|
||||
}
|
||||
|
||||
GhidraScriptUtil.refreshDuplicates();
|
||||
infoManager.refreshDuplicates();
|
||||
refreshScriptData();
|
||||
}
|
||||
|
||||
@@ -808,7 +800,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
// new ScriptInfo objects are created on performRefresh, e.g. on startup. Other
|
||||
// refresh operations might have old infos.
|
||||
// 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();
|
||||
scriptRoot.insert(categoryPath);
|
||||
}
|
||||
@@ -823,7 +815,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
for (ResourceFile script : scripts) {
|
||||
// First get the ScriptInfo object and refresh, which will ensure any
|
||||
// info data (ie: script icons) will be reloaded.
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
info.refresh();
|
||||
|
||||
ScriptAction scriptAction = actionManager.get(script);
|
||||
@@ -851,7 +843,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
|
||||
// note: turn String[] to List<String> to use hashing
|
||||
Set<List<String>> categories = new HashSet<>();
|
||||
for (ScriptInfo info : GhidraScriptUtil.getScriptInfoIterable()) {
|
||||
for (ScriptInfo info : infoManager.getScriptInfoIterable()) {
|
||||
String[] path = info.getCategory();
|
||||
List<String> category = Arrays.asList(path);
|
||||
for (int i = 1; i <= category.size(); i++) {
|
||||
@@ -938,7 +930,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
editorMap.put(newScript, editor);
|
||||
editorMap.remove(oldScript);
|
||||
// create corresponding info before inserting in table
|
||||
GhidraScriptUtil.getScriptInfo(newScript);
|
||||
infoManager.getScriptInfo(newScript);
|
||||
tableModel.insertScript(newScript);
|
||||
}
|
||||
|
||||
@@ -953,7 +945,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
actionManager.removeAction(script);
|
||||
GhidraScriptUtil.removeMetadata(script);
|
||||
infoManager.removeMetadata(script);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1012,7 +1004,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
// the selected script has been changed, update the description panel
|
||||
updateDescriptionPanel();
|
||||
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
updateCategoryTree(info.getCategory());
|
||||
}
|
||||
|
||||
@@ -1029,7 +1021,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
|
||||
@Override
|
||||
public List<String> transform(ResourceFile script) {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
list.clear();
|
||||
list.add(info.getName());
|
||||
list.add(info.getDescription());
|
||||
@@ -1063,7 +1055,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
String text = "Error! no script info!";
|
||||
ResourceFile script = getSelectedScript();
|
||||
if (script != null) {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
if (info != null) {
|
||||
text = info.getToolTipText();
|
||||
}
|
||||
@@ -1200,7 +1192,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
|
||||
@Override
|
||||
public boolean acceptsRow(ResourceFile script) {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
String[] category = getSelectedCategoryPath();
|
||||
|
||||
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.core.eclipse.EclipseConnection;
|
||||
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.services.*;
|
||||
import ghidra.framework.options.SaveState;
|
||||
@@ -50,16 +52,31 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
|
||||
|
||||
final private GhidraScriptComponentProvider provider;
|
||||
|
||||
static private int loaded = 0;
|
||||
final private BundleHost bundleHost;
|
||||
|
||||
public GhidraScriptMgrPlugin(PluginTool tool) {
|
||||
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
|
||||
protected void dispose() {
|
||||
super.dispose();
|
||||
provider.dispose();
|
||||
loaded -= 1;
|
||||
if (loaded == 0) {
|
||||
GhidraScriptUtil.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-8
@@ -23,7 +23,7 @@ import javax.swing.*;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.GhidraScriptInfoManager;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
@@ -44,10 +44,13 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
|
||||
private GhidraScriptComponentProvider provider;
|
||||
private List<ResourceFile> scriptList = new ArrayList<>();
|
||||
final private GhidraScriptInfoManager infoManager;
|
||||
|
||||
GhidraScriptTableModel(GhidraScriptComponentProvider provider) {
|
||||
GhidraScriptTableModel(GhidraScriptComponentProvider provider,
|
||||
GhidraScriptInfoManager infoManager) {
|
||||
super(provider.getTool());
|
||||
this.provider = provider;
|
||||
this.infoManager = infoManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,7 +108,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
scriptList.add(script);
|
||||
}
|
||||
}
|
||||
fireTableRowsInserted(rowStart, rowStart + scriptFiles.size()-1);
|
||||
fireTableRowsInserted(rowStart, rowStart + scriptFiles.size() - 1);
|
||||
}
|
||||
|
||||
void removeScript(ResourceFile script) {
|
||||
@@ -238,7 +241,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(data);
|
||||
|
||||
ResourceFile file = (ResourceFile) data.getRowObject();
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(file);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(file);
|
||||
|
||||
label.setText(null);
|
||||
label.setToolTipText(null);
|
||||
@@ -279,7 +282,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
@Override
|
||||
public ImageIcon getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||
if (info.isCompileErrors() || info.isDuplicate()) {
|
||||
return ERROR_IMG;
|
||||
}
|
||||
@@ -329,7 +332,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
@Override
|
||||
public String getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||
return info.getDescription();
|
||||
}
|
||||
|
||||
@@ -401,7 +404,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
@Override
|
||||
public KeyBindingsInfo getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||
KeyStroke actionKeyStroke = provider.getActionManager().getKeyBinding(rowObject);
|
||||
boolean isActionBinding = false;
|
||||
KeyStroke keyBinding = info.getKeyBinding();
|
||||
@@ -459,7 +462,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
||||
@Override
|
||||
public String getValue(ResourceFile rowObject, Settings settings, Object data,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(rowObject);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
|
||||
return getCategoryString(info);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import ghidra.app.script.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
public class SaveDialog extends DialogComponentProvider implements ListSelectionListener {
|
||||
private GhidraScriptComponentProvider componentProvider;
|
||||
protected GhidraScriptComponentProvider componentProvider;
|
||||
private GhidraScriptProvider provider;
|
||||
|
||||
private List<ResourceFile> paths;
|
||||
@@ -49,7 +49,8 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
|
||||
public SaveDialog(Component parent, String title,
|
||||
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
|
||||
HelpLocation help) {
|
||||
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(), scriptFile, help);
|
||||
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(),
|
||||
scriptFile, help);
|
||||
}
|
||||
|
||||
public SaveDialog(Component parent, String title,
|
||||
@@ -161,7 +162,7 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
|
||||
}
|
||||
|
||||
protected String getDuplicateNameErrorMessage(String name) {
|
||||
ScriptInfo existingInfo = GhidraScriptUtil.getExistingScriptInfo(name);
|
||||
ScriptInfo existingInfo = componentProvider.getInfoManager().getExistingScriptInfo(name);
|
||||
if (existingInfo != null) {
|
||||
// make sure the script has not been deleted
|
||||
ResourceFile sourceFile = existingInfo.getSourceFile();
|
||||
|
||||
+4
-6
@@ -15,20 +15,18 @@
|
||||
*/
|
||||
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.io.File;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
class SaveNewScriptDialog extends SaveDialog {
|
||||
|
||||
SaveNewScriptDialog(Component parent, String title,
|
||||
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
|
||||
HelpLocation help) {
|
||||
super(parent, title, componentProvider, scriptFile, help);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +35,7 @@ class SaveNewScriptDialog extends SaveDialog {
|
||||
*/
|
||||
@Override
|
||||
protected String getDuplicateNameErrorMessage(String name) {
|
||||
if (GhidraScriptUtil.alreadyExists(name)) {
|
||||
if (componentProvider.getInfoManager().alreadyExists(name)) {
|
||||
return "Duplicate script name.";
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import javax.swing.KeyStroke;
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.GhidraScriptInfoManager;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
@@ -30,6 +30,7 @@ class ScriptAction extends DockingAction {
|
||||
private static final String SCRIPT_GROUP = "_SCRIPT_GROUP_";
|
||||
|
||||
private GhidraScriptMgrPlugin plugin;
|
||||
private GhidraScriptInfoManager infoManager;
|
||||
private ResourceFile script;
|
||||
|
||||
/** 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) {
|
||||
super(script.getName(), plugin.getName());
|
||||
this.plugin = plugin;
|
||||
this.infoManager = plugin.getProvider().getInfoManager();
|
||||
this.script = script;
|
||||
setEnabled(true);
|
||||
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
|
||||
@@ -90,7 +92,7 @@ class ScriptAction extends DockingAction {
|
||||
}
|
||||
|
||||
// check to see if we have a fallback value
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
KeyStroke metadataKeyStroke = info.getKeyBinding();
|
||||
if (metadataKeyStroke == null) {
|
||||
// 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
|
||||
// that which is defined in the metadata of the script
|
||||
KeyStroke actionKeyStroke = keyBindingData.getKeyBinding();
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
KeyStroke metadataKeyBinding = info.getKeyBinding();
|
||||
isUserDefinedKeyBinding = !SystemUtilities.isEqual(actionKeyStroke, metadataKeyBinding);
|
||||
}
|
||||
@@ -119,7 +121,7 @@ class ScriptAction extends DockingAction {
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
ScriptInfo info = GhidraScriptUtil.getExistingScriptInfo(script);
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(script);
|
||||
KeyStroke stroke = info.getKeyBinding();
|
||||
if (!isUserDefinedKeyBinding) {
|
||||
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;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* NOTE: ALL GhidraScriptProvider CLASSES MUST END IN "ScriptProvider". If not,
|
||||
* the ClassSearcher will not find them.
|
||||
*
|
||||
*/
|
||||
public abstract class GhidraScriptProvider implements ExtensionPoint,
|
||||
Comparable<GhidraScriptProvider> {
|
||||
public abstract class GhidraScriptProvider
|
||||
implements ExtensionPoint, Comparable<GhidraScriptProvider> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -59,11 +59,7 @@ public abstract class GhidraScriptProvider implements ExtensionPoint,
|
||||
* @return true if the script was completely deleted and cleaned up
|
||||
*/
|
||||
public boolean deleteScript(ResourceFile scriptSource) {
|
||||
boolean deleted = !scriptSource.exists() || scriptSource.delete();
|
||||
if (deleted) {
|
||||
GhidraScriptUtil.removeMetadata(scriptSource);
|
||||
}
|
||||
return deleted;
|
||||
return !scriptSource.exists() || scriptSource.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,17 +44,13 @@ public class GhidraScriptUtil {
|
||||
*/
|
||||
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;
|
||||
|
||||
public static BundleHost getBundleHost() {
|
||||
return _bundleHost;
|
||||
}
|
||||
|
||||
public static void initialize(BundleHost bundleHost) {
|
||||
private static void setBundleHost(BundleHost bundleHost) {
|
||||
if (_bundleHost != null) {
|
||||
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 extraSystemPaths additional system paths for this run, can be null
|
||||
*
|
||||
*/
|
||||
public static void initialize(BundleHost bundleHost, List<String> extraSystemPaths) {
|
||||
initialize(bundleHost);
|
||||
|
||||
setBundleHost(bundleHost);
|
||||
if (extraSystemPaths != null) {
|
||||
for (String path : extraSystemPaths) {
|
||||
bundleHost.addGhidraBundle(new ResourceFile(path), true, true);
|
||||
@@ -90,19 +85,13 @@ public class GhidraScriptUtil {
|
||||
}
|
||||
|
||||
public static void dispose() {
|
||||
clearMetadata();
|
||||
_bundleHost = null;
|
||||
if (_bundleHost != null) {
|
||||
_bundleHost.dispose();
|
||||
_bundleHost = 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.
|
||||
* @return a list of the current script directories
|
||||
@@ -112,6 +101,20 @@ public class GhidraScriptUtil {
|
||||
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
|
||||
* SystemUtilities.USER_SCRIPTS_DIR system property.
|
||||
@@ -224,130 +227,6 @@ public class GhidraScriptUtil {
|
||||
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
|
||||
*
|
||||
@@ -435,6 +314,10 @@ public class GhidraScriptUtil {
|
||||
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.
|
||||
* <p>
|
||||
@@ -457,8 +340,8 @@ public class GhidraScriptUtil {
|
||||
return path + ".java";
|
||||
}
|
||||
|
||||
/** Returns true if the given filename exists in any of the given directories */
|
||||
private static ResourceFile findScriptFileInPaths(Collection<ResourceFile> scriptDirectories,
|
||||
static ResourceFile findScriptFileInPaths(
|
||||
Collection<ResourceFile> scriptDirectories,
|
||||
String filename) {
|
||||
|
||||
String validatedName = fixupName(filename);
|
||||
@@ -474,33 +357,6 @@ public class GhidraScriptUtil {
|
||||
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 */
|
||||
/**
|
||||
* Runs the specified script with the specified state
|
||||
@@ -537,31 +393,4 @@ public class GhidraScriptUtil {
|
||||
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) {
|
||||
ResourceFile sourceDir = getSourceDirectoryContaining(sourceFile);
|
||||
ResourceFile sourceDir = GhidraScriptUtil.getSourceDirectoryContaining(sourceFile);
|
||||
if (sourceDir == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -67,8 +67,6 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
@Override
|
||||
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
// in headless operation, ScriptInfo objects can be created here
|
||||
ScriptInfo info = GhidraScriptUtil.getScriptInfo(sourceFile);
|
||||
try {
|
||||
Class<?> clazz = loadClass(sourceFile, writer);
|
||||
Object object;
|
||||
@@ -83,16 +81,13 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
String message = "Not a valid Ghidra script: " + sourceFile.getName();
|
||||
writer.println(message);
|
||||
Msg.error(this, message);
|
||||
info.setCompileErrors(true);
|
||||
return null; // class is not GhidraScript
|
||||
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
info.setCompileErrors(true);
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
info.setCompileErrors(true);
|
||||
throw new ClassNotFoundException("", e);
|
||||
}
|
||||
}
|
||||
@@ -109,16 +104,6 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
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
|
||||
public void createNewScript(ResourceFile newScript, String category) throws IOException {
|
||||
String scriptName = newScript.getName();
|
||||
|
||||
@@ -78,7 +78,7 @@ public class ScriptInfo {
|
||||
* @param provider the script provider (for example, java or python)
|
||||
* @param sourceFile the script source file
|
||||
*/
|
||||
public ScriptInfo(GhidraScriptProvider provider, ResourceFile sourceFile) {
|
||||
ScriptInfo(GhidraScriptProvider provider, ResourceFile sourceFile) {
|
||||
this.provider = provider;
|
||||
this.sourceFile = sourceFile;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,13 +15,12 @@
|
||||
*/
|
||||
package ghidra.framework.analysis;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.app.services.*;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
||||
class AnalyzerInfo implements Comparable<AnalyzerInfo> {
|
||||
static final String XML_ELEMENT_NAME = "ANALYZER";
|
||||
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.
|
||||
AnalyzerType myType = 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;
|
||||
}
|
||||
if (myType != AnalyzerType.ONE_SHOT_ANALYZER && otherType == AnalyzerType.ONE_SHOT_ANALYZER) {
|
||||
if (myType != AnalyzerType.ONE_SHOT_ANALYZER &&
|
||||
otherType == AnalyzerType.ONE_SHOT_ANALYZER) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -125,13 +125,13 @@ class AnalyzerInfo implements Comparable<AnalyzerInfo> {
|
||||
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 type = element.getAttributeValue("ANALYZER_TYPE");
|
||||
AnalyzerType analyzerType = AnalyzerType.valueOf(type);
|
||||
int priority = Integer.parseInt(element.getAttributeValue("PRIORITY"));
|
||||
ScriptInfo scriptInfo = GhidraScriptUtil.findScriptByName(scriptName);
|
||||
ResourceFile file = scriptInfo.getSourceFile();
|
||||
ResourceFile file = GhidraScriptUtil.findScriptByName(scriptName);
|
||||
Analyzer analyzer = new GhidraScriptAnalyzerAdapter(file, analyzerType, priority);
|
||||
return new AnalyzerInfo(recipe, analyzer, true);
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class GhidraScriptAnalyzerAdapter extends AbstractAnalyzer {
|
||||
}
|
||||
|
||||
private static String getDescription(ResourceFile file) {
|
||||
return GhidraScriptUtil.getScriptInfo(file).getDescription();
|
||||
return GhidraScriptUtil.newScriptInfo(file).getDescription();
|
||||
}
|
||||
|
||||
public void setPrintWriter(PrintWriter writer) {
|
||||
|
||||
+4
-5
@@ -26,7 +26,6 @@ import docking.widgets.label.GLabel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.app.services.AnalyzerType;
|
||||
import ghidra.util.layout.HorizontalLayout;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
@@ -36,8 +35,9 @@ public class GhidraScriptSelectionDialog extends ListSelectionDialog<ResourceFil
|
||||
private IntegerTextField priorityField;
|
||||
|
||||
public GhidraScriptSelectionDialog() {
|
||||
super("Create Script Based Analyzer", "Script Name:", GhidraScriptUtil.getAllScripts(),
|
||||
new ScriptNameConverter(), new ScriptDescriptionConverter());
|
||||
super("Create Script Based Analyzer", "Script Name:",
|
||||
GhidraScriptUtil.getScriptSourceDirectories(), new ScriptNameConverter(),
|
||||
new ScriptDescriptionConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,8 +116,7 @@ public class GhidraScriptSelectionDialog extends ListSelectionDialog<ResourceFil
|
||||
private static class ScriptDescriptionConverter implements DataToStringConverter<ResourceFile> {
|
||||
@Override
|
||||
public String getString(ResourceFile resourceFile) {
|
||||
ScriptInfo info = GhidraScriptUtil.getScriptInfo(resourceFile);
|
||||
return info.getDescription();
|
||||
return GhidraScriptUtil.newScriptInfo(resourceFile).getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -231,13 +231,12 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
}
|
||||
|
||||
protected void assertScriptManagerKnowsAbout(ResourceFile script) {
|
||||
assertTrue(GhidraScriptUtil.containsMetadata(script));
|
||||
assertTrue(provider.getInfoManager().containsMetadata(script));
|
||||
assertNull(provider.getActionManager().get(script));
|
||||
}
|
||||
|
||||
protected void assertScriptManagerForgotAbout(ResourceFile script) {
|
||||
|
||||
assertFalse(GhidraScriptUtil.containsMetadata(script));
|
||||
assertFalse(provider.getInfoManager().containsMetadata(script));
|
||||
assertNull(provider.getActionManager().get(script));
|
||||
assertNull(provider.getEditorMap().get(script));
|
||||
}
|
||||
@@ -989,7 +988,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
// destroy any NewScriptxxx files...and Temp ones too
|
||||
BundleStatusComponentProvider bundleStatusComponentProvider =
|
||||
(BundleStatusComponentProvider) TestUtils.getInstanceField("bundleStatusComponentProvider", provider);
|
||||
(BundleStatusComponentProvider) TestUtils.getInstanceField(
|
||||
"bundleStatusComponentProvider", provider);
|
||||
List<ResourceFile> paths = bundleStatusComponentProvider.getModel().getEnabledPaths();
|
||||
|
||||
for (ResourceFile path : paths) {
|
||||
@@ -1161,7 +1161,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
}
|
||||
|
||||
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);
|
||||
return info.getSourceFile();
|
||||
}
|
||||
|
||||
+9
-11
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +15,9 @@
|
||||
*/
|
||||
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.services.*;
|
||||
@@ -32,8 +34,6 @@ import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class WindowsResourceReferenceAnalyzer extends AbstractAnalyzer {
|
||||
private static final String NAME = "WindowsResourceReference";
|
||||
private static final String DESCRIPTION =
|
||||
@@ -81,23 +81,21 @@ public class WindowsResourceReferenceAnalyzer extends AbstractAnalyzer {
|
||||
PluginTool tool = analysisManager.getAnalysisTool();
|
||||
Project project = findProject(tool);
|
||||
|
||||
GhidraState state =
|
||||
new GhidraState(tool, project, program, new ProgramLocation(program,
|
||||
set.getMinAddress()), new ProgramSelection(set), null);
|
||||
GhidraState state = new GhidraState(tool, project, program,
|
||||
new ProgramLocation(program, set.getMinAddress()), new ProgramSelection(set), null);
|
||||
try {
|
||||
ScriptInfo scriptInfo = GhidraScriptUtil.findScriptByName(scriptName);
|
||||
if (scriptInfo == null) {
|
||||
ResourceFile sourceFile = GhidraScriptUtil.findScriptByName(scriptName);
|
||||
if (sourceFile == null) {
|
||||
throw new IllegalAccessException("Couldn't find script");
|
||||
}
|
||||
GhidraScriptProvider provider =
|
||||
GhidraScriptUtil.getProvider(scriptInfo.getSourceFile());
|
||||
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(sourceFile);
|
||||
if (provider == null) {
|
||||
throw new IllegalAccessException("Couldn't find script provider");
|
||||
}
|
||||
|
||||
PrintWriter writer = getOutputMsgStream(tool);
|
||||
|
||||
GhidraScript script = provider.getScriptInstance(scriptInfo.getSourceFile(), writer);
|
||||
GhidraScript script = provider.getScriptInstance(sourceFile, writer);
|
||||
script.set(state, monitor, writer);
|
||||
|
||||
// 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 generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.framework.task.GScheduledTask;
|
||||
@@ -77,6 +78,12 @@ public class AnalysisManagerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
||||
program = programBuilder.getProgram();
|
||||
analyzers = new ArrayList<>();
|
||||
GhidraScriptUtil.initialize(new BundleHost(), null);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
GhidraScriptUtil.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+12
-6
@@ -21,11 +21,11 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jdom.Element;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||
import ghidra.app.script.GhidraScriptUtil;
|
||||
import ghidra.app.script.ScriptInfo;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
@@ -47,6 +47,12 @@ public class AnalysisRecipeTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
programBuilder.createMemory("AAA", "0x100", 0x1000);
|
||||
program = programBuilder.getProgram();
|
||||
analyzers = new ArrayList<>();
|
||||
GhidraScriptUtil.initialize(new BundleHost(), null);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
GhidraScriptUtil.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -88,9 +94,9 @@ public class AnalysisRecipeTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
analyzers.add(analyzer1);
|
||||
analyzers.add(analyzer2);
|
||||
recipe = new AnalysisRecipe("Test Recipe", analyzers, program);
|
||||
ScriptInfo info = GhidraScriptUtil.findScriptByName("HelloWorldScript.java");
|
||||
assertNotNull(info);
|
||||
recipe.addScriptAnalyzer(info.getSourceFile(), AnalyzerType.INSTRUCTION_ANALYZER, 15);
|
||||
ResourceFile sourceFile = GhidraScriptUtil.findScriptByName("HelloWorldScript.java");
|
||||
assertNotNull(sourceFile);
|
||||
recipe.addScriptAnalyzer(sourceFile, AnalyzerType.INSTRUCTION_ANALYZER, 15);
|
||||
AnalysisPhase lastPhase = recipe.getLastPhase();
|
||||
AnalysisPhase firstPhase = recipe.createPhase();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user