diff --git a/Ghidra/Features/BSim/ghidra_scripts/DumpBSimDebugSignaturesScript.py b/Ghidra/Features/BSim/ghidra_scripts/DumpBSimDebugSignaturesScript.py index 3d6221595a..430f4cf77e 100755 --- a/Ghidra/Features/BSim/ghidra_scripts/DumpBSimDebugSignaturesScript.py +++ b/Ghidra/Features/BSim/ghidra_scripts/DumpBSimDebugSignaturesScript.py @@ -16,6 +16,7 @@ # Generate the BSim signature for the function at the current address, # then dump the signature hashes and debug information to the console # @category: BSim.python +# @runtime Jython import ghidra.app.decompiler.DecompInterface as DecompInterface import ghidra.app.decompiler.DecompileOptions as DecompileOptions diff --git a/Ghidra/Features/BSim/ghidra_scripts/DumpBSimSignaturesScript.py b/Ghidra/Features/BSim/ghidra_scripts/DumpBSimSignaturesScript.py index c1189492cc..15114469f3 100755 --- a/Ghidra/Features/BSim/ghidra_scripts/DumpBSimSignaturesScript.py +++ b/Ghidra/Features/BSim/ghidra_scripts/DumpBSimSignaturesScript.py @@ -16,6 +16,7 @@ # Generate the BSim signature for the function at the current address, then dump the # signature hashes to the console # @category: BSim.python +# @runtime Jython import ghidra.app.decompiler.DecompInterface as DecompInterface import ghidra.app.decompiler.DecompileOptions as DecompileOptions diff --git a/Ghidra/Features/BSim/ghidra_scripts/ExampleOverviewQueryScript.py b/Ghidra/Features/BSim/ghidra_scripts/ExampleOverviewQueryScript.py index 0e3877d167..c92ee8603b 100755 --- a/Ghidra/Features/BSim/ghidra_scripts/ExampleOverviewQueryScript.py +++ b/Ghidra/Features/BSim/ghidra_scripts/ExampleOverviewQueryScript.py @@ -15,6 +15,7 @@ ## # Example of how to perform an overview query in a script # @category BSim.python +# @runtime Jython import ghidra.features.bsim.query.facade.SFOverviewInfo as SFOverviewInfo import ghidra.features.bsim.query.facade.SimilarFunctionQueryService as SimilarFunctionQueryService diff --git a/Ghidra/Features/BSim/ghidra_scripts/GenerateSignatures.py b/Ghidra/Features/BSim/ghidra_scripts/GenerateSignatures.py index 4cbc1edbbb..e518e776b7 100755 --- a/Ghidra/Features/BSim/ghidra_scripts/GenerateSignatures.py +++ b/Ghidra/Features/BSim/ghidra_scripts/GenerateSignatures.py @@ -15,6 +15,7 @@ ## #Generate signatures for every function in the current program and write them to an XML file in a user-specified directory #@category BSim.python +#@runtime Jython import java.lang.System as System import java.io.File as File diff --git a/Ghidra/Features/BSim/ghidra_scripts/QueryFunction.py b/Ghidra/Features/BSim/ghidra_scripts/QueryFunction.py index 9393b795dd..a57e4f9918 100755 --- a/Ghidra/Features/BSim/ghidra_scripts/QueryFunction.py +++ b/Ghidra/Features/BSim/ghidra_scripts/QueryFunction.py @@ -15,6 +15,7 @@ ## # Example of performing a BSim query on a single function # @category BSim.python +# @runtime Jython import ghidra.features.bsim.query.BSimClientFactory as BSimClientFactory import ghidra.features.bsim.query.GenSignatures as GenSignatures diff --git a/Ghidra/Features/Base/ghidra_scripts/LocateMemoryAddressesForFileOffset.py b/Ghidra/Features/Base/ghidra_scripts/LocateMemoryAddressesForFileOffset.py index 74cfec9e2d..19b25d1d46 100644 --- a/Ghidra/Features/Base/ghidra_scripts/LocateMemoryAddressesForFileOffset.py +++ b/Ghidra/Features/Base/ghidra_scripts/LocateMemoryAddressesForFileOffset.py @@ -19,6 +19,7 @@ #Print the file offset as a Ghidra comment at the memory address in the Ghidra Listing #If multiple addresses are located, then print the addresses to the console (do not set a Ghidra comment) # @category Examples +# @runtime Jython import sys from ghidra.program.model.address import Address diff --git a/Ghidra/Features/Base/ghidra_scripts/RecursiveStringFinder.py b/Ghidra/Features/Base/ghidra_scripts/RecursiveStringFinder.py index 91bd2c455b..efe9182bfe 100644 --- a/Ghidra/Features/Base/ghidra_scripts/RecursiveStringFinder.py +++ b/Ghidra/Features/Base/ghidra_scripts/RecursiveStringFinder.py @@ -15,6 +15,7 @@ ## #Given a function, find all strings used within all called funtions. # @category: Strings +# @runtime Jython # Handles only functions, not subroutines, as of now. Hopefully this will change later diff --git a/Ghidra/Features/Base/ghidra_scripts/RunYARAFromGhidra.py b/Ghidra/Features/Base/ghidra_scripts/RunYARAFromGhidra.py index 0977421ea6..5e142ba9fb 100644 --- a/Ghidra/Features/Base/ghidra_scripts/RunYARAFromGhidra.py +++ b/Ghidra/Features/Base/ghidra_scripts/RunYARAFromGhidra.py @@ -24,6 +24,7 @@ # generate the original bytes of the imported file and asks the user to provide a filename to store the bytes. YARA then runs on that file. #@category Memory.YARA +#@runtime Jython import os.path import sys diff --git a/Ghidra/Features/Base/ghidra_scripts/mark_in_out.py b/Ghidra/Features/Base/ghidra_scripts/mark_in_out.py index 8e9020ea18..40b2b3070e 100644 --- a/Ghidra/Features/Base/ghidra_scripts/mark_in_out.py +++ b/Ghidra/Features/Base/ghidra_scripts/mark_in_out.py @@ -15,6 +15,7 @@ ## # Sets up IOPORT IN/OUT references for the Program #@category Instructions +#@runtime Jython # Before running this script, you should have created an OVERLAY memory # space called IOMEM, starting at address 0, size 0x10000. # diff --git a/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm b/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm index 84eb0f34f1..7e524b0576 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm @@ -127,7 +127,7 @@
-+ +The tag indicates the top-level menu path. Path levels are delimited using the "." +
This tag indicates the top-level menu path. Path levels are delimited using the "." character. A mnemonic can be defined by adding an ampersand ("&") in front of the mnemonic key. Ampersands can be escaped by adding another ampersand ("&&").
@@ -149,7 +149,20 @@ then in the Ghidra installation. If the image does not exists, a toolbar button will be created using the default Ghidra image.
- For example, "@toolbar myScriptImage.gif".
+ For example, "@toolbar myScriptImage.gif".
+ +
@runtime
+diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java index 15fa34b459..b47ad3ab4c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java @@ -370,7 +370,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { } GhidraScriptProvider provider = GhidraScriptUtil.getProvider(script); - SaveDialog dialog = new SaveDialog(getComponent(), "Rename Script", this, script, + SaveDialog dialog = new SaveDialog(getComponent(), "Rename Script", this, script, provider, actionManager.getRenameHelpLocation()); if (dialog.isCancelled()) { plugin.getTool().setStatusInfo("User cancelled rename."); @@ -580,7 +580,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { ResourceFile newFile = GhidraScriptUtil.createNewScript(provider, new ResourceFile(userScriptsDir), getScriptDirectories()); SaveDialog dialog = new SaveNewScriptDialog(getComponent(), "New Script", this, newFile, - actionManager.getNewHelpLocation()); + provider, actionManager.getNewHelpLocation()); if (dialog.isCancelled()) { plugin.getTool().setStatusInfo("User cancelled creating a new script."); return; @@ -676,6 +676,11 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter { private GhidraScript getScriptInstance(ResourceFile scriptFile, ConsoleService console) { String scriptName = scriptFile.getName(); GhidraScriptProvider provider = GhidraScriptUtil.getProvider(scriptFile); + if (provider == null) { + console.addErrorMessage("", + "Could not find a compatible script provider for: " + scriptName); + return null; + } try { return provider.getScriptInstance(scriptFile, console.getStdErr()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java index 6ebee30d2e..cf3adad9eb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java @@ -561,8 +561,8 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider { private boolean saveAs() { HelpLocation help = new HelpLocation(plugin.getName(), saveAction.getName()); - SaveDialog dialog = - new SaveDialog(getComponent(), "Save Script", provider, scriptSourceFile, help); + SaveDialog dialog = new SaveDialog(getComponent(), "Save Script", provider, + scriptSourceFile, GhidraScriptUtil.getProvider(scriptSourceFile), help); if (dialog.isCancelled()) { return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptTableModel.java index ac44f948d6..606507fff2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptTableModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptTableModel.java @@ -66,6 +66,8 @@ class GhidraScriptTableModel extends GDynamicColumnTableModelThis tag indicates which Ghidra script runtime environment is required to execute the + script. It allows for greater control when more than one Ghidra script runtime environment + uses the same script file extension. If left unspecified, the first Ghidra script runtime + environment that matches the script's extension will be used.
+
+ For example, specify "@runtime Jython" if the script is targetted for a Jython 2 + runtime environment rather than a Python 3 runtime environment.
+ * In Python this is a triple single quote sequence, "'''". + * + * @return the Pattern for Python block comment openings + */ + @Override + public Pattern getBlockCommentStart() { + return BLOCK_COMMENT; + } + + /** + * {@inheritDoc} + *
+ * In Python this is a triple single quote sequence, "'''".
+ *
+ * @return the Pattern for Python block comment openings
+ */
+ @Override
+ public Pattern getBlockCommentEnd() {
+ return BLOCK_COMMENT;
+ }
+
+ @Override
+ public String getCommentCharacter() {
+ return "#";
+ }
+
+ @Override
+ protected String getCertifyHeaderStart() {
+ return "## ###";
+ }
+
+ @Override
+ protected String getCertificationBodyPrefix() {
+ return "#";
+ }
+
+ @Override
+ protected String getCertifyHeaderEnd() {
+ return "##";
+ }
+
+ @Override
+ public String getExtension() {
+ return ".py";
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java
index a68f214945..8d0a5cb1c9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProvider.java
@@ -105,6 +105,19 @@ public abstract class GhidraScriptProvider
public abstract void createNewScript(ResourceFile newScript, String category)
throws IOException;
+ /**
+ * Returns an optional runtime environment name of a {@link GhidraScriptProvider} that scripts
+ * can specify they require to run under. Useful for when more than one
+ * {@link GhidraScriptProvider} uses the same file extension.
+ *
+ * @return an optional runtime environment name of a {@link GhidraScriptProvider} that scripts
+ * can specify they require to run under (could be null if there is no requirement)
+ * @see ScriptInfo#AT_RUNTIME
+ */
+ public String getRuntimeEnvironmentName() {
+ return null;
+ }
+
/**
* Returns a Pattern that matches block comment openings.
*
@@ -161,6 +174,9 @@ public abstract class GhidraScriptProvider
if (metadataItem.equals(ScriptInfo.AT_CATEGORY)) {
writer.print(category);
}
+ else if (metadataItem.equals(ScriptInfo.AT_RUNTIME)) {
+ writer.print(getRuntimeEnvironmentName());
+ }
writer.println("");
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
index 542097febf..180b024e29 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
@@ -276,29 +276,31 @@ public class GhidraScriptUtil {
}
/**
- * Returns a list of all Ghidra script providers
+ * Returns a list of all supported Ghidra script providers
*
- * @return a list of all Ghidra script providers
+ * @return a list of all supported Ghidra script providers
*/
// Note: this method is synchronized so that two threads do not try to create the list when null
public static synchronized List
- * In Jython this is a triple single quote sequence, "'''".
- *
- * @return the Pattern for Jython block comment openings
- */
- @Override
- public Pattern getBlockCommentStart() {
- return BLOCK_COMMENT;
- }
-
- /**
- * {@inheritDoc}
- *
- *
- * In Jython this is a triple single quote sequence, "'''".
- *
- * @return the Pattern for Jython block comment openings
- */
- @Override
- public Pattern getBlockCommentEnd() {
- return BLOCK_COMMENT;
- }
-
- @Override
- public String getCommentCharacter() {
- return "#";
- }
-
- @Override
- protected String getCertifyHeaderStart() {
- return "## ###";
- }
-
- @Override
- protected String getCertificationBodyPrefix() {
- return "#";
- }
-
- @Override
- protected String getCertifyHeaderEnd() {
- return "##";
- }
+/**
+ * A {@link GhidraScriptProvider} used to run Jython scripts
+ */
+@ExtensionPointProperties(priority = 1000) // Enforce high priority so Jython is the default Python provider
+public class JythonScriptProvider extends AbstractPythonScriptProvider {
@Override
public String getDescription() {
@@ -87,8 +33,8 @@ public class JythonScriptProvider extends GhidraScriptProvider {
}
@Override
- public String getExtension() {
- return ".py";
+ public String getRuntimeEnvironmentName() {
+ return "Jython";
}
@Override
@@ -105,4 +51,5 @@ public class JythonScriptProvider extends GhidraScriptProvider {
throw new GhidraScriptLoadException(e);
}
}
+
}
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
index 0101856efb..f7ccf0cc71 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
@@ -82,7 +82,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
scriptDirs.add(new ResourceFile("/User/home/ghidra_scripts"));
SaveDialog dialog = new SaveDialog(tool.getToolFrame(), "Save Script", provider,
- scriptDirs, scriptFile, helpLocation);
+ scriptDirs, scriptFile, new JavaScriptProvider(), helpLocation);
tool.showDialog(dialog);
}, false);
@@ -208,7 +208,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
scriptDirs.add(new ResourceFile("/User/home/ghidra_scripts"));
SaveDialog dialog = new SaveDialog(tool.getToolFrame(), "Rename Script", provider,
- scriptDirs, scriptFile, helpLocation);
+ scriptDirs, scriptFile, new JavaScriptProvider(), helpLocation);
tool.showDialog(dialog);
}, false);
").append(space).append(escapeHTML(getName())).append("
");
@@ -529,6 +566,8 @@ public class ScriptInfo {
buffer.append(HTML_NEW_LINE);
buffer.append(space).append(htmlMenuPath);
buffer.append(HTML_NEW_LINE);
+ buffer.append(space).append(htmlRuntime);
+ buffer.append(HTML_NEW_LINE);
buffer.append(HTML_NEW_LINE);
return wrapAsHTML(buffer.toString());
}
@@ -560,7 +599,7 @@ public class ScriptInfo {
* @return true if the script either has compiler errors, or is a duplicate
*/
public boolean hasErrors() {
- return isCompileErrors() || isDuplicate();
+ return isCompileErrors() || isDuplicate() || hasUnsupportedProvider();
}
/**
@@ -575,6 +614,10 @@ public class ScriptInfo {
return "Script is a duplicate of another script";
}
+ if (hasUnsupportedProvider()) {
+ return "Script's @runtime tag specifies an unsupported runtime environment";
+ }
+
return null;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/UnsupportedScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/UnsupportedScriptProvider.java
new file mode 100644
index 0000000000..bf9720c20b
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/UnsupportedScriptProvider.java
@@ -0,0 +1,97 @@
+/* ###
+ * 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.io.IOException;
+import java.io.PrintWriter;
+import java.util.regex.Pattern;
+
+import generic.jar.ResourceFile;
+
+/**
+ * A stub provider for unsupported scripts. These will typically be scripts with supported
+ * extensions but unsupported {@link ScriptInfo#AT_RUNTIME} tags.
+ */
+public class UnsupportedScriptProvider extends GhidraScriptProvider {
+
+ private GhidraScriptProvider baseProvider;
+
+ public UnsupportedScriptProvider() {
+ // Necessary for instantiation from the ClassSearcher
+ }
+
+ /**
+ * Creates a new {@link UnsupportedScriptProvider} that is derived from the given base provider.
+ * The base provider is any provider with a compatible extension, but without the required
+ * {@link ScriptInfo#AT_RUNTIME} tag.
+ *
+ * @param baseProvider The base {@link GhidraScriptProvider}
+ */
+ public UnsupportedScriptProvider(GhidraScriptProvider baseProvider) {
+ this.baseProvider = baseProvider;
+ }
+
+ @Override
+ public String getDescription() {
+ return "