GP-4706: GhidraScripts can now use @runtime to specify which GhidraScriptProvider to use when multiple expect the same script file extension (e.g., '.py')

This commit is contained in:
Ryan Kurtz 2024-06-28 13:26:24 -04:00
parent 5ab72bf4f2
commit 420eb767e8
38 changed files with 438 additions and 93 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
#

View File

@ -127,7 +127,7 @@
</P>
<BLOCKQUOTE>
<P>The tag indicates the top-level menu path. Path levels are delimited using the "."
<P>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 ("&&"). </P>
@ -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 <IMG src="images/core.png" alt=""> image.<BR>
<BR>
For example, <TT>"@toolbar myScriptImage.gif"</TT>.<BR>
For example, <TT>"@toolbar myScriptImage.gif"</TT>.<BR>
</P>
</BLOCKQUOTE>
<P><CODE><B>@runtime</B></CODE></P>
<BLOCKQUOTE>
<P>This 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.<BR>
<BR>
For example, specify <TT>"@runtime Jython"</TT> if the script is targetted for a Jython 2
runtime environment rather than a Python 3 runtime environment.
</P>
</BLOCKQUOTE>
</BLOCKQUOTE>

View File

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

View File

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

View File

@ -66,6 +66,8 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
descriptor.addVisibleColumn(new CategoryColumn());
descriptor.addHiddenColumn(new CreatedColumn());
descriptor.addVisibleColumn(new ModifiedColumn());
descriptor.addHiddenColumn(new RuntimeColumn());
descriptor.addHiddenColumn(new ProviderColumn());
return descriptor;
}
@ -298,7 +300,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
public Icon getValue(ResourceFile rowObject, Settings settings, Object data,
ServiceProvider sp) throws IllegalArgumentException {
ScriptInfo info = infoManager.getExistingScriptInfo(rowObject);
if (info.isCompileErrors() || info.isDuplicate()) {
if (info.hasErrors()) {
return ERROR_IMG;
}
return info.getToolBarImage(true);
@ -541,6 +543,61 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
}
}
private class RuntimeColumn extends AbstractDynamicTableColumn<ResourceFile, String, Object> {
private Comparator<String> comparator = new CaseInsensitiveDuplicateStringComparator();
@Override
public Comparator<String> getComparator() {
return comparator;
}
@Override
public String getColumnName() {
return "Runtime";
}
@Override
public String getValue(ResourceFile rowObject, Settings settings, Object data,
ServiceProvider sp) throws IllegalArgumentException {
return infoManager.getExistingScriptInfo(rowObject).getRuntimeEnvironmentName();
}
@Override
public int getColumnPreferredWidth() {
return 100;
}
}
private class ProviderColumn extends AbstractDynamicTableColumn<ResourceFile, String, Object> {
private Comparator<String> comparator = new CaseInsensitiveDuplicateStringComparator();
@Override
public Comparator<String> getComparator() {
return comparator;
}
@Override
public String getColumnName() {
return "Runtime Provider";
}
@Override
public String getValue(ResourceFile rowObject, Settings settings, Object data,
ServiceProvider sp) throws IllegalArgumentException {
return infoManager.getExistingScriptInfo(rowObject)
.getProvider()
.getClass()
.getSimpleName();
}
@Override
public int getColumnPreferredWidth() {
return 100;
}
}
private class DateRenderer extends AbstractGColumnRenderer<Date> {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {

View File

@ -47,9 +47,9 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
private boolean cancelled;
SaveDialog(Component parent, String title, GhidraScriptComponentProvider componentProvider,
ResourceFile scriptFile, HelpLocation help) {
ResourceFile scriptFile, GhidraScriptProvider scriptProvider, HelpLocation help) {
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(),
scriptFile, help);
scriptFile, scriptProvider, help);
}
/**
@ -60,15 +60,16 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
* @param componentProvider the provider
* @param scriptDirs list of directories to give as options when saving
* @param scriptFile the default save location
* @param scriptProvider the {@link GhidraScriptProvider}
* @param help contextual help, e.g. for rename or save
*/
public SaveDialog(Component parent, String title,
GhidraScriptComponentProvider componentProvider, List<ResourceFile> scriptDirs,
ResourceFile scriptFile, HelpLocation help) {
ResourceFile scriptFile, GhidraScriptProvider scriptProvider, HelpLocation help) {
super(title, true, true, true, false);
this.componentProvider = componentProvider;
this.provider = GhidraScriptUtil.getProvider(scriptFile);
this.provider = scriptProvider;
this.scriptFile = scriptFile;
this.paths = new ArrayList<>(scriptDirs);

View File

@ -19,14 +19,15 @@ import java.awt.Component;
import java.io.File;
import generic.jar.ResourceFile;
import ghidra.app.script.GhidraScriptProvider;
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);
GhidraScriptProvider scriptProvider, HelpLocation help) {
super(parent, title, componentProvider, scriptFile, scriptProvider, help);
}
/**

View File

@ -0,0 +1,99 @@
/* ###
* 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.*;
import java.util.regex.Pattern;
import generic.jar.ResourceFile;
/**
* An abstract {@link GhidraScriptProvider} used to provide common functionality to different
* types of Python script implementations
*/
public abstract class AbstractPythonScriptProvider extends GhidraScriptProvider {
private static final Pattern BLOCK_COMMENT = Pattern.compile("'''");
@Override
public abstract String getDescription();
@Override
public abstract String getRuntimeEnvironmentName();
@Override
public abstract GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
throws GhidraScriptLoadException;
@Override
public void createNewScript(ResourceFile newScript, String category) throws IOException {
try (PrintWriter writer = new PrintWriter(new FileWriter(newScript.getFile(false)))) {
writeHeader(writer, category);
writer.println("");
writeBody(writer);
writer.println("");
}
}
/**
* {@inheritDoc}
* <p>
* 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}
* <p>
* 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";
}
}

View File

@ -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("");
}

View File

@ -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<GhidraScriptProvider> getProviders() {
if (providers == null) {
List<GhidraScriptProvider> newProviders =
new ArrayList<>(ClassSearcher.getInstances(GhidraScriptProvider.class));
Collections.sort(newProviders);
providers = newProviders;
providers = ClassSearcher.getInstances(GhidraScriptProvider.class)
.stream()
.filter(p -> !(p instanceof UnsupportedScriptProvider))
.sorted()
.toList();
}
return providers;
}
/**
* Returns the corresponding Ghidra script providers
* for the specified script file.
* Returns the corresponding Ghidra script provider for the specified script file.
*
* @param scriptFile the script file
* @return the Ghidra script provider
* @return the Ghidra script provider or {@link UnsupportedScriptProvider} if the script file
* does not exist or no provider matches
*/
public static GhidraScriptProvider getProvider(ResourceFile scriptFile) {
return findProvider(scriptFile.getName());
return findProvider(scriptFile);
}
/**
@ -308,11 +310,46 @@ public class GhidraScriptUtil {
* @return true if a provider exists that can process the specified file
*/
public static boolean hasScriptProvider(ResourceFile scriptFile) {
return findProvider(scriptFile.getName()) != null;
return findProvider(scriptFile) != null;
}
/**
* Find the provider whose extension matches the given filename extension.
* Find the first provider whose extension matches the given file's extension and whose
* {@link ScriptInfo#AT_RUNTIME} matches
*
* @param scriptFile the script file (not guaranteed to exist if this method is called because
* the script manager is creating a new script and all it has to go off of initially is
* the desired file extension...in this case there will not be a @runtime tag yet)
* @return the matching provider or null if no provider matches
*/
private static GhidraScriptProvider findProvider(ResourceFile scriptFile) {
GhidraScriptProvider baseProvider = null;
String fileName = scriptFile.getName().toLowerCase();
for (GhidraScriptProvider provider : getProviders()) {
String extension = provider.getExtension().toLowerCase();
if (fileName.endsWith(extension)) {
baseProvider = provider;
if (!scriptFile.exists()) {
// Use UnsupportedScriptProvider. The provider will be updated later when
// the file actually exists and we can properly look for an @runtime tag
// (or confirm that one is not defined)
break;
}
String runtime = new ScriptInfo(provider, scriptFile).getRuntimeEnvironmentName();
if (runtime == null ||
runtime.equalsIgnoreCase(provider.getRuntimeEnvironmentName())) {
return provider;
}
}
}
if (baseProvider != null) {
return new UnsupportedScriptProvider(baseProvider);
}
return null;
}
/**
* Find the first provider whose extension matches the given filename extension.
*
* @param fileName name of script file
* @return the first matching provider or null if no provider matches

View File

@ -69,6 +69,11 @@ public class JavaScriptProvider extends GhidraScriptProvider {
return ".java";
}
@Override
public String getRuntimeEnvironmentName() {
return "Java";
}
@Override
public boolean deleteScript(ResourceFile sourceFile) {
try {

View File

@ -46,12 +46,13 @@ public class ScriptInfo {
static final String AT_KEYBINDING = "@keybinding";
static final String AT_MENUPATH = "@menupath";
static final String AT_TOOLBAR = "@toolbar";
static final String AT_RUNTIME = "@runtime";
// omit from METADATA to avoid pre-populating in new scripts
private static final String AT_IMPORTPACKAGE = "@importpackage";
public static final String[] METADATA =
{ AT_AUTHOR, AT_CATEGORY, AT_KEYBINDING, AT_MENUPATH, AT_TOOLBAR, };
{ AT_AUTHOR, AT_CATEGORY, AT_KEYBINDING, AT_MENUPATH, AT_TOOLBAR, AT_RUNTIME };
private GhidraScriptProvider provider;
private ResourceFile sourceFile;
@ -68,6 +69,7 @@ public class ScriptInfo {
private String toolbar;
private ImageIcon toolbarImage;
private String importpackage;
private String runtime;
/**
* Constructs a new script.
@ -94,6 +96,7 @@ public class ScriptInfo {
toolbarImage = null;
importpackage = null;
keybindingErrorMessage = null;
runtime = null;
}
/**
@ -129,6 +132,26 @@ public class ScriptInfo {
return author;
}
/**
* Returns the name of the required runtime environment
* @return the name of the required runtime environment
* @see GhidraScriptProvider#getRuntimeEnvironmentName()
*/
public String getRuntimeEnvironmentName() {
parseHeader();
return runtime;
}
/**
* Returns the {@link GhidraScriptProvider} currently associated with the script
* @return The {@link GhidraScriptProvider} currently associated with the script
*/
public GhidraScriptProvider getProvider() {
parseHeader();
provider = GhidraScriptUtil.getProvider(sourceFile);
return provider;
}
/**
* Returns true if the script has compile errors.
* @return true if the script has compile errors
@ -163,6 +186,16 @@ public class ScriptInfo {
this.isDuplicate = isDuplicate;
}
/**
* Returns true if this script has an {@link UnsupportedScriptProvider}. This will typically
* happen when a script defines a wrong {@link ScriptInfo#AT_RUNTIME} tag.
*
* @return True if this script has an {@link UnsupportedScriptProvider}; otherwise, false
*/
public boolean hasUnsupportedProvider() {
return provider instanceof UnsupportedScriptProvider;
}
/**
* Returns the script description.
* @return the script description
@ -329,6 +362,9 @@ public class ScriptInfo {
else if (line.startsWith(AT_IMPORTPACKAGE)) {
importpackage = getTagValue(AT_IMPORTPACKAGE, line);
}
else if (line.startsWith(AT_RUNTIME)) {
runtime = getTagValue(AT_RUNTIME, line);
}
}
catch (Exception e) {
Msg.debug(this, "Unexpected exception reading script metadata " + "line: " + line, e);
@ -514,6 +550,7 @@ public class ScriptInfo {
String htmlCategory = bold("Category:") + space + escapeHTML(toString(category));
String htmlKeyBinding = bold("Key Binding:") + space + getKeybindingToolTip();
String htmlMenuPath = bold("Menu Path:") + space + escapeHTML(toString(menupath));
String htmlRuntime = bold("Runtime Environment:") + space + escapeHTML(toString(runtime));
StringBuilder buffer = new StringBuilder();
buffer.append("<h3>").append(space).append(escapeHTML(getName())).append("</h3>");
@ -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;
}

View File

@ -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 "<unsupported>";
}
@Override
public String getExtension() {
return baseProvider.getExtension();
}
@Override
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
throws GhidraScriptLoadException {
throw new GhidraScriptLoadException("Script is not supported.");
}
@Override
public void createNewScript(ResourceFile newScript, String category) throws IOException {
// Do nothing
}
@Override
public String getCommentCharacter() {
return baseProvider.getCommentCharacter();
}
@Override
public Pattern getBlockCommentStart() {
return baseProvider.getBlockCommentStart();
}
@Override
public Pattern getBlockCommentEnd() {
return baseProvider.getBlockCommentEnd();
}
@Override
protected String getCertifyHeaderStart() {
return baseProvider.getCertifyHeaderStart();
}
@Override
protected String getCertificationBodyPrefix() {
return baseProvider.getCertificationBodyPrefix();
}
@Override
protected String getCertifyHeaderEnd() {
return baseProvider.getCertifyHeaderEnd();
}
}

View File

@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@runtime Jython
from ghidra.program.model.address.Address import *

View File

@ -22,6 +22,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@runtime Jython
from ghidra.framework.model import DomainFile
from ghidra.framework.model import DomainFolder

View File

@ -21,6 +21,7 @@
# NOTE: Script will only process unversioned and checked-out files.
#@category Examples.Python
#@runtime Jython
from ghidra.app.script import GhidraState
from ghidra.framework.model import *

View File

@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@runtime Jython
runScript("HelloWorldScript.java")
runScript("HelloWorldPopupScript.java")

View File

@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@runtime Jython
from ghidra.app.util.datatype import DataTypeSelectionDialog
from ghidra.framework.plugintool import PluginTool

View File

@ -19,6 +19,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@runtime Jython
from ghidra.app.plugin.core.colorizer import ColorizingService
from ghidra.app.script import GhidraScript

View File

@ -20,6 +20,7 @@
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@runtime Jython
from time import *
import java.util.Calendar

View File

@ -24,6 +24,7 @@
# Omitting the address space or memory region specifier from the address will result in the function or label being created in the default address space.
# @author unkown; edited by matedealer <git@matedealer.de>
# @category Data
# @runtime Jython
#
from ghidra.program.model.symbol.SourceType import *

View File

@ -14,6 +14,7 @@
# limitations under the License.
##
# Prints out all the functions in the program that have a non-zero stack purge size
# @runtime Jython
for func in currentProgram.getFunctionManager().getFunctions(currentProgram.evaluateAddress("0"), 1):
if func.getStackPurgeSize() != 0:

View File

@ -18,7 +18,8 @@
# DISCLAIMER: This is a recreation of a Java Ghidra script for example
# use only. Please run the Java version in a production environment.
#@category Examples.Python
#@category Examples.Python
#@runtime Jython
from ghidra.framework.options import Options
from ghidra.framework.plugintool import PluginTool

View File

@ -15,6 +15,7 @@
##
# Example of being imported by a Ghidra Python script/module
# @category: Examples.Python
# @runtime Jython
# The following line will fail if this module is imported from external_module_caller.py,
# because only the script that gets directly launched by Ghidra inherits fields and methods

View File

@ -15,6 +15,7 @@
##
# Example of importing an external Ghidra Python module
# @category: Examples.Python
# @runtime Jython
# Import the external module that wants to access the Ghidra scripting API.
# NOTE: see external_module_callee.py for additional tips.

View File

@ -15,6 +15,7 @@
##
# Examples of basic Ghidra scripting in Python
# @category: Examples.Python
# @runtime Jython
# Get info about the current program
print

View File

@ -15,6 +15,7 @@
##
# Examples of Jython-specific functionality
# @category: Examples.Python
# @runtime Jython
# Using Java data structures from Jython
python_list = [1, 2, 3]

View File

@ -15,6 +15,7 @@
##
# Examples of basic Python
# @category: Examples.Python
# @runtime Jython
# Python data types
my_int = 32

View File

@ -15,71 +15,17 @@
*/
package ghidra.jython;
import java.io.*;
import java.util.regex.Pattern;
import java.io.PrintWriter;
import generic.jar.ResourceFile;
import ghidra.app.script.*;
import ghidra.util.classfinder.ExtensionPointProperties;
public class JythonScriptProvider extends GhidraScriptProvider {
private static final Pattern BLOCK_COMMENT = Pattern.compile("'''");
@Override
public void createNewScript(ResourceFile newScript, String category) throws IOException {
PrintWriter writer = new PrintWriter(new FileWriter(newScript.getFile(false)));
writeHeader(writer, category);
writer.println("");
writeBody(writer);
writer.println("");
writer.close();
}
/**
* {@inheritDoc}
*
* <p>
* 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}
*
* <p>
* 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);
}
}
}

View File

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