mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
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:
parent
5ab72bf4f2
commit
420eb767e8
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
#
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
@ -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("");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -69,6 +69,11 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
return ".java";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRuntimeEnvironmentName() {
|
||||
return "Java";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteScript(ResourceFile sourceFile) {
|
||||
try {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 *
|
||||
|
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -15,6 +15,7 @@
|
||||
##
|
||||
# Examples of basic Ghidra scripting in Python
|
||||
# @category: Examples.Python
|
||||
# @runtime Jython
|
||||
|
||||
# Get info about the current program
|
||||
print
|
||||
|
@ -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]
|
||||
|
@ -15,6 +15,7 @@
|
||||
##
|
||||
# Examples of basic Python
|
||||
# @category: Examples.Python
|
||||
# @runtime Jython
|
||||
|
||||
# Python data types
|
||||
my_int = 32
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user