mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-24 13:27:10 +08:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
* _Data Types_. Fixed the Data Type Preview to show the typedef data type before the typedef name. (GP-6858)
|
||||
* _Debugger:Agents_. Patched a possible out-of-memory vulnerability in TraceRmi. (GP-6718)
|
||||
* _Debugger:Listing_. Added __Disassemble as MIPS__:_:16e__ for the dynamic listing. (GP-6655)
|
||||
* _Framework_. Changed potentially unsafe calls to `Class.forName` to perform additional checks prior to instantiation. (GP-6717)
|
||||
* _Multi-User_. Increased the default value for serialization filter `maxarray` limit from 32,000 to 200,000. This was done to avoid related class serialization errors when communicating with the Ghidra Server for large repository database files. See `Ghidra/Framework/FileSystem/data/serialFilterREADME.md` - it may be necessary to specify an increased `maxarray` value if related serialization errors occur. (GP-6842)
|
||||
* _Multi-User_. Corrected memory leak issue related to Ghidra Server, affecting both client and server. Buffer compression logic failed to properly release system resources. (GP-6862)
|
||||
* _Multi-User:Merge_. Corrected bug in Property List Merge which could cause an exception. (GP-6854)
|
||||
@@ -19,6 +20,10 @@
|
||||
* _Processors_. Corrected issue with RISC-V attempting to write to constants. (GP-6849, Issue #9198)
|
||||
* _PyGhidra_. Handled a possible `NotADirectoryError` in `pyghidra_launcher.py`. (GP-6825)
|
||||
|
||||
### Notable API Changes
|
||||
* _BSim_. (GP-6866) The unused methods `FunctionDatabase.getConfigurationTemplates` and `FunctionDatabase.isConfigTemplate` have been removed.
|
||||
* _Framework_. (GP-6717) Added `ClassSearcher.forNameSafe` method.
|
||||
|
||||
# Ghidra 12.1 Change History (May 2026)
|
||||
|
||||
### New Features
|
||||
|
||||
+1
-56
@@ -52,7 +52,6 @@ import ghidra.util.layout.PairLayout;
|
||||
|
||||
public abstract class AbstractDebuggerParameterDialog<P> extends DialogComponentProvider
|
||||
implements PropertyChangeListener {
|
||||
static final String KEY_MEMORIZED_ARGUMENTS = "memorizedArguments";
|
||||
|
||||
public static class BigIntEditor extends PropertyEditorSupport {
|
||||
String asText = "";
|
||||
@@ -435,21 +434,7 @@ public abstract class AbstractDebuggerParameterDialog<P> extends DialogComponent
|
||||
}
|
||||
}
|
||||
|
||||
protected record NameTypePair(String name, Class<?> type) {
|
||||
public static NameTypePair fromString(String name) throws ClassNotFoundException {
|
||||
String[] parts = name.split(",", 2);
|
||||
if (parts.length != 2) {
|
||||
// This appears to be a bad assumption - empty fields results in solitary labels
|
||||
return new NameTypePair(parts[0], String.class);
|
||||
//throw new IllegalArgumentException("Could not parse name,type");
|
||||
}
|
||||
return new NameTypePair(parts[0], Class.forName(parts[1]));
|
||||
}
|
||||
|
||||
public final String encodeString() {
|
||||
return name + "," + type.getName();
|
||||
}
|
||||
}
|
||||
protected record NameTypePair(String name, Class<?> type) {}
|
||||
|
||||
private final BidiMap<P, PropertyEditor> paramEditors = new DualLinkedHashBidiMap<>();
|
||||
|
||||
@@ -722,46 +707,6 @@ public abstract class AbstractDebuggerParameterDialog<P> extends DialogComponent
|
||||
new ValStr<>(editor.getValue(), editor.getAsText()));
|
||||
}
|
||||
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
SaveState subState = new SaveState();
|
||||
for (Map.Entry<NameTypePair, ValStr<?>> ent : memorized.entrySet()) {
|
||||
NameTypePair ntp = ent.getKey();
|
||||
P param = parameters.get(ntp.name());
|
||||
if (param == null) {
|
||||
continue;
|
||||
}
|
||||
parameterSaveValue(param, subState, ntp.encodeString(), ent.getValue());
|
||||
}
|
||||
saveState.putSaveState(KEY_MEMORIZED_ARGUMENTS, subState);
|
||||
}
|
||||
|
||||
public void readConfigState(SaveState saveState) {
|
||||
/**
|
||||
* TODO: This method is defunct. It is only used by the DebuggerObjectsProvider, which is
|
||||
* now deprecated, but I suspect other providers intend to use this in the same way. If
|
||||
* those providers don't manually load/compute initial and default values at the time of
|
||||
* prompting, then this will need to be fixed. The decode of the values will need to be
|
||||
* delayed until (and repeated every time) parameters are populated.
|
||||
*/
|
||||
SaveState subState = saveState.getSaveState(KEY_MEMORIZED_ARGUMENTS);
|
||||
if (subState == null) {
|
||||
return;
|
||||
}
|
||||
for (String name : subState.getNames()) {
|
||||
try {
|
||||
NameTypePair ntp = NameTypePair.fromString(name);
|
||||
P param = parameters.get(ntp.name());
|
||||
if (param == null) {
|
||||
continue;
|
||||
}
|
||||
memorized.put(ntp, parameterLoadValue(param, subState, ntp.encodeString()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Error restoring memorized parameter " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setDescription(String htmlDescription) {
|
||||
if (htmlDescription == null) {
|
||||
descriptionLabel.setBorder(BorderFactory.createEmptyBorder());
|
||||
|
||||
+2
-15
@@ -27,12 +27,11 @@ import docking.test.AbstractDockingTest;
|
||||
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||
import ghidra.async.SwingExecutorService;
|
||||
import ghidra.debug.api.ValStr;
|
||||
import ghidra.framework.options.SaveState;
|
||||
|
||||
public class InvocationDialogHelper<P, D extends AbstractDebuggerParameterDialog<P>> {
|
||||
|
||||
public static <P, D extends AbstractDebuggerParameterDialog<P>> InvocationDialogHelper<P, D> waitFor(
|
||||
Class<D> cls) {
|
||||
public static <P, D extends AbstractDebuggerParameterDialog<P>> InvocationDialogHelper<P, D>
|
||||
waitFor(Class<D> cls) {
|
||||
D dialog = AbstractDockingTest.waitForDialogComponent(cls);
|
||||
return new InvocationDialogHelper<>(dialog);
|
||||
}
|
||||
@@ -86,16 +85,4 @@ public class InvocationDialogHelper<P, D extends AbstractDebuggerParameterDialog
|
||||
public void invoke() {
|
||||
runSwing(() -> dialog.invoke(null));
|
||||
}
|
||||
|
||||
public SaveState saveState() {
|
||||
SaveState parent = new SaveState();
|
||||
runSwing(() -> dialog.writeConfigState(parent));
|
||||
return parent.getSaveState(AbstractDebuggerParameterDialog.KEY_MEMORIZED_ARGUMENTS);
|
||||
}
|
||||
|
||||
public void loadState(SaveState state) {
|
||||
SaveState parent = new SaveState();
|
||||
parent.putSaveState(AbstractDebuggerParameterDialog.KEY_MEMORIZED_ARGUMENTS, state);
|
||||
runSwing(() -> dialog.readConfigState(parent));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ public class JythonScriptProvider extends AbstractPythonScriptProvider {
|
||||
throws GhidraScriptLoadException {
|
||||
|
||||
try {
|
||||
Class<?> clazz = Class.forName(JythonScript.class.getName());
|
||||
GhidraScript script = (GhidraScript) clazz.getConstructor().newInstance();
|
||||
GhidraScript script = new JythonScript();
|
||||
script.setSourceFile(sourceFile);
|
||||
return script;
|
||||
}
|
||||
|
||||
+2
-73
@@ -15,13 +15,9 @@
|
||||
*/
|
||||
package ghidra.features.bsim.query;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.parsers.*;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
@@ -33,7 +29,6 @@ import ghidra.features.bsim.query.facade.SFOverviewInfo;
|
||||
import ghidra.features.bsim.query.facade.SFQueryInfo;
|
||||
import ghidra.features.bsim.query.protocol.*;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public interface FunctionDatabase extends AutoCloseable {
|
||||
|
||||
@@ -333,72 +328,6 @@ public interface FunctionDatabase extends AutoCloseable {
|
||||
return new WeightedLSHCosineVectorFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all configuration template files.
|
||||
*
|
||||
* @return list of template files
|
||||
*/
|
||||
public static List<File> getConfigurationTemplates() {
|
||||
List<File> templateFiles = new ArrayList<>();
|
||||
|
||||
ResourceFile moduleDataSubDirectory;
|
||||
try {
|
||||
moduleDataSubDirectory = Application.getModuleDataSubDirectory("");
|
||||
File templateDir = new File(moduleDataSubDirectory.getAbsolutePath());
|
||||
if (!templateDir.exists()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
FilenameFilter nameFilter = (dir, name) -> {
|
||||
if (!name.endsWith(".xml")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
File[] files = templateDir.listFiles(nameFilter);
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (isConfigTemplate(file)) {
|
||||
templateFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(null, "Error retrieving configuration templates", e);
|
||||
}
|
||||
|
||||
return templateFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given xml file is a config template. This is done by opening the file
|
||||
* and checking for the presence of a {@code <dbconfig>} root tag.
|
||||
*
|
||||
* @param file the file to inspect
|
||||
* @return true if the file is config template
|
||||
*/
|
||||
static boolean isConfigTemplate(File file) {
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(file);
|
||||
|
||||
Element rootElem = doc.getDocumentElement();
|
||||
if (rootElem.getTagName().equals("dbconfig")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (ParserConfigurationException | SAXException | IOException e) {
|
||||
Msg.error(null, "Error inspecting xml file", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of functions to be queried per staged query when searching
|
||||
* for similar functions.
|
||||
|
||||
+2
-5
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -225,9 +225,6 @@ public class DescriptionManager {
|
||||
*/
|
||||
public ExecutableRecord newExecutableRecord(String md5, String enm, String cnm, String arc,
|
||||
Date dt, String repo, String path, RowKey id) throws LSHException {
|
||||
if (md5.length() != 32) {
|
||||
throw new LSHException("MD5 field must be exactly 32 hex characters");
|
||||
}
|
||||
ExecutableRecord newexe = new ExecutableRecord(md5, enm, cnm, arc, dt, id, repo, path);
|
||||
if (!exerec.add(newexe)) {
|
||||
ExecutableRecord oldexe = exerec.floor(newexe);
|
||||
|
||||
+18
@@ -19,6 +19,8 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import generic.hash.SimpleCRC32;
|
||||
import ghidra.features.bsim.query.LSHException;
|
||||
@@ -53,6 +55,9 @@ public class ExecutableRecord implements Comparable<ExecutableRecord> {
|
||||
public static final int METADATA_PATH = 32;
|
||||
public static final int METADATA_LIBR = 64;
|
||||
|
||||
private static final String md5Regex = "[a-fA-F0-9]{32}";
|
||||
private static final Pattern md5Matcher = Pattern.compile(md5Regex);
|
||||
|
||||
private final String md5sum; // The MD5 hash of the executable
|
||||
private final String executableName; // The name of the executable
|
||||
private final String architecture; // The architecture on which the executable runs
|
||||
@@ -77,6 +82,13 @@ public class ExecutableRecord implements Comparable<ExecutableRecord> {
|
||||
public List<CategoryRecord> catinsert; // Non-null, if there are only insertions
|
||||
}
|
||||
|
||||
private static void checkValidMD5(String md5) {
|
||||
Matcher matcher = md5Matcher.matcher(md5);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Invalid MD5 hash string: " + md5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a 32-bit integer to hexadecimal ascii representation
|
||||
* @param val is the integer to encode
|
||||
@@ -125,8 +137,10 @@ public class ExecutableRecord implements Comparable<ExecutableRecord> {
|
||||
/**
|
||||
* Constructor for searching within a DescriptionManager
|
||||
* @param md5 is hash of executable being searched for
|
||||
* @throws IllegalArgumentException if {@code md5} is not a valid md5 hash string
|
||||
*/
|
||||
protected ExecutableRecord(String md5) {
|
||||
checkValidMD5(md5);
|
||||
md5sum = md5;
|
||||
executableName = "";
|
||||
architecture = "";
|
||||
@@ -151,9 +165,11 @@ public class ExecutableRecord implements Comparable<ExecutableRecord> {
|
||||
* @param id is the row id of the record
|
||||
* @param repo is the repository containing the executable (may be null)
|
||||
* @param path is the path to the executable (may be null)
|
||||
* @throws IllegalArgumentException if {@code md5} is not a valid MD5 hash string
|
||||
*/
|
||||
public ExecutableRecord(String md5, String execName, String compilerName, String architecture,
|
||||
Date date, RowKey id, String repo, String path) {
|
||||
checkValidMD5(md5);
|
||||
this.md5sum = md5;
|
||||
this.executableName = execName;
|
||||
this.architecture = architecture;
|
||||
@@ -177,9 +193,11 @@ public class ExecutableRecord implements Comparable<ExecutableRecord> {
|
||||
* @param id is the row id of the record
|
||||
* @param repo is the repository containing the executable (may be null)
|
||||
* @param pth is the path to the executable (may be null)
|
||||
* @throws IllegalArgumentException if {@code md5} is not a valid MD5 hash string.
|
||||
*/
|
||||
public ExecutableRecord(String md5, String enm, String cnm, String arc, Date dt,
|
||||
List<CategoryRecord> uc, RowKey id, String repo, String pth) {
|
||||
checkValidMD5(md5);
|
||||
md5sum = md5;
|
||||
executableName = enm;
|
||||
architecture = arc;
|
||||
|
||||
@@ -24,6 +24,7 @@ import ghidra.program.util.AddressFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
public class LocationMemento {
|
||||
private static final String PROGRAM_PATH = "PROGRAM_PATH_";
|
||||
@@ -147,34 +148,25 @@ public class LocationMemento {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassLoader loader = LocationMemento.class.getClassLoader();
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className, false, loader);
|
||||
if (!LocationMemento.class.isAssignableFrom(clazz)) {
|
||||
Msg.error(LocationMemento.class, "Class is not a LocationMemento: " + clazz);
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends LocationMemento> mementoClass = clazz.asSubclass(LocationMemento.class);
|
||||
Class<? extends LocationMemento> mementoClass = ClassSearcher.forNameSafe(className,
|
||||
LocationMemento.class, LocationMemento.class.getClassLoader());
|
||||
Constructor<? extends LocationMemento> constructor =
|
||||
mementoClass.getConstructor(SaveState.class, Program[].class);
|
||||
return constructor.newInstance(saveState, programs);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
// class must have been deleted or renamed
|
||||
// this can happen for locations created by plugins that are no longer installed
|
||||
Msg.info(LocationMemento.class,
|
||||
"Unable to identify saved location, class not found: " + className);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error", "Class " + className +
|
||||
" must have public constructor!", e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error", "Class " + className +
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
Msg.showError(LocationMemento.class, null, "Programming Error", "Class " + className +
|
||||
" must have public constructor!", e);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error", "Class " + className +
|
||||
Msg.showError(LocationMemento.class, null, "Programming Error", "Class " + className +
|
||||
" must have a public constructor that takes a SaveState " + "and a Program[]!", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
@@ -54,6 +54,7 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.GhidraProgramUtilities;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utilities.util.FileUtilities;
|
||||
@@ -774,21 +775,21 @@ public class HeadlessAnalyzer {
|
||||
classLoaderForDotClassScripts =
|
||||
URLClassLoader.newInstance(urls.toArray(new URL[0]));
|
||||
|
||||
Class<?> c = Class.forName(className, true, classLoaderForDotClassScripts);
|
||||
ClassSearcher.forNameSafe(className, GhidraScript.class,
|
||||
classLoaderForDotClassScripts);
|
||||
|
||||
if (GhidraScript.class.isAssignableFrom(c)) {
|
||||
// No issues, but return null, which signifies we don't actually have a
|
||||
// ResourceFile to associate with the script name
|
||||
return null;
|
||||
}
|
||||
|
||||
Msg.error(this,
|
||||
"REPORT SCRIPT ERROR: java class '" + className + "' is not a GhidraScript");
|
||||
// No issues, but return null, which signifies we don't actually have a
|
||||
// ResourceFile to associate with the script name
|
||||
return null;
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
Msg.error(this,
|
||||
"REPORT SCRIPT ERROR: java class not found for '" + className + "'");
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
Msg.error(this,
|
||||
"REPORT SCRIPT ERROR: java class '" + className + "' is not a GhidraScript");
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid script: " + scriptName);
|
||||
}
|
||||
|
||||
@@ -901,13 +902,14 @@ public class HeadlessAnalyzer {
|
||||
}
|
||||
|
||||
String className = scriptName.substring(0, scriptName.length() - 6);
|
||||
Class<?> c = Class.forName(className, true, classLoaderForDotClassScripts);
|
||||
Class<? extends GhidraScript> c = ClassSearcher.forNameSafe(className,
|
||||
GhidraScript.class, classLoaderForDotClassScripts);
|
||||
|
||||
// Get parent folder to pass to GhidraScript
|
||||
File parentFile = new File(c.getResource(c.getSimpleName() + ".class").toURI())
|
||||
.getParentFile();
|
||||
|
||||
currScript = (GhidraScript) c.getConstructor().newInstance();
|
||||
currScript = c.getConstructor().newInstance();
|
||||
currScript.setScriptArgs(scriptArgs);
|
||||
|
||||
if (options.propertiesFilePaths.size() > 0) {
|
||||
|
||||
+3
-3
@@ -20,6 +20,7 @@ import java.util.Map.Entry;
|
||||
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* A state object to save settings each type of comparison view known by the system. This class
|
||||
@@ -73,9 +74,8 @@ public class CodeComparisonViewState {
|
||||
String[] names = classStates.getNames();
|
||||
for (String className : names) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends CodeComparisonView> clazz =
|
||||
(Class<? extends CodeComparisonView>) Class.forName(className);
|
||||
Class<? extends CodeComparisonView> clazz = ClassSearcher.forNameSafe(className,
|
||||
CodeComparisonView.class, getClass().getClassLoader());
|
||||
SaveState classState = classStates.getSaveState(className);
|
||||
states.put(clazz, classState);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package help.screenshot;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.GeneralPath;
|
||||
@@ -80,6 +81,7 @@ import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.ColorUtils;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.UsrException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -1355,10 +1357,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
|
||||
|
||||
public Plugin loadPlugin(String className) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Plugin> pluginClazz = (Class<? extends Plugin>) clazz;
|
||||
return loadPlugin(pluginClazz);
|
||||
Class<? extends Plugin> clazz =
|
||||
ClassSearcher.forNameSafe(className, Plugin.class, getClass().getClassLoader());
|
||||
return loadPlugin(clazz);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
+8
-7
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package ghidra.framework.plugintool.dialog;
|
||||
|
||||
import static org.hamcrest.collection.IsEmptyCollection.*;
|
||||
import static org.hamcrest.core.IsNot.*;
|
||||
import static org.hamcrest.collection.IsEmptyCollection.empty;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
@@ -31,6 +31,7 @@ import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import resources.Icons;
|
||||
|
||||
/**
|
||||
@@ -372,9 +373,9 @@ public class ManagePlugins2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private void loadPlugin(String name) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(name);
|
||||
Plugin plugin =
|
||||
(Plugin) clazz.getDeclaredConstructor(PluginTool.class).newInstance(tool);
|
||||
Class<? extends Plugin> clazz =
|
||||
ClassSearcher.forNameSafe(name, Plugin.class, getClass().getClassLoader());
|
||||
Plugin plugin = clazz.getDeclaredConstructor(PluginTool.class).newInstance(tool);
|
||||
loadedPlugins.add(plugin);
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
@@ -22,7 +22,6 @@ import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.FocusOwnerProvider;
|
||||
import generic.test.TestUtils;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* A class that helps to delegate key events to the system override key event dispatcher. This
|
||||
@@ -64,30 +63,22 @@ public class TestKeyEventDispatcher {
|
||||
}
|
||||
|
||||
private static KeyEventDispatcher getOverriddenKeyEventDispatcher() {
|
||||
|
||||
// Note: our custom key event dispatcher has package access, so we cannot refer to
|
||||
// it directly
|
||||
try {
|
||||
Class<?> clazz = Class.forName("docking.KeyBindingOverrideKeyEventDispatcher");
|
||||
Object customDispatcher = TestUtils.getInstanceField("instance", clazz);
|
||||
if (customDispatcher == null) {
|
||||
return null; // not installed
|
||||
}
|
||||
|
||||
//
|
||||
// Dependency Inject our own focus provider so that we can force the event
|
||||
// dispatcher to deliver events to our component
|
||||
//
|
||||
TestUtils.invokeInstanceMethod("setFocusOwnerProvider", customDispatcher,
|
||||
FocusOwnerProvider.class, focusProvider);
|
||||
|
||||
return (KeyEventDispatcher) customDispatcher;
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
Msg.error(TestKeyEventDispatcher.class, "Unable to find the system KeyEventDispatcher",
|
||||
e);
|
||||
return null;
|
||||
Class<?> clazz = docking.KeyBindingOverrideKeyEventDispatcher.class;
|
||||
Object customDispatcher = TestUtils.getInstanceField("instance", clazz);
|
||||
if (customDispatcher == null) {
|
||||
return null; // not installed
|
||||
}
|
||||
|
||||
//
|
||||
// Dependency Inject our own focus provider so that we can force the event
|
||||
// dispatcher to deliver events to our component
|
||||
//
|
||||
TestUtils.invokeInstanceMethod("setFocusOwnerProvider", customDispatcher,
|
||||
FocusOwnerProvider.class, focusProvider);
|
||||
|
||||
return (KeyEventDispatcher) customDispatcher;
|
||||
}
|
||||
|
||||
private static class TestFocusOwnerProvider implements FocusOwnerProvider {
|
||||
|
||||
@@ -34,6 +34,7 @@ import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* A p-code thread which incorporates per-architecture state modifiers
|
||||
@@ -232,16 +233,11 @@ public class ModifiedPcodeThread<T> extends DefaultPcodeThread<T> {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Class<?> c = Class.forName(classname);
|
||||
if (!EmulateInstructionStateModifier.class.isAssignableFrom(c)) {
|
||||
Msg.error(this,
|
||||
"Language " + language.getLanguageID() + " does not specify a valid " +
|
||||
GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS);
|
||||
throw new RuntimeException(classname + " does not implement interface " +
|
||||
EmulateInstructionStateModifier.class.getName());
|
||||
}
|
||||
Constructor<?> constructor = c.getConstructor(Emulate.class);
|
||||
return (EmulateInstructionStateModifier) constructor.newInstance(emulate);
|
||||
Class<? extends EmulateInstructionStateModifier> c = ClassSearcher.forNameSafe(
|
||||
classname, EmulateInstructionStateModifier.class, getClass().getClassLoader());
|
||||
Constructor<? extends EmulateInstructionStateModifier> constructor =
|
||||
c.getConstructor(Emulate.class);
|
||||
return constructor.newInstance(emulate);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Language " + language.getLanguageID() + " does not specify a valid " +
|
||||
|
||||
@@ -31,6 +31,7 @@ import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@@ -113,18 +114,10 @@ public class Emulate {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class<?> c = Class.forName(classname);
|
||||
if (!EmulateInstructionStateModifier.class.isAssignableFrom(c)) {
|
||||
Msg.error(this,
|
||||
"Language " + language.getLanguageID() + " does not specify a valid " +
|
||||
GhidraLanguagePropertyKeys.EMULATE_INSTRUCTION_STATE_MODIFIER_CLASS);
|
||||
throw new RuntimeException(classname + " does not implement interface " +
|
||||
EmulateInstructionStateModifier.class.getName());
|
||||
}
|
||||
Class<? extends EmulateInstructionStateModifier> instructionStateModifierClass =
|
||||
(Class<? extends EmulateInstructionStateModifier>) c;
|
||||
Class<? extends EmulateInstructionStateModifier> c = ClassSearcher.forNameSafe(
|
||||
classname, EmulateInstructionStateModifier.class, getClass().getClassLoader());
|
||||
Constructor<? extends EmulateInstructionStateModifier> constructor =
|
||||
instructionStateModifierClass.getConstructor(Emulate.class);
|
||||
c.getConstructor(Emulate.class);
|
||||
instructionStateModifier = constructor.newInstance(this);
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
@@ -252,7 +252,7 @@ public class Application {
|
||||
|
||||
private ResourceFile getModuleForClass(String className) {
|
||||
try {
|
||||
Class<?> callersClass = Class.forName(className);
|
||||
Class<?> callersClass = Class.forName(className, false, getClass().getClassLoader());
|
||||
return getModuleForClass(callersClass);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.google.gson.*;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
import utilities.util.FileUtilities;
|
||||
@@ -433,17 +434,16 @@ public class GProperties {
|
||||
}
|
||||
|
||||
Enum<?> getEnumValue(String enumClassName, String value) {
|
||||
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
try {
|
||||
Class<?> clazz = Class.forName(enumClassName, false, loader);
|
||||
if (!Enum.class.isAssignableFrom(clazz)) {
|
||||
Msg.error(this, "Class is not an Enum: " + clazz);
|
||||
return null;
|
||||
}
|
||||
// It would be better to restrict this to some interface we define. Otherwise, we ought
|
||||
// to examine the static initializers of every enum in our classpath. Luckily, the
|
||||
// constructor invocations are all defined by the enum values, and so cannot be randomly
|
||||
// invoked. Famous last words: I doubt there's anything of concern in any enum's static
|
||||
// initializer....
|
||||
Class<?> enumClass =
|
||||
ClassSearcher.forNameSafe(enumClassName, Enum.class, getClass().getClassLoader());
|
||||
|
||||
// Note: calling valueOf() will trigger class initialization
|
||||
Method m = clazz.getMethod("valueOf", new Class[] { String.class });
|
||||
Method m = enumClass.getMethod("valueOf", new Class[] { String.class });
|
||||
if (m != null) {
|
||||
return (Enum<?>) m.invoke(null, new Object[] { value });
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -21,7 +21,6 @@ import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
import junit.framework.TestSuite;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
@@ -34,11 +33,7 @@ public class TestSuiteUtilities {
|
||||
|
||||
private static Class<?> TEST_CASE_CLASS = createTestClass();
|
||||
private static Class<?> createTestClass() {
|
||||
try {
|
||||
return Class.forName("junit.framework.TestCase");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new AssertException();
|
||||
}
|
||||
return junit.framework.TestCase.class;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -274,6 +274,42 @@ public class ClassSearcher {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the named class in a safe(r) manner.
|
||||
* <p>
|
||||
* This addresses the concern that many tools and database entries can refer to custom types,
|
||||
* e.g., options and properties, that may permit an attacker to construct instances of arbitrary
|
||||
* class, those constructors perhaps implementing gadgets that could be used in an exploit
|
||||
* chain.
|
||||
* <p>
|
||||
* The pattern to "mitigate" this is to ensure the found class implements a given interface or
|
||||
* extends from a given class, <em>before</em> invoking a constructor. Ideally, this check can
|
||||
* be applied before <em>class initialization</em>. While much narrower, static initializers may
|
||||
* also provide gadgets. This method implements the pattern which avoids class initialization
|
||||
* until the type has been checked.
|
||||
* <p>
|
||||
* <b>WARNING:</b> Do not pass {@link Object}, a standard JDK interface, or any interface from a
|
||||
* dependency like apache-commons. The purpose of the super type is to narrow the set of
|
||||
* acceptable classes to those we control or that a user has intentionally installed/loaded as a
|
||||
* Ghidra extension. Thus, the static initializers and constructors of all subclasses should be
|
||||
* reviewed carefully.
|
||||
*
|
||||
* @param <T> the super type
|
||||
* @param name the class binary name, as in {@link Class#forName(String, boolean, ClassLoader)}.
|
||||
* @param sup the super type
|
||||
* @param loader the class loader, or {@code null} to use the bootstrap loader.
|
||||
* @return the found class as a subclass of the given type
|
||||
* @throws ClassNotFoundException if the class cannot be found
|
||||
* @throws ClassCastException if the found class is not a sub type of the given type
|
||||
*/
|
||||
public static <T> Class<? extends T> forNameSafe(String name, Class<T> sup, ClassLoader loader)
|
||||
throws ClassNotFoundException {
|
||||
Class<?> cls = Class.forName(name, false, loader);
|
||||
Class<? extends T> sub = cls.asSubclass(sup);
|
||||
Class.forName(name, true, loader); // Initialize it this time
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a change listener that will be notified when the classpath
|
||||
* is searched for new classes.
|
||||
@@ -631,11 +667,11 @@ public class ClassSearcher {
|
||||
|
||||
/**
|
||||
* If the given class name matches the known extension name patterns, then this method will try
|
||||
* to load that class using the provided path. Extensions may be loaded using their own
|
||||
* class loader, depending on the system property
|
||||
* to load that class using the provided path. Extensions may be loaded using their own
|
||||
* class loader, depending on the system property
|
||||
* {@link GhidraClassLoader#ENABLE_RESTRICTED_EXTENSIONS_PROPERTY}.
|
||||
* <p>
|
||||
* Examples:
|
||||
* Examples:
|
||||
* <pre>
|
||||
* /foo/bar/baz/file.jar fully.qualified.ClassName
|
||||
* /foo/bar/baz/bin fully.qualified.ClassName
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* ###
|
||||
* 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.util.classfinder;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ClassSearcherTest {
|
||||
public enum Canary {
|
||||
ALIVE, DEAD;
|
||||
}
|
||||
|
||||
public static Canary canary;
|
||||
|
||||
public interface TestSuper {
|
||||
}
|
||||
|
||||
public interface WrongSuper {
|
||||
}
|
||||
|
||||
public static class ClassWithStaticInitializerSideEffects implements TestSuper {
|
||||
static {
|
||||
canary = Canary.DEAD;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that our safe(r) version of {@link Class#forName(String)} does not invoke any static
|
||||
* initializer.
|
||||
*
|
||||
* @throws Exception because
|
||||
*/
|
||||
@Test
|
||||
public void testForNameSafeNoClinit() throws Exception {
|
||||
canary = Canary.ALIVE;
|
||||
|
||||
Class<? extends TestSuper> found = ClassSearcher.forNameSafe(
|
||||
"ghidra.util.classfinder.ClassSearcherTest$ClassWithStaticInitializerSideEffects",
|
||||
TestSuper.class, getClass().getClassLoader());
|
||||
assertNotNull(found);
|
||||
assertEquals(Canary.ALIVE, canary);
|
||||
Constructor<? extends TestSuper> constructor = found.getConstructor();
|
||||
assertEquals(Canary.ALIVE, canary);
|
||||
TestSuper instance = constructor.newInstance();
|
||||
assertNotNull(instance);
|
||||
assertEquals(Canary.DEAD, canary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForNameSafeNoClinitErr() throws Exception {
|
||||
canary = Canary.ALIVE;
|
||||
|
||||
try {
|
||||
ClassSearcher.forNameSafe(
|
||||
"ghidra.util.classfinder.ClassSearcherTest$ClassWithStaticInitializerSideEffects",
|
||||
WrongSuper.class, getClass().getClassLoader());
|
||||
fail("Should not have permitted the class");
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
// pass
|
||||
}
|
||||
assertEquals(Canary.ALIVE, canary);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* Reads and writes current theme info to preferences
|
||||
@@ -47,15 +48,9 @@ public class ThemePreferences {
|
||||
else if (themeId.startsWith(DiscoverableGTheme.CLASS_PREFIX)) {
|
||||
String className = themeId.substring(DiscoverableGTheme.CLASS_PREFIX.length());
|
||||
try {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
Class<?> clazz = Class.forName(className, false, loader);
|
||||
if (!GTheme.class.isAssignableFrom(clazz)) {
|
||||
Msg.showError(GTheme.class, null, "Can't Load Previous Theme",
|
||||
"Theme class name does not point to a GTheme instance: " + className);
|
||||
return ThemeManager.getDefaultTheme();
|
||||
}
|
||||
|
||||
return (GTheme) clazz.getDeclaredConstructor().newInstance();
|
||||
Class<? extends GTheme> clazz =
|
||||
ClassSearcher.forNameSafe(className, GTheme.class, getClass().getClassLoader());
|
||||
return clazz.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(GTheme.class, null, "Can't Load Previous Theme",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
public class FileOptions extends AbstractOptions {
|
||||
|
||||
@@ -53,8 +54,9 @@ public class FileOptions extends AbstractOptions {
|
||||
public CustomOption readCustomOption(GProperties properties) {
|
||||
String customOptionClassName = properties.getString("CUSTOM_OPTION_CLASS", null);
|
||||
try {
|
||||
Class<?> c = Class.forName(customOptionClassName);
|
||||
CustomOption customOption = (CustomOption) c.getDeclaredConstructor().newInstance();
|
||||
Class<? extends CustomOption> c = ClassSearcher.forNameSafe(customOptionClassName,
|
||||
CustomOption.class, getClass().getClassLoader());
|
||||
CustomOption customOption = c.getDeclaredConstructor().newInstance();
|
||||
customOption.readState(properties);
|
||||
return customOption;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jdom2.input.SAXBuilder;
|
||||
import org.jdom2.output.XMLOutputter;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.xml.GenericXMLOutputter;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
|
||||
@@ -231,8 +232,9 @@ public enum OptionType {
|
||||
String customOptionClassName =
|
||||
saveState.getString(CustomOption.CUSTOM_OPTION_CLASS_NAME_KEY, null);
|
||||
try {
|
||||
Class<?> c = Class.forName(customOptionClassName);
|
||||
CustomOption option = (CustomOption) c.getConstructor().newInstance();
|
||||
Class<? extends CustomOption> c = ClassSearcher.forNameSafe(customOptionClassName,
|
||||
CustomOption.class, OptionType.class.getClassLoader());
|
||||
CustomOption option = c.getConstructor().newInstance();
|
||||
option.readState(saveState);
|
||||
return option;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jdom2.Element;
|
||||
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
@@ -151,9 +152,11 @@ public class ToolOptions extends AbstractOptions {
|
||||
continue; // shouldn't happen
|
||||
}
|
||||
|
||||
Class<?> c = Class.forName(element.getAttributeValue(CLASS_ATTRIBUTE));
|
||||
Constructor<?> constructor = c.getDeclaredConstructor();
|
||||
WrappedOption wo = (WrappedOption) constructor.newInstance();
|
||||
Class<? extends WrappedOption> c =
|
||||
ClassSearcher.forNameSafe(element.getAttributeValue(CLASS_ATTRIBUTE),
|
||||
WrappedOption.class, getClass().getClassLoader());
|
||||
Constructor<? extends WrappedOption> constructor = c.getDeclaredConstructor();
|
||||
WrappedOption wo = constructor.newInstance();
|
||||
wo.readState(new SaveState(element));
|
||||
if (wo instanceof WrappedCustomOption wrappedCustom && !wrappedCustom.isValid()) {
|
||||
continue;
|
||||
|
||||
+7
-13
@@ -16,6 +16,7 @@
|
||||
package ghidra.framework.options;
|
||||
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
public class WrappedCustomOption implements WrappedOption {
|
||||
|
||||
@@ -33,28 +34,21 @@ public class WrappedCustomOption implements WrappedOption {
|
||||
|
||||
@Override
|
||||
public void readState(SaveState saveState) {
|
||||
String className = saveState.getString("CUSTOM OPTION CLASS", null);
|
||||
String customOptionClassName = saveState.getString("CUSTOM OPTION CLASS", null);
|
||||
valid = false;
|
||||
try {
|
||||
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
Class<?> clazz = Class.forName(className, false, loader);
|
||||
if (!CustomOption.class.isAssignableFrom(clazz)) {
|
||||
Msg.error(this, "Can't create custom option instance; not a CustomOpiton: " +
|
||||
className);
|
||||
return;
|
||||
}
|
||||
|
||||
value = (CustomOption) clazz.getConstructor().newInstance();
|
||||
Class<? extends CustomOption> c = ClassSearcher.forNameSafe(customOptionClassName,
|
||||
CustomOption.class, getClass().getClassLoader());
|
||||
value = c.getConstructor().newInstance();
|
||||
value.readState(saveState);
|
||||
valid = true;
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
Msg.info(this,
|
||||
"Custom option class '%s' does not exist".formatted(className));
|
||||
"Custom option class '%s' does not exist".formatted(customOptionClassName));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Can't create custom option instance for: " + className, e);
|
||||
Msg.error(this, "Can't create custom option instance for: " + customOptionClassName, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,13 @@ import org.jdom2.*;
|
||||
import org.jdom2.input.SAXBuilder;
|
||||
import org.jdom2.output.XMLOutputter;
|
||||
|
||||
import com.sun.source.util.Plugin;
|
||||
|
||||
import ghidra.framework.model.ProjectManager;
|
||||
import ghidra.framework.model.ToolTemplate;
|
||||
import ghidra.framework.project.tool.GhidraToolTemplate;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.xml.GenericXMLOutputter;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
@@ -175,7 +178,7 @@ public class ToolUtils {
|
||||
// check to see if we can still find the plugin class (it may have
|
||||
// been removed)
|
||||
try {
|
||||
Class.forName(value);
|
||||
ClassSearcher.forNameSafe(value, Plugin.class, ToolUtils.class.getClassLoader());
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// oh well, leave it out
|
||||
|
||||
+4
-8
@@ -85,11 +85,8 @@ public class PluginUtils {
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> tmpClass = Class.forName(pluginClassName);
|
||||
if (!Plugin.class.isAssignableFrom(tmpClass)) {
|
||||
throw new PluginException(
|
||||
"Class " + pluginClassName + " is not derived from Plugin");
|
||||
}
|
||||
Class<? extends Plugin> tmpClass = ClassSearcher.forNameSafe(pluginClassName,
|
||||
Plugin.class, PluginUtils.class.getClassLoader());
|
||||
return tmpClass.asSubclass(Plugin.class);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
@@ -134,8 +131,8 @@ public class PluginUtils {
|
||||
}
|
||||
if (defaultProviderClassName != null) {
|
||||
try {
|
||||
Class<?> tmpClass = Class.forName(defaultProviderClassName);
|
||||
return tmpClass.asSubclass(Plugin.class);
|
||||
return ClassSearcher.forNameSafe(defaultProviderClassName, Plugin.class,
|
||||
PluginUtils.class.getClassLoader());
|
||||
}
|
||||
catch (ClassCastException cce) {
|
||||
Msg.error(PluginUtils.class,
|
||||
@@ -145,7 +142,6 @@ public class PluginUtils {
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new AssertException(
|
||||
"default provider class for " + serviceClass.getName() + " not found!");
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
+5
-6
@@ -22,11 +22,11 @@ import javax.swing.ImageIcon;
|
||||
import org.jdom2.Element;
|
||||
|
||||
import docking.util.image.ToolIconURL;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.framework.model.ToolTemplate;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* Implementation for a tool template that has the class names of the
|
||||
@@ -135,8 +135,8 @@ public class GhidraToolTemplate implements ToolTemplate {
|
||||
Element elem = (Element) list.get(i);
|
||||
String className = elem.getAttribute(CLASS_NAME_XML_NAME).getValue();
|
||||
try {
|
||||
// no need to perform static initialization; clients only check isAssignableFrom()
|
||||
dtList.add(Class.forName(className, false, loader));
|
||||
dtList.add(ClassSearcher
|
||||
.forNameSafe(className, DomainObject.class, loader));
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
Msg.warn(this, "Tool supported content class not found: " + className);
|
||||
@@ -182,8 +182,7 @@ public class GhidraToolTemplate implements ToolTemplate {
|
||||
iconElem.setText(NumericUtilities.convertBytesToString(iconURL.getIconBytes()));
|
||||
}
|
||||
root.addContent(iconElem);
|
||||
|
||||
root.addContent((toolElement.clone()));
|
||||
root.addContent(toolElement.clone());
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
+5
-12
@@ -45,6 +45,7 @@ import ghidra.program.model.pcode.*;
|
||||
import ghidra.program.model.util.ProcessorSymbolType;
|
||||
import ghidra.sleigh.grammar.SourceFileIndexer;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.exception.TimeoutException;
|
||||
import ghidra.util.task.PreserveStateWrappingTaskMonitor;
|
||||
@@ -1492,18 +1493,10 @@ public class SleighLanguage implements Language {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class<?> helperClass = Class.forName(className);
|
||||
if (!ParallelInstructionLanguageHelper.class.isAssignableFrom(helperClass)) {
|
||||
Msg.error(this,
|
||||
"Invalid Class specified for " +
|
||||
GhidraLanguagePropertyKeys.PARALLEL_INSTRUCTION_HELPER_CLASS + " (" +
|
||||
helperClass.getName() + "): " + description.getSpecFile());
|
||||
}
|
||||
else {
|
||||
parallelHelper =
|
||||
(ParallelInstructionLanguageHelper) helperClass.getDeclaredConstructor()
|
||||
.newInstance();
|
||||
}
|
||||
Class<? extends ParallelInstructionLanguageHelper> helperClass =
|
||||
ClassSearcher.forNameSafe(className, ParallelInstructionLanguageHelper.class,
|
||||
getClass().getClassLoader());
|
||||
parallelHelper = helperClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new SleighException("Failed to instantiate " +
|
||||
|
||||
+11
-8
@@ -51,6 +51,7 @@ import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Lock.Closeable;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.classfinder.ClassTranslator;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -2491,7 +2492,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
|
||||
/**
|
||||
* Queue a datatype to deleted in response to another datatype being deleted.
|
||||
* @param id datatype ID to be removed
|
||||
* @param dt datatype to be deleted
|
||||
* @param id datatype ID to be deleted
|
||||
*/
|
||||
protected void addDataTypeToDelete(DataType dt, long id) {
|
||||
if (dt instanceof DataTypeDB dbDt) {
|
||||
@@ -2807,9 +2809,10 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
String classPath = record.getString(BuiltinDBAdapter.BUILT_IN_CLASSNAME_COL);
|
||||
String name = record.getString(BuiltinDBAdapter.BUILT_IN_NAME_COL);
|
||||
try {
|
||||
Class<?> clazz;
|
||||
Class<? extends BuiltInDataType> clazz;
|
||||
try {
|
||||
clazz = Class.forName(classPath);
|
||||
clazz = ClassSearcher.forNameSafe(classPath, BuiltInDataType.class,
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException | NoClassDefFoundError e) {
|
||||
// Check the classNameMap.
|
||||
@@ -2818,15 +2821,15 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
clazz = Class.forName(newClassPath);
|
||||
clazz = ClassSearcher.forNameSafe(newClassPath, BuiltInDataType.class,
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e1) {
|
||||
throw e1;
|
||||
}
|
||||
}
|
||||
|
||||
BuiltInDataType bdt =
|
||||
(BuiltInDataType) clazz.getDeclaredConstructor().newInstance();
|
||||
BuiltInDataType bdt = clazz.getDeclaredConstructor().newInstance();
|
||||
bdt.setName(name);
|
||||
bdt.setCategoryPath(catPath);
|
||||
|
||||
@@ -4541,7 +4544,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
* Check for cached equivalence of a type contained within this datatype manager
|
||||
* against another datatype. Every call to this method when {@code null} is
|
||||
* returned must be following by an invocation of
|
||||
* {@link #putCachedEquivalence(DataTypeDB, DataType, boolean)} once an
|
||||
* {@link #putCachedEquivalence(DataTypeDB, DataType, Boolean)} once an
|
||||
* equivalence determination has been made. The number of outstanding calls to
|
||||
* this method will be tracked. When the outstanding call count returns to zero
|
||||
* the cache will be cleared. <br>
|
||||
@@ -4554,7 +4557,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||
* @return true, false or {@code null} if unknown. A {@code null} value mandates
|
||||
* that the caller make a determination and put the result into the
|
||||
* cache when known (see
|
||||
* {@link #putCachedEquivalence(DataTypeDB, DataType, boolean)}.
|
||||
* {@link #putCachedEquivalence(DataTypeDB, DataType, Boolean)}.
|
||||
*/
|
||||
Boolean getCachedEquivalence(DataTypeDB dataTypeDB, DataType dataType) {
|
||||
EquivalenceCache cache = equivalenceCache.get();
|
||||
|
||||
+7
-5
@@ -28,6 +28,7 @@ import ghidra.program.util.ChangeManager;
|
||||
import ghidra.util.Lock.Closeable;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Saveable;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.classfinder.ClassTranslator;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -133,9 +134,10 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Class<? extends Saveable> getSaveableClassForName(String classPath) {
|
||||
Class<?> c = null;
|
||||
Class<? extends Saveable> c = null;
|
||||
try {
|
||||
c = Class.forName(classPath);
|
||||
c = ClassSearcher.forNameSafe(classPath, Saveable.class,
|
||||
ObjectPropertyMapDB.class.getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
// Check the classNameMap.
|
||||
@@ -143,10 +145,10 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
|
||||
if (newClassPath != null) {
|
||||
classPath = newClassPath;
|
||||
try {
|
||||
c = Class.forName(newClassPath);
|
||||
c = ClassSearcher.forNameSafe(newClassPath, Saveable.class,
|
||||
ObjectPropertyMapDB.class.getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException e1) {
|
||||
|
||||
// Since we can't get the class, at least handle it generically.
|
||||
}
|
||||
}
|
||||
@@ -159,7 +161,7 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
|
||||
"Object property class does not implement Saveable interface: " + classPath);
|
||||
}
|
||||
// If unable to get valid Saveable class use generic implementation
|
||||
return (c != null) ? (Class<? extends Saveable>) c : GenericSaveable.class;
|
||||
return (c != null) ? c : GenericSaveable.class;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+12
-12
@@ -37,6 +37,7 @@ import ghidra.program.util.AbstractProgramContext;
|
||||
import ghidra.program.util.ProgramContextImpl;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@@ -1864,29 +1865,28 @@ public class Disassembler implements DisassemblerConflictHandler {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends Disassembler> getLanguageSpecificDisassembler(Language parser) {
|
||||
String className = parser.getProperty(GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS);
|
||||
if (className == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Class<?> disassemblerClass = Class.forName(className);
|
||||
if (!Disassembler.class.isAssignableFrom(disassemblerClass)) {
|
||||
Msg.error(Disassembler.class,
|
||||
"Invalid Class specified for " +
|
||||
GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS + " (" +
|
||||
disassemblerClass.getName() + "): " +
|
||||
parser.getLanguageDescription().getLanguageID());
|
||||
return null;
|
||||
}
|
||||
return (Class<? extends Disassembler>) disassemblerClass;
|
||||
Class<? extends Disassembler> disassemblerClass = ClassSearcher.forNameSafe(className,
|
||||
Disassembler.class, Disassembler.class.getClassLoader());
|
||||
return disassemblerClass;
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Invalid Class specified for " +
|
||||
GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS + " (" + className + "): " +
|
||||
parser.getLanguageDescription().getLanguageID(), e);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
Msg.error(Disassembler.class,
|
||||
"Invalid Class specified for " +
|
||||
GhidraLanguagePropertyKeys.CUSTOM_DISASSEMBLER_CLASS + " (" + className +
|
||||
"): " +
|
||||
parser.getLanguageDescription().getLanguageID());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-12
@@ -38,6 +38,7 @@ import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
@@ -249,7 +250,6 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||
restoreXml(parser);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void buildInjectLibrary() {
|
||||
String classname =
|
||||
language.getProperty(GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
|
||||
@@ -258,18 +258,10 @@ public class BasicCompilerSpec implements CompilerSpec {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Class<?> c = Class.forName(classname);
|
||||
if (!PcodeInjectLibrary.class.isAssignableFrom(c)) {
|
||||
Msg.error(this,
|
||||
"Language " + language.getLanguageID() + " does not specify a valid " +
|
||||
GhidraLanguagePropertyKeys.PCODE_INJECT_LIBRARY_CLASS);
|
||||
throw new RuntimeException(classname + " does not implement interface " +
|
||||
PcodeInjectLibrary.class.getName());
|
||||
}
|
||||
Class<? extends PcodeInjectLibrary> injectLibraryClass =
|
||||
(Class<? extends PcodeInjectLibrary>) c;
|
||||
Class<? extends PcodeInjectLibrary> c = ClassSearcher.forNameSafe(classname,
|
||||
PcodeInjectLibrary.class, getClass().getClassLoader());
|
||||
Constructor<? extends PcodeInjectLibrary> constructor =
|
||||
injectLibraryClass.getConstructor(SleighLanguage.class);
|
||||
c.getConstructor(SleighLanguage.class);
|
||||
pcodeInject = constructor.newInstance(language);
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
+6
-9
@@ -22,6 +22,7 @@ import ghidra.framework.options.SaveState;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
/**
|
||||
* <CODE>ProgramLocation</CODE> provides information about a location in a program in the most
|
||||
@@ -278,10 +279,10 @@ public class ProgramLocation implements Cloneable, Comparable<ProgramLocation> {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassLoader loader = ProgramLocation.class.getClassLoader();
|
||||
try {
|
||||
Class<? extends ProgramLocation> locationClass = ClassSearcher.forNameSafe(className,
|
||||
ProgramLocation.class, ProgramLocation.class.getClassLoader());
|
||||
|
||||
Class<?> locationClass = Class.forName(className, false, loader);
|
||||
if (locationClass.isInterface()) {
|
||||
// This check is needed due to a refactoring that has changed a class into an
|
||||
// interface. The class name may have been saved into the tool. Upon restoring we
|
||||
@@ -290,13 +291,7 @@ public class ProgramLocation implements Cloneable, Comparable<ProgramLocation> {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ProgramLocation.class.isAssignableFrom(locationClass)) {
|
||||
Msg.error(ProgramLocation.class,
|
||||
"Class is not a ProgramLocation: " + locationClass);
|
||||
return null;
|
||||
}
|
||||
|
||||
ProgramLocation loc = (ProgramLocation) locationClass.getConstructor().newInstance();
|
||||
ProgramLocation loc = locationClass.getConstructor().newInstance();
|
||||
loc.restoreState(program, saveState);
|
||||
if (loc.getAddress() != null) {
|
||||
return loc;
|
||||
@@ -305,6 +300,8 @@ public class ProgramLocation implements Cloneable, Comparable<ProgramLocation> {
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
// this can happen for locations created by plugins that are no longer installed
|
||||
Msg.info(ProgramLocation.class,
|
||||
"Unable to restore program location, class not found: " + className);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
|
||||
Msg.showError(ProgramLocation.class, null, "Programming Error",
|
||||
|
||||
+5
-4
@@ -33,6 +33,7 @@ import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.IncompatibleLanguageException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
@@ -386,18 +387,18 @@ class SimpleLanguageTranslator extends LanguageTranslatorAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends LanguagePostUpgradeInstructionHandler> parsePostUpgradeHandlerEntry(
|
||||
Element element) throws SAXException {
|
||||
|
||||
String className = element.getAttributeValue("class");
|
||||
if (className == null) {
|
||||
throw new SAXException(element.getName() + " must specify 'class' attribute");
|
||||
}
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
Class<? extends LanguagePostUpgradeInstructionHandler> clazz =
|
||||
ClassSearcher.forNameSafe(className, LanguagePostUpgradeInstructionHandler.class,
|
||||
SimpleLanguageTranslator.class.getClassLoader());
|
||||
getPostUpgradeInstructionHandler((Program) null, clazz); // test construction
|
||||
return (Class<? extends LanguagePostUpgradeInstructionHandler>) clazz;
|
||||
return clazz;
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof SAXException) {
|
||||
|
||||
+2
-33
@@ -24,7 +24,8 @@ import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
|
||||
import ghidra.app.plugin.core.debug.gui.InvocationDialogHelper;
|
||||
@@ -32,7 +33,6 @@ import ghidra.app.plugin.core.debug.gui.tracermi.launcher.ScriptAttributesParser
|
||||
import ghidra.async.SwingExecutorService;
|
||||
import ghidra.debug.api.ValStr;
|
||||
import ghidra.debug.api.tracermi.LaunchParameter;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.AutoConfigState.PathIsDir;
|
||||
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
|
||||
|
||||
@@ -146,37 +146,6 @@ public class TraceRmiLaunchDialogTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
result.h.invoke();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntSaveHexValue() throws Throwable {
|
||||
PromptResult result = prompt(PARAM_INT);
|
||||
result.h.setArgAsString(PARAM_INT, "0x11");
|
||||
result.h.invoke();
|
||||
|
||||
SaveState state = result.h.saveState();
|
||||
assertEquals("0x11", state.getString("some_int,java.math.BigInteger", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testIntLoadHexValue() throws Throwable {
|
||||
/**
|
||||
* TODO: This is a bit out of order. However, the dialog cannot load/decode from the state
|
||||
* until it has the parameters. Worse, to check that user input was valid, the dialog
|
||||
* verifies that the value it gets back matches the text in the box, because if it doesn't,
|
||||
* then the editor must have failed to parse/decode the value. Currently, loading the state
|
||||
* while the dialog box has already populated its values, does not modify the contents of
|
||||
* any editor, so the text <em>will not</em> match, causing this test to fail.
|
||||
*/
|
||||
PromptResult result = prompt(PARAM_INT);
|
||||
SaveState state = new SaveState();
|
||||
state.putString("some_int,java.math.BigInteger", "0x11");
|
||||
result.h.loadState(state);
|
||||
result.h.invoke();
|
||||
|
||||
Map<String, ValStr<?>> args = waitOn(result.args);
|
||||
assertEquals(Map.of("some_int", intVal(17, "0x11")), args);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoolDefaultValue() throws Throwable {
|
||||
PromptResult result = prompt(PARAM_BOOL);
|
||||
|
||||
+15
-8
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
@@ -18,8 +17,10 @@ package ghidra.app.util.headless;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Locale;
|
||||
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import sun.java2d.HeadlessGraphicsEnvironment;
|
||||
|
||||
public class MyHeadlessGraphicsEnvironment extends GraphicsEnvironment {
|
||||
@@ -78,17 +79,23 @@ public class MyHeadlessGraphicsEnvironment extends GraphicsEnvironment {
|
||||
|
||||
private void getRealGraphicsEnvironemnt() {
|
||||
try {
|
||||
localEnv = (GraphicsEnvironment) Class.forName(preferredGraphicsEnv).newInstance();
|
||||
localEnv = ClassSearcher
|
||||
.forNameSafe(preferredGraphicsEnv, GraphicsEnvironment.class,
|
||||
getClass().getClassLoader())
|
||||
.getConstructor()
|
||||
.newInstance();
|
||||
if (isHeadless()) {
|
||||
localEnv = new HeadlessGraphicsEnvironment(localEnv);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new Error("Could not find class: " + preferredGraphicsEnv);
|
||||
} catch (InstantiationException e) {
|
||||
}
|
||||
catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new Error("Could not instantiate Graphics Environment: " + preferredGraphicsEnv);
|
||||
} catch (IllegalAccessException e) {
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new Error("Could not access Graphics Environment: " + preferredGraphicsEnv);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-6
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -22,10 +22,12 @@ import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.im.InputMethodHighlight;
|
||||
import java.awt.image.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import sun.awt.HeadlessToolkit;
|
||||
|
||||
public class MyHeadlessToolkit extends Toolkit {
|
||||
@@ -163,22 +165,23 @@ public class MyHeadlessToolkit extends Toolkit {
|
||||
|
||||
private void getRealToolkit() {
|
||||
|
||||
Class<?> cls = null;
|
||||
Class<? extends Toolkit> cls = null;
|
||||
try {
|
||||
try {
|
||||
cls = Class.forName(preferredToolkit);
|
||||
cls = ClassSearcher.forNameSafe(preferredToolkit, Toolkit.class,
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
catch (ClassNotFoundException ee) {
|
||||
throw new AWTError("Toolkit not found: " + preferredToolkit);
|
||||
}
|
||||
if (cls != null) {
|
||||
localToolKit = (Toolkit) cls.newInstance();
|
||||
localToolKit = cls.getConstructor().newInstance();
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
localToolKit = new HeadlessToolkit(localToolKit);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new AWTError("Could not instantiate Toolkit: " + preferredToolkit);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
|
||||
Reference in New Issue
Block a user