mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Merge remote-tracking branch 'origin/Ghidra_9.2'
This commit is contained in:
commit
038b701670
39 changed files with 710 additions and 151 deletions
0
Ghidra/Extensions/bundle_examples/Module.manifest
Normal file
0
Ghidra/Extensions/bundle_examples/Module.manifest
Normal file
116
Ghidra/Extensions/bundle_examples/build.gradle
Normal file
116
Ghidra/Extensions/bundle_examples/build.gradle
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/* This extension is different from the others. It produces a zip containing
|
||||||
|
* directories of source bundles and jar bundles.
|
||||||
|
* - Each source directory is added as a sourceset so that the eclipse plugin
|
||||||
|
* can add them to the generated project.
|
||||||
|
* - the source destined to be included as jars are compiled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||||
|
apply plugin: 'eclipse'
|
||||||
|
|
||||||
|
// there is no main jar
|
||||||
|
jar.enabled=false
|
||||||
|
|
||||||
|
eclipse.project.name = 'Xtra Bundle Examples'
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':Base')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def srcDirs = []
|
||||||
|
file(project.projectDir).eachDirMatch(~/.*scripts_.*/) { srcDirs << it.name }
|
||||||
|
|
||||||
|
srcDirs.each {dirName ->
|
||||||
|
sourceSets.create(dirName) {
|
||||||
|
java {
|
||||||
|
srcDir {
|
||||||
|
dirName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and return a jar task for the given source directory
|
||||||
|
def makeJarTask(dirName) {
|
||||||
|
return tasks.create("build${dirName}", Jar) {
|
||||||
|
baseName dirName
|
||||||
|
archiveName "${dirName}.jar"
|
||||||
|
ext.dirName=dirName
|
||||||
|
|
||||||
|
|
||||||
|
from(sourceSets[dirName].output) {
|
||||||
|
include "**"
|
||||||
|
}
|
||||||
|
manifest {
|
||||||
|
def manifestFile=file("${dirName}/META-INF/MANIFEST.MF")
|
||||||
|
// if there is a source manifest, use it
|
||||||
|
if(manifestFile.exists())
|
||||||
|
from manifestFile
|
||||||
|
else // otherwise, use a default manifest
|
||||||
|
attributes \
|
||||||
|
"Bundle-Name": dirName,
|
||||||
|
"Bundle-SymbolicName": dirName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def jarTasks=[
|
||||||
|
makeJarTask("scripts_jar1"),
|
||||||
|
makeJarTask("scripts_jar2")
|
||||||
|
]
|
||||||
|
|
||||||
|
eclipse {
|
||||||
|
classpath {
|
||||||
|
// jar1 and jar2 implement the same classes (with different OSGi package versions)
|
||||||
|
// adding both as source directories would cause errors in eclipse, so remove jar2.
|
||||||
|
sourceSets-=[sourceSets.scripts_jar2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// we need a alternative to the zipExtensions task from
|
||||||
|
// "$rootProject.projectDir/gradle/support/extensionCommon.gradle"
|
||||||
|
task zipExtensions(type: Zip, dependsOn:jarTasks) {
|
||||||
|
def p = this.project
|
||||||
|
archiveName "${rootProject.ext.ZIP_NAME_PREFIX}_${p.name}.zip"
|
||||||
|
destinationDir rootProject.ext.DISTRIBUTION_DIR
|
||||||
|
|
||||||
|
duplicatesStrategy 'exclude'
|
||||||
|
|
||||||
|
from '.'
|
||||||
|
|
||||||
|
srcDirs.each { f ->
|
||||||
|
include f + '/**'
|
||||||
|
}
|
||||||
|
|
||||||
|
include "scripts_*.jar"
|
||||||
|
|
||||||
|
for(jarTask in jarTasks) {
|
||||||
|
from relativePath(jarTask.archivePath)
|
||||||
|
exclude jarTask.dirName
|
||||||
|
}
|
||||||
|
|
||||||
|
into p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registratino with rootProject.createInstallationZip is ususally done in
|
||||||
|
// "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle", but
|
||||||
|
// since we define a custom zipExtensions task (and can't overwrite it), we do
|
||||||
|
// the registration here.
|
||||||
|
rootProject.createInstallationZip {
|
||||||
|
from (this.project.zipExtensions) {
|
||||||
|
into {
|
||||||
|
ZIP_DIR_PREFIX + "/Extensions/Ghidra"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doLast {
|
||||||
|
this.project.zipExtensions.outputs.each {
|
||||||
|
delete it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
Ghidra/Extensions/bundle_examples/certification.manifest
Normal file
7
Ghidra/Extensions/bundle_examples/certification.manifest
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
##VERSION: 2.0
|
||||||
|
Module.manifest||GHIDRA||||END|
|
||||||
|
build.gradle||GHIDRA||||END|
|
||||||
|
extension.properties||GHIDRA||||END|
|
||||||
|
scripts_jar1/META-INF/MANIFEST.MF||GHIDRA||||END|
|
||||||
|
scripts_jar2/META-INF/MANIFEST.MF||GHIDRA||||END|
|
||||||
|
scripts_with_manifest/META-INF/MANIFEST.MF||GHIDRA||||END|
|
5
Ghidra/Extensions/bundle_examples/extension.properties
Normal file
5
Ghidra/Extensions/bundle_examples/extension.properties
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
name=Bundle Examples
|
||||||
|
description=This zip contains example script directories and jar bundles that demonstrate dynamic modularity (OSGi) features in Ghidra scripting. From a code browser menu, select Window -> Bundle Manager. Then select the plus (+) to add bundles. Navigate to the install path for this extension zip, and add all of the directories and jars from the root. In the script manager, select the category Examples/Bundle - each script demonstrates a different feature.
|
||||||
|
author=Ghidra Team
|
||||||
|
createdOn=10/14/2020
|
||||||
|
version=@extversion@
|
|
@ -0,0 +1,7 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Bundle-ManifestVersion: 2
|
||||||
|
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=11))"
|
||||||
|
Bundle-SymbolicName: org.jarlib
|
||||||
|
Bundle-Version: 1.0.0
|
||||||
|
Bundle-Name: Example library
|
||||||
|
Export-Package: org.jarlib;version=1.0.0
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* ###
|
||||||
|
* 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 org.jarlib;
|
||||||
|
|
||||||
|
public class JarUtil {
|
||||||
|
public static String getVersion() {
|
||||||
|
return "1.0";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Bundle-ManifestVersion: 2
|
||||||
|
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=11))"
|
||||||
|
Bundle-SymbolicName: org.jarlib
|
||||||
|
Bundle-Version: 2.0.0
|
||||||
|
Bundle-Name: Example library
|
||||||
|
Export-Package: org.jarlib;version=2.0.0
|
|
@ -0,0 +1,22 @@
|
||||||
|
/* ###
|
||||||
|
* 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 org.jarlib;
|
||||||
|
|
||||||
|
public class JarUtil {
|
||||||
|
public static String getVersion() {
|
||||||
|
return "2.0";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Intra-bundle dependency example.
|
||||||
|
//@category Examples.Bundle
|
||||||
|
|
||||||
|
import org.other.lib.Util; // defined in this bundle, no need to @importpackage
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
|
public class IntraBundleExampleScript extends GhidraScript {
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
println("This script shows the use of " + Util.class.getCanonicalName() +
|
||||||
|
" from within the same bundle.");
|
||||||
|
|
||||||
|
Util.doStuff(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* ###
|
||||||
|
* 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 org.other.lib;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
|
public class Util {
|
||||||
|
/**
|
||||||
|
* An example of a utility function that scripts might want to use.
|
||||||
|
*
|
||||||
|
* @param script the calling script
|
||||||
|
*/
|
||||||
|
public static void doStuff(GhidraScript script) {
|
||||||
|
script.println("in " + Util.class.getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Inter-bundle dependency example.
|
||||||
|
//@category Examples.Bundle
|
||||||
|
//@importpackage org.other.lib
|
||||||
|
|
||||||
|
import org.other.lib.Util; // from another bundle, use @importpackage
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
|
public class InterBundleExampleScript extends GhidraScript {
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
println("This script shows the use of " + Util.class.getCanonicalName() +
|
||||||
|
" from a different bundle.");
|
||||||
|
println(
|
||||||
|
"In this case, the dependency is declared with the metadata comment \"//@importpackage org.other.lib\"");
|
||||||
|
|
||||||
|
Util.doStuff(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Example use of jar bundle
|
||||||
|
//@category Examples.Bundle
|
||||||
|
//@importpackage org.jarlib
|
||||||
|
|
||||||
|
import org.jarlib.JarUtil;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
|
public class UsesJarExampleScript extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
println("This script shows the use of " + JarUtil.class.getCanonicalName() + ".");
|
||||||
|
println(" a class defined in an external jar bundle.");
|
||||||
|
println("There are two versions of the jar in the bundle examples directory,");
|
||||||
|
println(" since \"@importpackage\" declaration doesn't specify a version, either");
|
||||||
|
println(" of the jar bundles, scripts_jar1.jar or scripts_jar2.jar works.");
|
||||||
|
println(" Try enabling only one of the \"scripts_jar*\" bundles and rerun this script.");
|
||||||
|
|
||||||
|
println("Currently using JarUtil version " + JarUtil.getVersion());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Example use of jar bundle with a version constraint
|
||||||
|
//@category Examples.Bundle
|
||||||
|
//@importpackage org.jarlib;version="[2,3)"
|
||||||
|
|
||||||
|
import org.jarlib.JarUtil;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
|
public class UsesJarByVersionExampleScript extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
println("This script shows the use of " + JarUtil.class.getCanonicalName() + ".");
|
||||||
|
println(" a class defined in an external jar bundle.");
|
||||||
|
println("There are two versions of the jar in the bundle examples directory,");
|
||||||
|
println(" since \"@importpackage\" declaration doesn't specify a version, either");
|
||||||
|
println(" of the jar bundles, scripts_jar1.jar or scripts_jar2.jar works.");
|
||||||
|
println(" Try enabling only one of the \"scripts_jar*\" bundles and rerun this script.");
|
||||||
|
|
||||||
|
println("Currently using JarUtil version " + JarUtil.getVersion());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Example script that cleans up actions with a bundle activator.
|
||||||
|
//@category Examples.Bundle
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.action.*;
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.services.CodeViewerService;
|
||||||
|
import ghidra.app.services.ConsoleService;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import internal.MyActivator;
|
||||||
|
import resources.Icons;
|
||||||
|
|
||||||
|
public class ActivatorExampleScript extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
PluginTool tool = state.getTool();
|
||||||
|
println("This script is from a bundle with a custom activator class, " +
|
||||||
|
MyActivator.class.getCanonicalName() + ".");
|
||||||
|
println("When the bundle is activated, its start method is called.");
|
||||||
|
println("When deactivated, the activator's stop method is called.");
|
||||||
|
println("To demonstrate how it can be useful, this script adds an action to the toolbar,");
|
||||||
|
println(" it's a gear icon with tooltip \"Added by script!!\".");
|
||||||
|
println("The activator will remove the action if this bundle is deactivated,");
|
||||||
|
println(" e.g. if this script is modified and the bundle needs to be reloaded.");
|
||||||
|
|
||||||
|
DockingAction action = new DockingAction("Added by script!!", null, false) {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionContext context) {
|
||||||
|
ConsoleService console = tool.getService(ConsoleService.class);
|
||||||
|
CodeViewerService codeviewer = tool.getService(CodeViewerService.class);
|
||||||
|
ProgramLocation loc = codeviewer.getCurrentLocation();
|
||||||
|
console.getStdOut().printf("Current location is %s\n", loc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
action.setToolBarData(new ToolBarData(Icons.CONFIGURE_FILTER_ICON));
|
||||||
|
MyActivator.addAction(tool, action);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/* ###
|
||||||
|
* 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 internal;
|
||||||
|
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
|
import docking.action.DockingAction;
|
||||||
|
import ghidra.app.plugin.core.osgi.GhidraBundleActivator;
|
||||||
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example BundleActivator that manages a {@link DockingAction}.
|
||||||
|
*/
|
||||||
|
public class MyActivator extends GhidraBundleActivator {
|
||||||
|
|
||||||
|
private static PluginTool storedTool;
|
||||||
|
private static DockingAction storedAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add {@code action} to {@code tool} and store both to remove later.
|
||||||
|
*
|
||||||
|
* Note: this can be used exactly once per bundle lifecycle, i.e. between start & stop.
|
||||||
|
*
|
||||||
|
* @param tool the tool to add {@code action} to
|
||||||
|
* @param action the action to add to {@code tool}
|
||||||
|
* @return false if this add operation has already been performed
|
||||||
|
*/
|
||||||
|
public static boolean addAction(PluginTool tool, DockingAction action) {
|
||||||
|
if (storedTool != null || storedAction != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
storedTool = tool;
|
||||||
|
storedAction = action;
|
||||||
|
Swing.runNow(() -> {
|
||||||
|
storedTool.addAction(storedAction);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Ghidra when bundle is activated.
|
||||||
|
*
|
||||||
|
* @param bundleContext the context for this bundle
|
||||||
|
* @param api placeholder for future Ghidra API
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void start(BundleContext bundleContext, Object api) {
|
||||||
|
if (storedAction != null) {
|
||||||
|
Msg.showError(this, null, "Activator error!", "storedAction non-null on bundle start!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Ghidra when bundle is deactivated.
|
||||||
|
*
|
||||||
|
* @param bundleContext the context for this bundle
|
||||||
|
* @param api placeholder for future Ghidra API
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void stop(BundleContext bundleContext, Object api) {
|
||||||
|
if (storedAction != null) {
|
||||||
|
storedAction.dispose();
|
||||||
|
if (storedTool == null) {
|
||||||
|
Msg.showError(this, null, "Activator error!", "storedTool is null!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
storedTool.removeAction(storedAction);
|
||||||
|
}
|
||||||
|
storedTool = null;
|
||||||
|
storedAction = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
// Inter-bundle dependency example where import is done with bundle manifest.
|
||||||
|
//@category Examples.Bundle
|
||||||
|
|
||||||
|
import org.other.lib.Util; // from another bundle, import done in META-INF/MANIFEST.MF
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
|
||||||
|
public class InterBundleManifestExampleScript extends GhidraScript {
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
println("This script shows the use of " + Util.class.getCanonicalName() +
|
||||||
|
" from a different bundle.");
|
||||||
|
println(
|
||||||
|
"In this case, the dependency is declared in the source file, \"META-INF/MANIFEST.MF\"");
|
||||||
|
println(" see the line starting \"Import-Package\"");
|
||||||
|
|
||||||
|
Util.doStuff(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Bundle-ManifestVersion: 2
|
||||||
|
Export-Package: com.mystuff.exports
|
||||||
|
Import-Package: org.other.lib,ghidra.app.script,ghidra.app.plugin.core.osgi
|
||||||
|
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=11))"
|
||||||
|
Bundle-SymbolicName: examples.scripts_with_manifest
|
||||||
|
Bundle-Name: Example Ghidra script directory with manifest.
|
||||||
|
Bundle-Version: 1.0
|
|
@ -62,16 +62,15 @@ public abstract class NavigatableContextAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
||||||
return true;
|
// assume that all Navigatable context actions require a valid program location
|
||||||
|
return context.getLocation() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isAddToPopup(NavigatableActionContext context) {
|
protected boolean isAddToPopup(NavigatableActionContext context) {
|
||||||
return isEnabledForContext(context);
|
return isEnabledForContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void actionPerformed(NavigatableActionContext context) {
|
protected abstract void actionPerformed(NavigatableActionContext context);
|
||||||
// optional for subclasses
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
public boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -22,29 +21,29 @@ import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
|
|
||||||
public abstract class ProgramLocationContextAction extends DockingAction {
|
public abstract class ProgramLocationContextAction extends DockingAction {
|
||||||
|
|
||||||
public ProgramLocationContextAction(String name, String owner) {
|
public ProgramLocationContextAction(String name, String owner) {
|
||||||
super(name, owner);
|
super(name, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
Object contextObject = context.getContextObject();
|
Object contextObject = context.getContextObject();
|
||||||
if (!(contextObject instanceof ProgramLocationActionContext)) {
|
if (!(contextObject instanceof ProgramLocationActionContext)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return isEnabledForContext((ProgramLocationActionContext)contextObject);
|
return isEnabledForContext((ProgramLocationActionContext) contextObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext actionContext) {
|
public void actionPerformed(ActionContext actionContext) {
|
||||||
actionPerformed((ProgramLocationActionContext)actionContext.getContextObject());
|
actionPerformed((ProgramLocationActionContext) actionContext.getContextObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidContext(ActionContext context) {
|
public boolean isValidContext(ActionContext context) {
|
||||||
if (context instanceof ProgramLocationActionContext) {
|
if (context instanceof ProgramLocationActionContext) {
|
||||||
return isValidContext((ProgramLocationActionContext)context);
|
return isValidContext((ProgramLocationActionContext) context);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +51,7 @@ public abstract class ProgramLocationContextAction extends DockingAction {
|
||||||
@Override
|
@Override
|
||||||
public boolean isAddToPopup(ActionContext context) {
|
public boolean isAddToPopup(ActionContext context) {
|
||||||
if (context instanceof ProgramLocationActionContext) {
|
if (context instanceof ProgramLocationActionContext) {
|
||||||
return isAddToPopup((ProgramLocationActionContext)context);
|
return isAddToPopup((ProgramLocationActionContext) context);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -66,13 +65,13 @@ public abstract class ProgramLocationContextAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isEnabledForContext(ProgramLocationActionContext context) {
|
protected boolean isEnabledForContext(ProgramLocationActionContext context) {
|
||||||
return true;
|
// assume that all ProgramLocation context actions require a valid program location
|
||||||
|
return context.getLocation() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void actionPerformed(ProgramLocationActionContext context) {
|
// a version of actionPerformed() that takes a more specific context than our parent
|
||||||
|
protected abstract void actionPerformed(ProgramLocationActionContext context);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
public boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
||||||
for (Class<?> class1 : contextTypes) {
|
for (Class<?> class1 : contextTypes) {
|
||||||
|
|
|
@ -363,13 +363,15 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
|
||||||
|
|
||||||
Point p = e.getLocation();
|
Point p = e.getLocation();
|
||||||
ProgramLocation loc = listingPanel.getProgramLocation(p);
|
ProgramLocation loc = listingPanel.getProgramLocation(p);
|
||||||
|
if (loc == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CodeViewerActionContext context = new CodeViewerActionContext(this, loc);
|
CodeViewerActionContext context = new CodeViewerActionContext(this, loc);
|
||||||
if (loc != null) {
|
for (ProgramDropProvider dropProvider : dropProviders) {
|
||||||
for (ProgramDropProvider dropProvider : dropProviders) {
|
if (dropProvider.isDropOk(context, e)) {
|
||||||
if (dropProvider.isDropOk(context, e)) {
|
curDropProvider = dropProvider;
|
||||||
curDropProvider = dropProvider;
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -110,9 +110,13 @@ class GhidraScriptActionManager {
|
||||||
void restoreScriptsThatAreInTool(SaveState saveState) {
|
void restoreScriptsThatAreInTool(SaveState saveState) {
|
||||||
String[] array = saveState.getStrings(SCRIPT_ACTIONS_KEY, new String[0]);
|
String[] array = saveState.getStrings(SCRIPT_ACTIONS_KEY, new String[0]);
|
||||||
for (String filename : array) {
|
for (String filename : array) {
|
||||||
ScriptInfo info = infoManager.findScriptInfoByName(filename);
|
ResourceFile file = generic.util.Path.fromPathString(filename);
|
||||||
if (info != null) { // the file may have been deleted from disk
|
if (file.exists()) {
|
||||||
provider.getActionManager().createAction(info.getSourceFile());
|
// restore happens early -- the next call will create a new ScriptInfo
|
||||||
|
ScriptInfo info = infoManager.getScriptInfo(file);
|
||||||
|
if (info != null) {
|
||||||
|
createAction(info.getSourceFile());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Msg.info(this, "Cannot find script for keybinding: '" + filename + "'");
|
Msg.info(this, "Cannot find script for keybinding: '" + filename + "'");
|
||||||
|
@ -159,7 +163,7 @@ class GhidraScriptActionManager {
|
||||||
Set<ResourceFile> actionScriptFiles = actionMap.keySet();
|
Set<ResourceFile> actionScriptFiles = actionMap.keySet();
|
||||||
Set<String> scriptPaths = new HashSet<>(actionScriptFiles.size());
|
Set<String> scriptPaths = new HashSet<>(actionScriptFiles.size());
|
||||||
for (ResourceFile file : actionScriptFiles) {
|
for (ResourceFile file : actionScriptFiles) {
|
||||||
scriptPaths.add(file.getName());
|
scriptPaths.add(generic.util.Path.toPathString(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] array = scriptPaths.toArray(new String[scriptPaths.size()]);
|
String[] array = scriptPaths.toArray(new String[scriptPaths.size()]);
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.searchmem.mask;
|
package ghidra.app.plugin.core.searchmem.mask;
|
||||||
|
|
||||||
import docking.ActionContext;
|
|
||||||
import docking.action.MenuData;
|
import docking.action.MenuData;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.context.NavigatableActionContext;
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
|
@ -33,7 +32,7 @@ import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a set of actions that can be performed on a selection to initiate a memory search. All
|
* Defines a set of actions that can be performed on a selection to initiate a memory search. All
|
||||||
* actions will ultimately open the {@link MemSearchDialog} with the search string field
|
* actions will ultimately open the {@code MemSearchDialog} with the search string field
|
||||||
* pre-populated.
|
* pre-populated.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -57,76 +56,67 @@ public class MnemonicSearchPlugin extends Plugin {
|
||||||
static final String MENU_PULLRIGHT = "For Matching Instructions";
|
static final String MENU_PULLRIGHT = "For Matching Instructions";
|
||||||
static final String POPUP_MENU_GROUP = "Search";
|
static final String POPUP_MENU_GROUP = "Search";
|
||||||
|
|
||||||
// Actions (accessible via Tools menu).
|
// Actions (accessible via Tools menu)
|
||||||
private NavigatableContextAction setSearchMnemonicOpsNoConstAction;
|
private NavigatableContextAction setSearchMnemonicOpsNoConstAction;
|
||||||
private NavigatableContextAction setSearchMnemonicOpsConstAction;
|
private NavigatableContextAction setSearchMnemonicOpsConstAction;
|
||||||
private NavigatableContextAction setSearchMnemonicNoOpsNoConstAction;
|
private NavigatableContextAction setSearchMnemonicNoOpsNoConstAction;
|
||||||
|
|
||||||
// Final bit string used to populate the mem search dialog.
|
// Final bit string used to populate the memory search dialog.
|
||||||
public String maskedBitString;
|
public String maskedBitString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param tool
|
* @param tool the tool
|
||||||
*/
|
*/
|
||||||
public MnemonicSearchPlugin(PluginTool tool) {
|
public MnemonicSearchPlugin(PluginTool tool) {
|
||||||
super(tool);
|
super(tool);
|
||||||
createActions();
|
createActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Retrieves the selection region from the program, builds the search string, and pops
|
* Retrieves the selection region from the program, builds the search string, and pops
|
||||||
* up the {@link MemSearchDialog}.
|
* up the MemSearchDialog
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param useOps
|
|
||||||
* @param useConsts
|
|
||||||
*/
|
*/
|
||||||
private void processAction(ActionContext context, boolean useOps, boolean useConsts) {
|
private void processAction(NavigatableActionContext context, boolean useOps,
|
||||||
|
boolean useConsts) {
|
||||||
|
|
||||||
// Make sure the action is of the right type. Users may create a selection from
|
NavigatableActionContext newContext =
|
||||||
// different windows, which will create different action context objects (ie:
|
(NavigatableActionContext) context.getContextObject();
|
||||||
// ListingActionContext, CodeViewerActionContext, etc...). Ultimately they are all
|
|
||||||
// NavigatableActionContext's, so use that.
|
|
||||||
if (context != null && context.getContextObject() instanceof NavigatableActionContext) {
|
|
||||||
NavigatableActionContext newContext =
|
|
||||||
(NavigatableActionContext) context.getContextObject();
|
|
||||||
|
|
||||||
// Grab the program and selection from the context.
|
// Grab the program and selection from the context.
|
||||||
Program program = newContext.getProgram();
|
Program program = newContext.getProgram();
|
||||||
ProgramSelection selection = newContext.getSelection();
|
ProgramSelection selection = newContext.getSelection();
|
||||||
|
|
||||||
// If there are multiple regions selected, let the user know via popup and
|
// If there are multiple regions selected, let the user know via popup and
|
||||||
// exit. This is not allowed.
|
// exit. This is not allowed.
|
||||||
// Note: We could disable the menu items and not allow this operation to
|
// Note: We could disable the menu items and not allow this operation to
|
||||||
// be initiated at all, but the decision was made to do it this way
|
// be initiated at all, but the decision was made to do it this way
|
||||||
// so it's more obvious to the user why the operation is invalid.
|
// so it's more obvious to the user why the operation is invalid.
|
||||||
if (selection.getNumAddressRanges() > 1) {
|
if (selection.getNumAddressRanges() > 1) {
|
||||||
Msg.showInfo(this, context.getComponentProvider().getComponent(),
|
Msg.showInfo(this, context.getComponentProvider().getComponent(),
|
||||||
"Mnemonic Search Error",
|
"Mnemonic Search Error",
|
||||||
"Multiple selected regions are not allowed; please limit to one.");
|
"Multiple selected regions are not allowed; please limit to one.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the mask information (this is based solely on the menu action
|
// Store the mask information (this is based solely on the menu action
|
||||||
// that initiated this whole operation.
|
// that initiated this whole operation.
|
||||||
SLMaskControl maskControl = new SLMaskControl(useOps, useConsts);
|
SLMaskControl maskControl = new SLMaskControl(useOps, useConsts);
|
||||||
MaskGenerator generator = new MaskGenerator(maskControl);
|
MaskGenerator generator = new MaskGenerator(maskControl);
|
||||||
MaskValue mask = generator.getMask(program, selection);
|
MaskValue mask = generator.getMask(program, selection);
|
||||||
|
|
||||||
// Now build the search string and set up the search service. This preps the mem search
|
// Now build the search string and set up the search service. This preps the mem search
|
||||||
// dialog with the proper search string.
|
// dialog with the proper search string.
|
||||||
if (mask != null) {
|
if (mask != null) {
|
||||||
maskedBitString = createMaskedBitString(mask.getValue(), mask.getMask());
|
maskedBitString = createMaskedBitString(mask.getValue(), mask.getMask());
|
||||||
byte[] maskedBytes = maskedBitString.getBytes();
|
byte[] maskedBytes = maskedBitString.getBytes();
|
||||||
|
|
||||||
MemorySearchService memorySearchService =
|
MemorySearchService memorySearchService =
|
||||||
tool.getService(MemorySearchService.class);
|
tool.getService(MemorySearchService.class);
|
||||||
memorySearchService.setIsMnemonic(true);
|
memorySearchService.setIsMnemonic(true);
|
||||||
memorySearchService.search(maskedBytes, newContext);
|
memorySearchService.search(maskedBytes, newContext);
|
||||||
memorySearchService.setSearchText(maskedBitString);
|
memorySearchService.setSearchText(maskedBitString);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +138,7 @@ public class MnemonicSearchPlugin extends Plugin {
|
||||||
new NavigatableContextAction("Include Operands (except constants)", getName()) {
|
new NavigatableContextAction("Include Operands (except constants)", getName()) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(NavigatableActionContext context) {
|
||||||
processAction(context, true, false);
|
processAction(context, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,18 +157,19 @@ public class MnemonicSearchPlugin extends Plugin {
|
||||||
//
|
//
|
||||||
// ACTION 2: Search for instructions, including operands.
|
// ACTION 2: Search for instructions, including operands.
|
||||||
//
|
//
|
||||||
setSearchMnemonicOpsConstAction = new NavigatableContextAction("Include Operands", getName()) {
|
setSearchMnemonicOpsConstAction =
|
||||||
|
new NavigatableContextAction("Include Operands", getName()) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(NavigatableActionContext context) {
|
||||||
processAction(context, true, true);
|
processAction(context, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
||||||
return context.hasSelection();
|
return context.hasSelection();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setSearchMnemonicOpsConstAction.setMenuBarData(new MenuData(new String[] { "&Search",
|
setSearchMnemonicOpsConstAction.setMenuBarData(new MenuData(new String[] { "&Search",
|
||||||
MENU_PULLRIGHT, "Include Operands" }, null, group, MenuData.NO_MNEMONIC, "2"));
|
MENU_PULLRIGHT, "Include Operands" }, null, group, MenuData.NO_MNEMONIC, "2"));
|
||||||
|
@ -191,7 +182,7 @@ public class MnemonicSearchPlugin extends Plugin {
|
||||||
new NavigatableContextAction("Exclude Operands", getName()) {
|
new NavigatableContextAction("Exclude Operands", getName()) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(NavigatableActionContext context) {
|
||||||
processAction(context, false, false);
|
processAction(context, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,12 +205,8 @@ public class MnemonicSearchPlugin extends Plugin {
|
||||||
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
|
tool.setMenuGroup(new String[] { MENU_PULLRIGHT }, POPUP_MENU_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns a single string based on the masked bits.
|
* Returns a single string based on the masked bits
|
||||||
*
|
|
||||||
* @param values
|
|
||||||
* @param masks
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private String createMaskedBitString(byte values[], byte masks[]) {
|
private String createMaskedBitString(byte values[], byte masks[]) {
|
||||||
|
|
||||||
|
|
|
@ -1171,7 +1171,10 @@ class FSBActionManager {
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
FSRL containerFSRL = FSBUtils.getFileFSRLFromContext(context);
|
FSRL containerFSRL = FSBUtils.getFileFSRLFromContext(context);
|
||||||
if (containerFSRL != null && context.getContextObject() instanceof FSBFileNode) {
|
if (containerFSRL != null && context.getContextObject() instanceof FSBFileNode) {
|
||||||
FSBFileNode fileNode = (FSBFileNode) context.getContextObject();
|
FSBFileNode xfileNode = (FSBFileNode) context.getContextObject();
|
||||||
|
FSBFileNode modelFileNode =
|
||||||
|
(FSBFileNode) gTree.getModelNodeForPath(xfileNode.getTreePath());
|
||||||
|
|
||||||
gTree.runTask(monitor -> {
|
gTree.runTask(monitor -> {
|
||||||
try {
|
try {
|
||||||
FileSystemRef fsRef =
|
FileSystemRef fsRef =
|
||||||
|
@ -1185,17 +1188,12 @@ class FSBActionManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, fileNode);
|
FSBRootNode nestedRootNode = new FSBRootNode(fsRef, modelFileNode);
|
||||||
FSBRootNode containingFSBRootNode =
|
|
||||||
FSBNode.findContainingFileSystemFSBRootNode(fileNode);
|
|
||||||
if (containingFSBRootNode != null) {
|
|
||||||
containingFSBRootNode.getSubRootNodes().add(nestedRootNode);
|
|
||||||
}
|
|
||||||
nestedRootNode.setChildren(nestedRootNode.generateChildren(monitor));
|
nestedRootNode.setChildren(nestedRootNode.generateChildren(monitor));
|
||||||
|
|
||||||
int indexInParent = fileNode.getIndexInParent();
|
int indexInParent = modelFileNode.getIndexInParent();
|
||||||
GTreeNode parent = fileNode.getParent();
|
GTreeNode parent = modelFileNode.getParent();
|
||||||
parent.removeNode(fileNode);
|
parent.removeNode(modelFileNode);
|
||||||
parent.addNode(indexInParent, nestedRootNode);
|
parent.addNode(indexInParent, nestedRootNode);
|
||||||
gTree.expandPath(nestedRootNode);
|
gTree.expandPath(nestedRootNode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,9 @@ public class GraphAST extends GhidraScript {
|
||||||
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
|
// graphDisplay.defineVertexAttribute(CODE_ATTRIBUTE); //
|
||||||
// graphDisplay.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
// graphDisplay.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
|
||||||
// graphDisplay.defineEdgeAttribute(EDGE_TYPE_ATTRIBUTE);
|
// graphDisplay.defineEdgeAttribute(EDGE_TYPE_ATTRIBUTE);
|
||||||
graphDisplay.setGraph(graph, "Data-flow AST", false, monitor);
|
String description = "AST Data Flow Graph For " + func.getName();
|
||||||
|
|
||||||
|
graphDisplay.setGraph(graph, description, false, monitor);
|
||||||
|
|
||||||
// Install a handler so the selection/location will map
|
// Install a handler so the selection/location will map
|
||||||
graphDisplay.setGraphDisplayListener(
|
graphDisplay.setGraphDisplayListener(
|
||||||
|
|
|
@ -214,6 +214,13 @@ public class DecompilerController {
|
||||||
decompilerMgr.decompile(program, location, null, debugFile, true);
|
decompilerMgr.decompile(program, location, null, debugFile, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasDecompileResults() {
|
||||||
|
if (currentDecompileData != null) {
|
||||||
|
return currentDecompileData.hasDecompileResults();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public ClangTokenGroup getCCodeModel() {
|
public ClangTokenGroup getCCodeModel() {
|
||||||
return currentDecompileData.getCCodeMarkup();
|
return currentDecompileData.getCCodeMarkup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,6 +224,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (!controller.hasDecompileResults()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Address entryPoint = function.getEntryPoint();
|
Address entryPoint = function.getEntryPoint();
|
||||||
boolean isDecompiling = controller.isDecompiling();
|
boolean isDecompiling = controller.isDecompiling();
|
||||||
return new DecompilerActionContext(this, entryPoint, isDecompiling);
|
return new DecompilerActionContext(this, entryPoint, isDecompiling);
|
||||||
|
|
|
@ -121,6 +121,7 @@ public class ASTGraphTask extends Task {
|
||||||
|
|
||||||
String description =
|
String description =
|
||||||
graphType == GraphType.DATA_FLOW_GRAPH ? "AST Data Flow" : "AST Control Flow";
|
graphType == GraphType.DATA_FLOW_GRAPH ? "AST Data Flow" : "AST Control Flow";
|
||||||
|
description = description + " for " + hfunction.getFunction().getName();
|
||||||
display.setGraph(graph, description, false, monitor);
|
display.setGraph(graph, description, false, monitor);
|
||||||
// set the graph location
|
// set the graph location
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
class ExportAttributedGraphDisplay implements GraphDisplay {
|
class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
|
|
||||||
private final PluginTool pluginTool;
|
private final PluginTool pluginTool;
|
||||||
private String description;
|
private String title;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the initial display, the graph-less visualization viewer, and its controls
|
* Create the initial display, the graph-less visualization viewer, and its controls
|
||||||
|
@ -86,9 +86,9 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setGraph(AttributedGraph graphData, String description, boolean append,
|
public void setGraph(AttributedGraph graphData, String title, boolean append,
|
||||||
TaskMonitor monitor) {
|
TaskMonitor monitor) {
|
||||||
this.description = description;
|
this.title = title;
|
||||||
doSetGraphData(graphData);
|
doSetGraphData(graphData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +106,8 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getGraphDescription() {
|
public String getGraphTitle() {
|
||||||
return description;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
|
Logger log = Logger.getLogger(DefaultGraphDisplay.class.getName());
|
||||||
|
|
||||||
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
private GraphDisplayListener listener = new DummyGraphDisplayListener();
|
||||||
private String description;
|
private String title;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the {@link Graph} to visualize
|
* the {@link Graph} to visualize
|
||||||
|
@ -892,20 +892,21 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
/**
|
/**
|
||||||
* consume a {@link Graph} and display it
|
* consume a {@link Graph} and display it
|
||||||
* @param graph the graph to display or consume
|
* @param graph the graph to display or consume
|
||||||
* @param description a description of the graph
|
* @param title a title for the graph
|
||||||
* @param append if true, append the new graph to any existing graph.
|
* @param append if true, append the new graph to any existing graph.
|
||||||
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setGraph(AttributedGraph graph, String description, boolean append,
|
public void setGraph(AttributedGraph graph, String title, boolean append,
|
||||||
TaskMonitor monitor) {
|
TaskMonitor monitor) {
|
||||||
iconCache.clear();
|
iconCache.clear();
|
||||||
|
|
||||||
if (append && Objects.equals(description, this.description) && this.graph != null) {
|
if (append && Objects.equals(title, this.title) && this.graph != null) {
|
||||||
graph = mergeGraphs(graph, this.graph);
|
graph = mergeGraphs(graph, this.graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.description = description;
|
this.title = title;
|
||||||
|
componentProvider.setTitle(title);
|
||||||
int count = graph.getVertexCount();
|
int count = graph.getVertexCount();
|
||||||
if (count > MAX_NODES) {
|
if (count > MAX_NODES) {
|
||||||
Msg.showWarn(this, null, "Graph Not Rendered - Too many nodes!",
|
Msg.showWarn(this, null, "Graph Not Rendered - Too many nodes!",
|
||||||
|
@ -1014,13 +1015,9 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
viewer.repaint();
|
viewer.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return a description of this graph
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getGraphDescription() {
|
public String getGraphTitle() {
|
||||||
return description;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,7 +41,6 @@ public class DefaultGraphDisplayComponentProvider extends ComponentProviderAdapt
|
||||||
setIcon(DefaultDisplayGraphIcons.PROGRAM_GRAPH_ICON);
|
setIcon(DefaultDisplayGraphIcons.PROGRAM_GRAPH_ICON);
|
||||||
setTransient();
|
setTransient();
|
||||||
setWindowGroup(WINDOW_GROUP);
|
setWindowGroup(WINDOW_GROUP);
|
||||||
setSubTitle(Integer.toString(display.getId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class DefaultGraphDisplayProvider implements GraphDisplayProvider {
|
||||||
private final Set<DefaultGraphDisplay> displays = new HashSet<>();
|
private final Set<DefaultGraphDisplay> displays = new HashSet<>();
|
||||||
private PluginTool pluginTool;
|
private PluginTool pluginTool;
|
||||||
private Options options;
|
private Options options;
|
||||||
private int displayCounter;
|
private int displayCounter = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
|
@ -153,7 +153,7 @@ public class BlockGraphTask extends Task {
|
||||||
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
||||||
codeLimitPerBlock + 1);
|
codeLimitPerBlock + 1);
|
||||||
}
|
}
|
||||||
display.setGraph(graph, actionName, appendGraph, monitor);
|
display.setGraph(graph, getDescription(), appendGraph, monitor);
|
||||||
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
// initialize the graph location, but don't have the graph send an event
|
// initialize the graph location, but don't have the graph send an event
|
||||||
|
@ -175,6 +175,17 @@ public class BlockGraphTask extends Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getDescription() {
|
||||||
|
String description = actionName;
|
||||||
|
if (selection != null && !selection.isEmpty()) {
|
||||||
|
description += ": " + selection.getMinAddress();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
description += " (Entire Program)";
|
||||||
|
}
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the maximum number of code lines which will be used per block when
|
* Set the maximum number of code lines which will be used per block when
|
||||||
* showCode is enabled.
|
* showCode is enabled.
|
||||||
|
|
|
@ -277,11 +277,11 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphBlockFlow() {
|
private void graphBlockFlow() {
|
||||||
graph("Flow Graph", blockModelService.getActiveBlockModelName(), false);
|
graph("Block Flow Graph", blockModelService.getActiveBlockModelName(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphCodeFlow() {
|
private void graphCodeFlow() {
|
||||||
graph("Code Graph", blockModelService.getActiveBlockModelName(), true);
|
graph("Code Flow Graph", blockModelService.getActiveBlockModelName(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphSubroutines() {
|
private void graphSubroutines() {
|
||||||
|
@ -289,7 +289,7 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphSubroutinesUsing(String modelName) {
|
private void graphSubroutinesUsing(String modelName) {
|
||||||
graph("Call Graph", modelName, false);
|
graph("Call Graph (" + modelName + ")", modelName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graph(String actionName, String modelName, boolean showCode) {
|
private void graph(String actionName, String modelName, boolean showCode) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
private Set<String> definedVertexAttributes = new HashSet<>();
|
private Set<String> definedVertexAttributes = new HashSet<>();
|
||||||
private Set<String> definedEdgeAttributes = new HashSet<>();
|
private Set<String> definedEdgeAttributes = new HashSet<>();
|
||||||
private AttributedGraph graph;
|
private AttributedGraph graph;
|
||||||
private String graphDescription;
|
private String title;
|
||||||
private GraphDisplayListener listener;
|
private GraphDisplayListener listener;
|
||||||
private AttributedVertex focusedVertex;
|
private AttributedVertex focusedVertex;
|
||||||
private Set<AttributedVertex> currentSelection;
|
private Set<AttributedVertex> currentSelection;
|
||||||
|
@ -80,11 +80,11 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setGraph(AttributedGraph graph, String description, boolean append,
|
public void setGraph(AttributedGraph graph, String title, boolean append,
|
||||||
TaskMonitor monitor)
|
TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.graphDescription = description;
|
this.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,8 +98,8 @@ public class TestGraphDisplay implements GraphDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getGraphDescription() {
|
public String getGraphTitle() {
|
||||||
return graphDescription;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -260,6 +260,11 @@ abstract class CoreGTreeNode implements Cloneable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to dispose filtered "clone" nodes. When a filter is applied to the tree,
|
||||||
|
* the nodes that matched are "shallow" cloned, so when the filter is removed, we don't
|
||||||
|
* want to do a full dispose on the nodes, just clean up the parent-child references.
|
||||||
|
*/
|
||||||
final void disposeClones() {
|
final void disposeClones() {
|
||||||
List<GTreeNode> oldChildren;
|
List<GTreeNode> oldChildren;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
|
|
@ -243,12 +243,17 @@ public class GTree extends JPanel implements BusyListener {
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
filterUpdateManager.dispose();
|
filterUpdateManager.dispose();
|
||||||
worker.dispose();
|
worker.dispose();
|
||||||
GTreeNode root = model.getModelRoot();
|
|
||||||
if (root != null) {
|
if (realModelRootNode != null) {
|
||||||
root.dispose();
|
realModelRootNode.dispose();
|
||||||
|
}
|
||||||
|
// if there is a filter applied, clean up the filtered nodes. Note that filtered nodes
|
||||||
|
// are expected to be shallow clones of the model nodes, so we don't want to call full
|
||||||
|
// dispose on the filtered nodes because internal clean-up should happen when the
|
||||||
|
// model nodes are disposed. The disposeClones just breaks the child-parent ties.
|
||||||
|
if (realViewRootNode != null && realViewRootNode != realModelRootNode) {
|
||||||
|
realViewRootNode.disposeClones();
|
||||||
}
|
}
|
||||||
realModelRootNode.dispose();
|
|
||||||
realViewRootNode.dispose();
|
|
||||||
model.dispose();
|
model.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,11 @@ public final class DataUtilities {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA &&
|
||||||
|
!Undefined.isUndefined(existingDT)) {
|
||||||
|
throw new CodeUnitInsertionException("Could not create Data at address " + addr);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for external reference on pointer
|
// Check for external reference on pointer
|
||||||
if (existingDT instanceof Pointer) {
|
if (existingDT instanceof Pointer) {
|
||||||
// TODO: This can probably be eliminated
|
// TODO: This can probably be eliminated
|
||||||
|
@ -199,11 +204,6 @@ public final class DataUtilities {
|
||||||
private static void checkEnoughSpace(Program program, Address addr, int existingDataLen,
|
private static void checkEnoughSpace(Program program, Address addr, int existingDataLen,
|
||||||
DataTypeInstance dti, ClearDataMode mode) throws CodeUnitInsertionException {
|
DataTypeInstance dti, ClearDataMode mode) throws CodeUnitInsertionException {
|
||||||
Listing listing = program.getListing();
|
Listing listing = program.getListing();
|
||||||
// int newSize = dti.getLength();
|
|
||||||
// if (newSize <= existingDataLen) {
|
|
||||||
// listing.clearCodeUnits(addr, addr, false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
try {
|
try {
|
||||||
Address end = addr.addNoWrap(existingDataLen - 1);
|
Address end = addr.addNoWrap(existingDataLen - 1);
|
||||||
Address newEnd = addr.addNoWrap(dti.getLength() - 1);
|
Address newEnd = addr.addNoWrap(dti.getLength() - 1);
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class PointerDataType extends BuiltIn implements Pointer {
|
||||||
* organization when resolved.
|
* organization when resolved.
|
||||||
*/
|
*/
|
||||||
public PointerDataType() {
|
public PointerDataType() {
|
||||||
this(DataType.DEFAULT, -1, null);
|
this(null, -1, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,7 +72,7 @@ public class PointerDataType extends BuiltIn implements Pointer {
|
||||||
* @param dtm data-type manager whose data organization should be used
|
* @param dtm data-type manager whose data organization should be used
|
||||||
*/
|
*/
|
||||||
public PointerDataType(DataTypeManager dtm) {
|
public PointerDataType(DataTypeManager dtm) {
|
||||||
this(DataType.DEFAULT, -1, dtm);
|
this(null, -1, dtm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -119,12 +119,12 @@ public interface GraphDisplay {
|
||||||
/**
|
/**
|
||||||
* Sets the graph to be displayed or consumed by this graph display
|
* Sets the graph to be displayed or consumed by this graph display
|
||||||
* @param graph the graph to display or consume
|
* @param graph the graph to display or consume
|
||||||
* @param description a description of the graph
|
* @param title a title for the graph
|
||||||
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
* @param monitor a {@link TaskMonitor} which can be used to cancel the graphing operation
|
||||||
* @param append if true, append the new graph to any existing graph.
|
* @param append if true, append the new graph to any existing graph.
|
||||||
* @throws CancelledException thrown if the graphing operation was cancelled
|
* @throws CancelledException thrown if the graphing operation was cancelled
|
||||||
*/
|
*/
|
||||||
public void setGraph(AttributedGraph graph, String description, boolean append,
|
public void setGraph(AttributedGraph graph, String title, boolean append,
|
||||||
TaskMonitor monitor)
|
TaskMonitor monitor)
|
||||||
throws CancelledException;
|
throws CancelledException;
|
||||||
|
|
||||||
|
@ -141,10 +141,10 @@ public interface GraphDisplay {
|
||||||
public void updateVertexName(AttributedVertex vertex, String newName);
|
public void updateVertexName(AttributedVertex vertex, String newName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the description of the current graph
|
* Returns the title of the current graph
|
||||||
* @return the description of the current graph
|
* @return the title of the current graph
|
||||||
*/
|
*/
|
||||||
public String getGraphDescription();
|
public String getGraphTitle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the action to the graph display. Not all GraphDisplays support adding custom
|
* Adds the action to the graph display. Not all GraphDisplays support adding custom
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue