mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-28 05:20:21 +00:00
Merge remote-tracking branch 'origin/GP-2618_Dan_scriptErrorMessages--SQUASHED'
This commit is contained in:
commit
f1177763aa
|
@ -20,6 +20,7 @@ import java.awt.Color;
|
|||
import java.awt.event.MouseEvent;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
@ -1592,7 +1593,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
try {
|
||||
script = provider.getScriptInstance(sourceFile, writer);
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
catch (GhidraScriptLoadException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.awt.BorderLayout;
|
|||
import java.awt.Rectangle;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -671,14 +672,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
try {
|
||||
return provider.getScriptInstance(scriptFile, console.getStdErr());
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
console.addErrorMessage("", "Unable to access script: " + scriptName);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
console.addErrorMessage("", "Unable to instantiate script: " + scriptName);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
console.addErrorMessage("", "Unable to locate script class: " + scriptName);
|
||||
catch (GhidraScriptLoadException e) {
|
||||
console.addErrorMessage("", "Unable to load script: " + scriptName);
|
||||
console.addErrorMessage("", " detail: " + e.getMessage());
|
||||
}
|
||||
|
||||
// show the error icon
|
||||
|
@ -819,7 +815,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
|
||||
/*
|
||||
Unusual Algorithm
|
||||
|
||||
|
||||
The tree nodes represent categories, but do not contain nodes for individual
|
||||
scripts. We wish to remove any of the tree nodes that no longer represent script
|
||||
categories. (This can happen when a script is deleted or its category is changed.)
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* ###
|
||||
* 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 ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
* An exception for when a script provider cannot create a script instance
|
||||
*/
|
||||
public class GhidraScriptLoadException extends UsrException {
|
||||
/**
|
||||
* Construct an exception with a custom message and cause
|
||||
*
|
||||
* <p>
|
||||
* Note that the error message displayed to the user does not automatically include details from
|
||||
* the cause. The client must provide details from the cause in the message as needed.
|
||||
*
|
||||
* @param message the error message including details and possible remedies
|
||||
* @param cause the exception causing this one
|
||||
*/
|
||||
public GhidraScriptLoadException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception with a message
|
||||
*
|
||||
* @param message the error message including details and possible remedies
|
||||
*/
|
||||
public GhidraScriptLoadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an exception with a cause
|
||||
*
|
||||
* <p>
|
||||
* This will copy the cause's message into this exception's message.
|
||||
*
|
||||
* @param cause the exception causing this one
|
||||
*/
|
||||
public GhidraScriptLoadException(Throwable cause) {
|
||||
super(cause.getMessage(), cause);
|
||||
}
|
||||
}
|
|
@ -23,9 +23,11 @@ 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.
|
||||
*
|
||||
* A provider that can compile, interpret, load, etc., Ghidra Scripts from a given language.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> ALL GhidraScriptProvider CLASSES MUST END IN "ScriptProvider". If not, the
|
||||
* ClassSearcher will not find them.
|
||||
*/
|
||||
public abstract class GhidraScriptProvider
|
||||
implements ExtensionPoint, Comparable<GhidraScriptProvider> {
|
||||
|
@ -56,6 +58,7 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Deletes the script file and unloads the script from the script manager.
|
||||
*
|
||||
* @param scriptSource the script source file
|
||||
* @return true if the script was completely deleted and cleaned up
|
||||
*/
|
||||
|
@ -65,31 +68,36 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Returns a description for this type of script.
|
||||
*
|
||||
* @return a description for this type of script
|
||||
*/
|
||||
public abstract String getDescription();
|
||||
|
||||
/**
|
||||
* Returns the file extension for this type of script.
|
||||
*
|
||||
* <p>
|
||||
* For example, ".java" or ".py".
|
||||
*
|
||||
* @return the file extension for this type of script
|
||||
*/
|
||||
public abstract String getExtension();
|
||||
|
||||
/**
|
||||
* Returns a GhidraScript instance for the specified source file.
|
||||
*
|
||||
* @param sourceFile the source file
|
||||
* @param writer the print writer to write warning/error messages
|
||||
* @param writer the print writer to write warning/error messages. If the error prevents
|
||||
* success, throw an exception instead. The caller will print the error.
|
||||
* @return a GhidraScript instance for the specified source file
|
||||
* @throws ClassNotFoundException if the script class cannot be found
|
||||
* @throws InstantiationException if the construction of the script fails for some reason
|
||||
* @throws IllegalAccessException if the class constructor is not accessible
|
||||
* @throws GhidraScriptLoadException when the script instance cannot be created
|
||||
*/
|
||||
public abstract GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException;
|
||||
throws GhidraScriptLoadException;
|
||||
|
||||
/**
|
||||
* Creates a new script using the specified file.
|
||||
*
|
||||
* @param newScript the new script file
|
||||
* @param category the script category
|
||||
* @throws IOException if an error occurs writing the file
|
||||
|
@ -99,7 +107,10 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Returns a Pattern that matches block comment openings.
|
||||
*
|
||||
* <p>
|
||||
* If block comments are not supported by this provider, then this returns null.
|
||||
*
|
||||
* @return the Pattern for block comment openings, null if block comments are not supported
|
||||
*/
|
||||
public Pattern getBlockCommentStart() {
|
||||
|
@ -108,7 +119,10 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Returns a Pattern that matches block comment closings.
|
||||
*
|
||||
* <p>
|
||||
* If block comments are not supported by this provider, then this returns null.
|
||||
*
|
||||
* @return the Pattern for block comment closings, null if block comments are not supported
|
||||
*/
|
||||
public Pattern getBlockCommentEnd() {
|
||||
|
@ -117,14 +131,20 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Returns the comment character.
|
||||
*
|
||||
* <p>
|
||||
* For example, "//" or "#".
|
||||
*
|
||||
* @return the comment character
|
||||
*/
|
||||
public abstract String getCommentCharacter();
|
||||
|
||||
/**
|
||||
* Writes the script header.
|
||||
* Writes the script header.
|
||||
*
|
||||
* <p>
|
||||
* Include a place holder for each meta-data item.
|
||||
*
|
||||
* @param writer the print writer
|
||||
* @param category the default category
|
||||
*/
|
||||
|
@ -150,6 +170,7 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Writes the script body template.
|
||||
*
|
||||
* @param writer the print writer
|
||||
*/
|
||||
protected void writeBody(PrintWriter writer) {
|
||||
|
@ -159,8 +180,9 @@ public abstract class GhidraScriptProvider
|
|||
/**
|
||||
* Fixup a script name for searching in script directories.
|
||||
*
|
||||
* <p>This method is part of a poorly specified behavior that is due for future amendment,
|
||||
* see {@link GhidraScriptUtil#fixupName(String)}.
|
||||
* <p>
|
||||
* This method is part of a poorly specified behavior that is due for future amendment, see
|
||||
* {@link GhidraScriptUtil#fixupName(String)}.
|
||||
*
|
||||
* @param scriptName the name of the script, must end with this provider's extension
|
||||
* @return a (relative) file path to the corresponding script
|
||||
|
@ -172,6 +194,7 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Return the start of certification header line if this file type is subject to certification.
|
||||
*
|
||||
* @return start of certification header or null if not supported
|
||||
*/
|
||||
protected String getCertifyHeaderStart() {
|
||||
|
@ -179,8 +202,9 @@ public abstract class GhidraScriptProvider
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the prefix for each certification header body line if this file is subject to
|
||||
* Return the prefix for each certification header body line if this file is subject to
|
||||
* certification.
|
||||
*
|
||||
* @return certification header body prefix or null if not supported
|
||||
*/
|
||||
protected String getCertificationBodyPrefix() {
|
||||
|
@ -189,6 +213,7 @@ public abstract class GhidraScriptProvider
|
|||
|
||||
/**
|
||||
* Return the end of certification header line if this file type is subject to certification.
|
||||
*
|
||||
* @return end of certification header or null if not supported
|
||||
*/
|
||||
protected String getCertifyHeaderEnd() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.script;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -26,6 +27,9 @@ import ghidra.app.plugin.core.osgi.*;
|
|||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* The provider for Ghidra Scripts written in Java
|
||||
*/
|
||||
public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
private static final Pattern BLOCK_COMMENT_START = Pattern.compile("/\\*");
|
||||
private static final Pattern BLOCK_COMMENT_END = Pattern.compile("\\*/");
|
||||
|
@ -33,14 +37,16 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
private final BundleHost bundleHost;
|
||||
|
||||
/**
|
||||
* Create a new {@link JavaScriptProvider} associated with the current bundle host used by scripting.
|
||||
* Create a new {@link JavaScriptProvider} associated with the current bundle host used by
|
||||
* scripting.
|
||||
*/
|
||||
public JavaScriptProvider() {
|
||||
bundleHost = GhidraScriptUtil.getBundleHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link GhidraSourceBundle} containing the given source file, assuming it already exists.
|
||||
* Get the {@link GhidraSourceBundle} containing the given source file, assuming it already
|
||||
* exists.
|
||||
*
|
||||
* @param sourceFile the source file
|
||||
* @return the bundle
|
||||
|
@ -80,35 +86,53 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
|
||||
@Override
|
||||
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
throws GhidraScriptLoadException {
|
||||
try {
|
||||
Class<?> clazz = loadClass(sourceFile, writer);
|
||||
Object object;
|
||||
object = clazz.getDeclaredConstructor().newInstance();
|
||||
|
||||
if (object instanceof GhidraScript) {
|
||||
GhidraScript script = (GhidraScript) object;
|
||||
if (GhidraScript.class.isAssignableFrom(clazz)) {
|
||||
GhidraScript script = (GhidraScript) clazz.getDeclaredConstructor().newInstance();
|
||||
script.setSourceFile(sourceFile);
|
||||
return script;
|
||||
}
|
||||
|
||||
String message = "Not a valid Ghidra script: " + sourceFile.getName();
|
||||
writer.println(message);
|
||||
Msg.error(this, message);
|
||||
return null; // class is not GhidraScript
|
||||
|
||||
throw new GhidraScriptLoadException(
|
||||
"Ghidra scripts in Java must extend " + GhidraScript.class.getName() + ". " +
|
||||
sourceFile.getName() + " does not.");
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw e;
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new GhidraScriptLoadException("The class could not be found. " +
|
||||
"It must be the public class of the .java file: " + e.getMessage(), e);
|
||||
}
|
||||
catch (NoClassDefFoundError e) {
|
||||
throw new GhidraScriptLoadException("The class could not be found or loaded, " +
|
||||
"perhaps due to a previous initialization error: " + e.getMessage(), e);
|
||||
}
|
||||
catch (ExceptionInInitializerError e) {
|
||||
throw new GhidraScriptLoadException(
|
||||
"Error during class initialization: " + e.getException(), e.getException());
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new GhidraScriptLoadException(
|
||||
"Error during class construction: " + e.getTargetException(),
|
||||
e.getTargetException());
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new GhidraScriptLoadException(
|
||||
"The default constructor does not exist: " + e.getMessage(), e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new GhidraScriptLoadException(
|
||||
"The class or its default constructor is not accessible: " + e.getMessage(), e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ClassNotFoundException("", e);
|
||||
throw new GhidraScriptLoadException("Unexpected error: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate and build the {@link GhidraSourceBundle} containing {@code sourceFile}
|
||||
* then load the script's class from its class loader.
|
||||
* Activate and build the {@link GhidraSourceBundle} containing {@code sourceFile} then load the
|
||||
* script's class from its class loader.
|
||||
*
|
||||
* @param sourceFile the source file
|
||||
* @param writer the target for build messages
|
||||
|
@ -164,9 +188,10 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a Pattern that matches block comment openings.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* For Java this is "/*".
|
||||
* @return the Pattern for Java block comment openings
|
||||
*/
|
||||
@Override
|
||||
public Pattern getBlockCommentStart() {
|
||||
|
@ -174,9 +199,10 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a Pattern that matches block comment closings.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* In Java this is an asterisk followed by a forward slash.
|
||||
* @return the Pattern for Java block comment closings
|
||||
*/
|
||||
@Override
|
||||
public Pattern getBlockCommentEnd() {
|
||||
|
@ -204,14 +230,19 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* Fix script name for search in script directories, such as Java package parts in the name and
|
||||
* inner class names.
|
||||
*
|
||||
* Fix script name for search in script directories, such as Java package parts in the name and inner class names.
|
||||
* <p>
|
||||
* This method can handle names with '$' (inner classes) and names with '.' characters for
|
||||
* package separators
|
||||
*
|
||||
* <p>This method can handle names with '$' (inner classes) and names with '.'
|
||||
* characters for package separators
|
||||
*
|
||||
* <p>It is part of a poorly specified behavior that is due for future amendment,
|
||||
* see {@link GhidraScriptUtil#fixupName(String)}.
|
||||
* <p>
|
||||
* It is part of a poorly specified behavior that is due for future amendment, see
|
||||
* {@link GhidraScriptUtil#fixupName(String)}.
|
||||
*
|
||||
* @param scriptName the name of the script
|
||||
* @return the name as a '.java' file path (with '/'s and not '.'s)
|
||||
|
@ -227,5 +258,4 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
}
|
||||
return path + ".java";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,8 +35,7 @@ import ghidra.app.events.OpenProgramPluginEvent;
|
|||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
||||
import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.script.JavaScriptProvider;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.base.project.GhidraProject;
|
||||
import ghidra.framework.Application;
|
||||
|
@ -564,7 +563,7 @@ public class TestEnv {
|
|||
try {
|
||||
script = scriptProvider.getScriptInstance(resourceFile, writer);
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
catch (GhidraScriptLoadException e) {
|
||||
Msg.error(TestEnv.class, "Problem creating script", e);
|
||||
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ import java.io.*;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.script.GhidraScriptProvider;
|
||||
import ghidra.app.script.*;
|
||||
|
||||
public class PythonScriptProvider extends GhidraScriptProvider {
|
||||
|
||||
|
@ -37,8 +36,11 @@ public class PythonScriptProvider extends GhidraScriptProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a Pattern that matches block comment openings.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* In Python this is a triple single quote sequence, "'''".
|
||||
*
|
||||
* @return the Pattern for Python block comment openings
|
||||
*/
|
||||
@Override
|
||||
|
@ -47,8 +49,11 @@ public class PythonScriptProvider extends GhidraScriptProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a Pattern that matches block comment closings.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>
|
||||
* In Python this is a triple single quote sequence, "'''".
|
||||
*
|
||||
* @return the Pattern for Python block comment openings
|
||||
*/
|
||||
@Override
|
||||
|
@ -88,11 +93,16 @@ public class PythonScriptProvider extends GhidraScriptProvider {
|
|||
|
||||
@Override
|
||||
public GhidraScript getScriptInstance(ResourceFile sourceFile, PrintWriter writer)
|
||||
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
||||
throws GhidraScriptLoadException {
|
||||
|
||||
Class<?> clazz = Class.forName(PythonScript.class.getName());
|
||||
GhidraScript script = (GhidraScript) clazz.newInstance();
|
||||
script.setSourceFile(sourceFile);
|
||||
return script;
|
||||
try {
|
||||
Class<?> clazz = Class.forName(PythonScript.class.getName());
|
||||
GhidraScript script = (GhidraScript) clazz.getConstructor().newInstance();
|
||||
script.setSourceFile(sourceFile);
|
||||
return script;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new GhidraScriptLoadException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue