GP-707: GhidraDev updates

- New wizard to import a module source dir
- Ghidra won't launch if build dir is present
- Better validation when exporting extension
This commit is contained in:
Ryan Kurtz 2024-02-23 08:50:28 -05:00
parent fc0e3e1b6f
commit 91cb801521
20 changed files with 568 additions and 61 deletions

View file

@ -163,19 +163,19 @@
<setEntry value="ch.qos.logback.classic@default:default"/> <setEntry value="ch.qos.logback.classic@default:default"/>
<setEntry value="ch.qos.logback.core@default:default"/> <setEntry value="ch.qos.logback.core@default:default"/>
<setEntry value="com.google.gson@default:default"/> <setEntry value="com.google.gson@default:default"/>
<setEntry value="com.google.guava*30.1.0.v20221112-0806@default:default"/>
<setEntry value="com.google.guava*32.1.2.jre@default:default"/>
<setEntry value="com.google.guava.failureaccess@default:default"/> <setEntry value="com.google.guava.failureaccess@default:default"/>
<setEntry value="com.google.guava@default:default"/>
<setEntry value="com.ibm.icu@default:default"/> <setEntry value="com.ibm.icu@default:default"/>
<setEntry value="com.python.pydev.analysis@default:default"/> <setEntry value="com.python.pydev.analysis@default:default"/>
<setEntry value="com.python.pydev.debug@default:default"/> <setEntry value="com.python.pydev.debug@default:default"/>
<setEntry value="com.python.pydev.refactoring@default:default"/> <setEntry value="com.python.pydev.refactoring@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/> <setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna@default:default"/> <setEntry value="com.sun.jna@default:default"/>
<setEntry value="jakarta.servlet-api*4.0.0@default:default"/> <setEntry value="jakarta.annotation-api*1.3.5@default:default"/>
<setEntry value="jakarta.servlet-api*5.0.0@default:default"/> <setEntry value="jakarta.annotation-api*2.1.1@default:default"/>
<setEntry value="javax.annotation@default:default"/> <setEntry value="jakarta.inject.jakarta.inject-api*1.0.5@default:default"/>
<setEntry value="javax.inject@default:default"/> <setEntry value="jakarta.inject.jakarta.inject-api*2.0.1@default:default"/>
<setEntry value="jakarta.servlet-api@default:default"/>
<setEntry value="javax.xml@default:default"/> <setEntry value="javax.xml@default:default"/>
<setEntry value="jaxen@default:default"/> <setEntry value="jaxen@default:default"/>
<setEntry value="org.apache.aries.spifly.dynamic.bundle@default:default"/> <setEntry value="org.apache.aries.spifly.dynamic.bundle@default:default"/>
@ -184,10 +184,10 @@
<setEntry value="org.apache.batik.i18n@default:default"/> <setEntry value="org.apache.batik.i18n@default:default"/>
<setEntry value="org.apache.batik.util@default:default"/> <setEntry value="org.apache.batik.util@default:default"/>
<setEntry value="org.apache.commons.cli@default:default"/> <setEntry value="org.apache.commons.cli@default:default"/>
<setEntry value="org.apache.commons.codec@default:default"/>
<setEntry value="org.apache.commons.commons-codec@default:default"/> <setEntry value="org.apache.commons.commons-codec@default:default"/>
<setEntry value="org.apache.commons.commons-io@default:default"/> <setEntry value="org.apache.commons.commons-io@default:default"/>
<setEntry value="org.apache.commons.jxpath@default:default"/> <setEntry value="org.apache.commons.jxpath@default:default"/>
<setEntry value="org.apache.commons.lang3@default:default"/>
<setEntry value="org.apache.commons.logging@default:default"/> <setEntry value="org.apache.commons.logging@default:default"/>
<setEntry value="org.apache.felix.gogo.command@default:default"/> <setEntry value="org.apache.felix.gogo.command@default:default"/>
<setEntry value="org.apache.felix.gogo.runtime@default:default"/> <setEntry value="org.apache.felix.gogo.runtime@default:default"/>
@ -204,9 +204,8 @@
<setEntry value="org.eclipse.buildship.compat@default:default"/> <setEntry value="org.eclipse.buildship.compat@default:default"/>
<setEntry value="org.eclipse.buildship.core@default:default"/> <setEntry value="org.eclipse.buildship.core@default:default"/>
<setEntry value="org.eclipse.buildship.ui@default:default"/> <setEntry value="org.eclipse.buildship.ui@default:default"/>
<setEntry value="org.eclipse.cdt.core.macosx*5.3.0.201502131403@default:default"/>
<setEntry value="org.eclipse.cdt.core.native*5.7.0.201502131403@default:default"/> <setEntry value="org.eclipse.cdt.core.native*5.7.0.201502131403@default:default"/>
<setEntry value="org.eclipse.cdt.core.win32*5.4.0.201502131403@default:false"/>
<setEntry value="org.eclipse.cdt.core.win32.x86_64*5.3.0.201502131403@default:false"/>
<setEntry value="org.eclipse.cdt.core@default:default"/> <setEntry value="org.eclipse.cdt.core@default:default"/>
<setEntry value="org.eclipse.cdt.ui@default:default"/> <setEntry value="org.eclipse.cdt.ui@default:default"/>
<setEntry value="org.eclipse.compare.core@default:default"/> <setEntry value="org.eclipse.compare.core@default:default"/>
@ -220,13 +219,10 @@
<setEntry value="org.eclipse.core.expressions@default:default"/> <setEntry value="org.eclipse.core.expressions@default:default"/>
<setEntry value="org.eclipse.core.externaltools@default:default"/> <setEntry value="org.eclipse.core.externaltools@default:default"/>
<setEntry value="org.eclipse.core.filebuffers@default:default"/> <setEntry value="org.eclipse.core.filebuffers@default:default"/>
<setEntry value="org.eclipse.core.filesystem.win32.x86_64@default:false"/> <setEntry value="org.eclipse.core.filesystem.macosx@default:default"/>
<setEntry value="org.eclipse.core.filesystem@default:default"/> <setEntry value="org.eclipse.core.filesystem@default:default"/>
<setEntry value="org.eclipse.core.jobs@default:default"/> <setEntry value="org.eclipse.core.jobs@default:default"/>
<setEntry value="org.eclipse.core.net.win32.x86_64@default:false"/>
<setEntry value="org.eclipse.core.net.win32@default:false"/>
<setEntry value="org.eclipse.core.net@default:default"/> <setEntry value="org.eclipse.core.net@default:default"/>
<setEntry value="org.eclipse.core.resources.win32.x86_64@default:false"/>
<setEntry value="org.eclipse.core.resources@default:default"/> <setEntry value="org.eclipse.core.resources@default:default"/>
<setEntry value="org.eclipse.core.runtime@default:true"/> <setEntry value="org.eclipse.core.runtime@default:true"/>
<setEntry value="org.eclipse.core.variables@default:default"/> <setEntry value="org.eclipse.core.variables@default:default"/>
@ -250,9 +246,9 @@
<setEntry value="org.eclipse.e4.ui.model.workbench@default:default"/> <setEntry value="org.eclipse.e4.ui.model.workbench@default:default"/>
<setEntry value="org.eclipse.e4.ui.progress@default:default"/> <setEntry value="org.eclipse.e4.ui.progress@default:default"/>
<setEntry value="org.eclipse.e4.ui.services@default:default"/> <setEntry value="org.eclipse.e4.ui.services@default:default"/>
<setEntry value="org.eclipse.e4.ui.swt.win32@default:false"/>
<setEntry value="org.eclipse.e4.ui.widgets@default:default"/> <setEntry value="org.eclipse.e4.ui.widgets@default:default"/>
<setEntry value="org.eclipse.e4.ui.workbench.addons.swt@default:default"/> <setEntry value="org.eclipse.e4.ui.workbench.addons.swt@default:default"/>
<setEntry value="org.eclipse.e4.ui.workbench.renderers.swt.cocoa@default:default"/>
<setEntry value="org.eclipse.e4.ui.workbench.renderers.swt@default:default"/> <setEntry value="org.eclipse.e4.ui.workbench.renderers.swt@default:default"/>
<setEntry value="org.eclipse.e4.ui.workbench.swt@default:default"/> <setEntry value="org.eclipse.e4.ui.workbench.swt@default:default"/>
<setEntry value="org.eclipse.e4.ui.workbench3@default:default"/> <setEntry value="org.eclipse.e4.ui.workbench3@default:default"/>
@ -289,15 +285,15 @@
<setEntry value="org.eclipse.equinox.p2.ui@default:default"/> <setEntry value="org.eclipse.equinox.p2.ui@default:default"/>
<setEntry value="org.eclipse.equinox.preferences@default:default"/> <setEntry value="org.eclipse.equinox.preferences@default:default"/>
<setEntry value="org.eclipse.equinox.registry@default:default"/> <setEntry value="org.eclipse.equinox.registry@default:default"/>
<setEntry value="org.eclipse.equinox.security.macosx@default:default"/>
<setEntry value="org.eclipse.equinox.security.ui@default:default"/> <setEntry value="org.eclipse.equinox.security.ui@default:default"/>
<setEntry value="org.eclipse.equinox.security.win32.x86_64@default:false"/>
<setEntry value="org.eclipse.equinox.security@default:default"/> <setEntry value="org.eclipse.equinox.security@default:default"/>
<setEntry value="org.eclipse.equinox.simpleconfigurator.manipulator@default:default"/> <setEntry value="org.eclipse.equinox.simpleconfigurator.manipulator@default:default"/>
<setEntry value="org.eclipse.equinox.simpleconfigurator@1:true"/> <setEntry value="org.eclipse.equinox.simpleconfigurator@1:true"/>
<setEntry value="org.eclipse.help.base@default:default"/> <setEntry value="org.eclipse.help.base@default:default"/>
<setEntry value="org.eclipse.help.ui@default:default"/> <setEntry value="org.eclipse.help.ui@default:default"/>
<setEntry value="org.eclipse.help@default:default"/> <setEntry value="org.eclipse.help@default:default"/>
<setEntry value="org.eclipse.jdt.annotation*2.2.700.v20220826-1026@default:default"/> <setEntry value="org.eclipse.jdt.annotation*2.2.800.v20231029-1039@default:default"/>
<setEntry value="org.eclipse.jdt.core.compiler.batch@default:default"/> <setEntry value="org.eclipse.jdt.core.compiler.batch@default:default"/>
<setEntry value="org.eclipse.jdt.core.manipulation@default:default"/> <setEntry value="org.eclipse.jdt.core.manipulation@default:default"/>
<setEntry value="org.eclipse.jdt.core@default:default"/> <setEntry value="org.eclipse.jdt.core@default:default"/>
@ -309,14 +305,18 @@
<setEntry value="org.eclipse.jdt.launching@default:default"/> <setEntry value="org.eclipse.jdt.launching@default:default"/>
<setEntry value="org.eclipse.jdt.ui@default:default"/> <setEntry value="org.eclipse.jdt.ui@default:default"/>
<setEntry value="org.eclipse.jem.util@default:default"/> <setEntry value="org.eclipse.jem.util@default:default"/>
<setEntry value="org.eclipse.jetty.ee8.security@default:default"/>
<setEntry value="org.eclipse.jetty.ee8.server@default:default"/>
<setEntry value="org.eclipse.jetty.ee8.servlet@default:default"/>
<setEntry value="org.eclipse.jetty.ee8.webapp@default:default"/>
<setEntry value="org.eclipse.jetty.http@default:default"/> <setEntry value="org.eclipse.jetty.http@default:default"/>
<setEntry value="org.eclipse.jetty.io@default:default"/> <setEntry value="org.eclipse.jetty.io@default:default"/>
<setEntry value="org.eclipse.jetty.security@default:default"/> <setEntry value="org.eclipse.jetty.security@default:default"/>
<setEntry value="org.eclipse.jetty.server@default:default"/> <setEntry value="org.eclipse.jetty.server@default:default"/>
<setEntry value="org.eclipse.jetty.servlet@default:default"/> <setEntry value="org.eclipse.jetty.servlet-api@default:default"/>
<setEntry value="org.eclipse.jetty.session@default:default"/>
<setEntry value="org.eclipse.jetty.util.ajax@default:default"/> <setEntry value="org.eclipse.jetty.util.ajax@default:default"/>
<setEntry value="org.eclipse.jetty.util@default:default"/> <setEntry value="org.eclipse.jetty.util@default:default"/>
<setEntry value="org.eclipse.jetty.webapp@default:default"/>
<setEntry value="org.eclipse.jetty.xml@default:default"/> <setEntry value="org.eclipse.jetty.xml@default:default"/>
<setEntry value="org.eclipse.jface.databinding@default:default"/> <setEntry value="org.eclipse.jface.databinding@default:default"/>
<setEntry value="org.eclipse.jface.notifications@default:default"/> <setEntry value="org.eclipse.jface.notifications@default:default"/>
@ -324,30 +324,33 @@
<setEntry value="org.eclipse.jface@default:default"/> <setEntry value="org.eclipse.jface@default:default"/>
<setEntry value="org.eclipse.ltk.core.refactoring@default:default"/> <setEntry value="org.eclipse.ltk.core.refactoring@default:default"/>
<setEntry value="org.eclipse.ltk.ui.refactoring@default:default"/> <setEntry value="org.eclipse.ltk.ui.refactoring@default:default"/>
<setEntry value="org.eclipse.m2e.archetype.catalog@default:default"/> <setEntry value="org.eclipse.m2e.archetype.catalog@default:false"/>
<setEntry value="org.eclipse.m2e.archetype.common@default:default"/> <setEntry value="org.eclipse.m2e.archetype.common@default:default"/>
<setEntry value="org.eclipse.m2e.archetype.descriptor@default:default"/> <setEntry value="org.eclipse.m2e.archetype.descriptor@default:false"/>
<setEntry value="org.eclipse.m2e.archetype.maven-artifact-transfer@default:default"/> <setEntry value="org.eclipse.m2e.archetype.maven-artifact-transfer@default:false"/>
<setEntry value="org.eclipse.m2e.core.ui@default:default"/> <setEntry value="org.eclipse.m2e.core.ui@default:default"/>
<setEntry value="org.eclipse.m2e.core@default:default"/> <setEntry value="org.eclipse.m2e.core@default:default"/>
<setEntry value="org.eclipse.m2e.logback@default:false"/> <setEntry value="org.eclipse.m2e.logback@default:false"/>
<setEntry value="org.eclipse.m2e.maven.runtime@default:default"/> <setEntry value="org.eclipse.m2e.maven.runtime@default:default"/>
<setEntry value="org.eclipse.m2e.model.edit@default:default"/> <setEntry value="org.eclipse.m2e.model.edit@default:default"/>
<setEntry value="org.eclipse.m2e.workspace.cli@default:default"/> <setEntry value="org.eclipse.m2e.workspace.cli@default:default"/>
<setEntry value="org.eclipse.orbit.xml-apis-ext@default:default"/>
<setEntry value="org.eclipse.osgi.compatibility.state@default:false"/> <setEntry value="org.eclipse.osgi.compatibility.state@default:false"/>
<setEntry value="org.eclipse.osgi.services@default:default"/> <setEntry value="org.eclipse.osgi.services@default:default"/>
<setEntry value="org.eclipse.osgi.util@default:default"/> <setEntry value="org.eclipse.osgi.util@default:default"/>
<setEntry value="org.eclipse.osgi@-1:true"/> <setEntry value="org.eclipse.osgi@-1:true"/>
<setEntry value="org.eclipse.platform@default:default"/> <setEntry value="org.eclipse.platform@default:default"/>
<setEntry value="org.eclipse.rap.tools.launch.rwt@default:default"/> <setEntry value="org.eclipse.rap.tools.launch.rwt@default:default"/>
<setEntry value="org.eclipse.search.core@default:default"/>
<setEntry value="org.eclipse.search@default:default"/> <setEntry value="org.eclipse.search@default:default"/>
<setEntry value="org.eclipse.swt.win32.win32.x86_64@default:false"/> <setEntry value="org.eclipse.swt.cocoa.macosx.aarch64@default:default"/>
<setEntry value="org.eclipse.swt@default:default"/> <setEntry value="org.eclipse.swt@default:default"/>
<setEntry value="org.eclipse.team.core@default:default"/> <setEntry value="org.eclipse.team.core@default:default"/>
<setEntry value="org.eclipse.team.ui@default:default"/> <setEntry value="org.eclipse.team.ui@default:default"/>
<setEntry value="org.eclipse.text@default:default"/> <setEntry value="org.eclipse.text@default:default"/>
<setEntry value="org.eclipse.ui.browser@default:default"/> <setEntry value="org.eclipse.ui.browser@default:default"/>
<setEntry value="org.eclipse.ui.cheatsheets@default:default"/> <setEntry value="org.eclipse.ui.cheatsheets@default:default"/>
<setEntry value="org.eclipse.ui.cocoa@default:default"/>
<setEntry value="org.eclipse.ui.console@default:default"/> <setEntry value="org.eclipse.ui.console@default:default"/>
<setEntry value="org.eclipse.ui.editors@default:default"/> <setEntry value="org.eclipse.ui.editors@default:default"/>
<setEntry value="org.eclipse.ui.forms@default:default"/> <setEntry value="org.eclipse.ui.forms@default:default"/>
@ -361,7 +364,6 @@
<setEntry value="org.eclipse.ui.views.log@default:default"/> <setEntry value="org.eclipse.ui.views.log@default:default"/>
<setEntry value="org.eclipse.ui.views.properties.tabbed@default:default"/> <setEntry value="org.eclipse.ui.views.properties.tabbed@default:default"/>
<setEntry value="org.eclipse.ui.views@default:default"/> <setEntry value="org.eclipse.ui.views@default:default"/>
<setEntry value="org.eclipse.ui.win32@default:false"/>
<setEntry value="org.eclipse.ui.workbench.texteditor@default:default"/> <setEntry value="org.eclipse.ui.workbench.texteditor@default:default"/>
<setEntry value="org.eclipse.ui.workbench@default:default"/> <setEntry value="org.eclipse.ui.workbench@default:default"/>
<setEntry value="org.eclipse.ui@default:default"/> <setEntry value="org.eclipse.ui@default:default"/>
@ -421,16 +423,12 @@
<setEntry value="org.sat4j.core@default:default"/> <setEntry value="org.sat4j.core@default:default"/>
<setEntry value="org.sat4j.pb@default:default"/> <setEntry value="org.sat4j.pb@default:default"/>
<setEntry value="org.tukaani.xz@default:default"/> <setEntry value="org.tukaani.xz@default:default"/>
<setEntry value="org.w3c.css.sac@default:default"/>
<setEntry value="org.w3c.dom.events@default:default"/>
<setEntry value="org.w3c.dom.smil@default:default"/>
<setEntry value="org.w3c.dom.svg@default:default"/>
<setEntry value="slf4j.api@default:default"/> <setEntry value="slf4j.api@default:default"/>
</setAttribute> </setAttribute>
<setAttribute key="selected_workspace_bundles"> <setAttribute key="selected_workspace_bundles">
<setEntry value="ghidra.ghidradev@default:default"/> <setEntry value="ghidra.ghidradev@default:default"/>
</setAttribute> </setAttribute>
<booleanAttribute key="show_selected_only" value="false"/> <booleanAttribute key="show_selected_only" value="true"/>
<booleanAttribute key="tracing" value="false"/> <booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/> <booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfig" value="true"/> <booleanAttribute key="useDefaultConfig" value="true"/>

View file

@ -36,6 +36,7 @@ change with future releases.</p>
<li><a href="#NewGhidraScript">New Ghidra Script</a></li> <li><a href="#NewGhidraScript">New Ghidra Script</a></li>
<li><a href="#NewGhidraScriptProject">New Ghidra Script Project</a></li> <li><a href="#NewGhidraScriptProject">New Ghidra Script Project</a></li>
<li><a href="#NewGhidraModuleProject">New Ghidra Module Project</a></li> <li><a href="#NewGhidraModuleProject">New Ghidra Module Project</a></li>
<li><a href="#ImportGhidraModuleSource">Import Ghidra Module Source</a></li>
<li><a href="#ExportGhidraModuleExtension">Export Ghidra Module Extension</a></li> <li><a href="#ExportGhidraModuleExtension">Export Ghidra Module Extension</a></li>
<li><a href="#Preferences">Preferences</a></li> <li><a href="#Preferences">Preferences</a></li>
<li><a href="#LinkGhidra">Link Ghidra</a></li> <li><a href="#LinkGhidra">Link Ghidra</a></li>
@ -59,6 +60,14 @@ change with future releases.</p>
GhidraDev has been upgraded to be compatible with Ghidra 11.1 and later. Older versions of GhidraDev has been upgraded to be compatible with Ghidra 11.1 and later. Older versions of
GhidraDev will report an error when trying to link against Ghidra 11.1 or later. GhidraDev will report an error when trying to link against Ghidra 11.1 or later.
</li> </li>
<li>
GhidraDev now supports importing a Ghidra module source directory. This will work best
with Ghidra module projects created from Ghidra 11.1 or later.
</li>
<li>
GhidraDev will now fail to launch Ghidra if a top-level <i>build</i> directory is detected.
Presence of this intermediate build artifact can cause Ghidra to have runtime/debugging issues.
</li>
</ul> </ul>
<p><u><b>3.0.2</b>:</u> <p><u><b>3.0.2</b>:</u>
<ul> <ul>
@ -243,6 +252,13 @@ installed into Ghidra as an "extension".</p>
project can be initialized with optional template source files that provide a good starting project can be initialized with optional template source files that provide a good starting
point for implementing advanced Ghidra features such as Analyzers, Plugins, Loaders, etc. point for implementing advanced Ghidra features such as Analyzers, Plugins, Loaders, etc.
</li> </li>
</ul>
<li>Import</li>
<ul>
<li>
<a name="ImportGhidraModuleSource"></a><b>Ghidra Module Source:</b> Opens a wizard that
imports a Ghidra module source directory as a new Ghidra module project.
</li>
</ul> </ul>
<li>Export</li> <li>Export</li>
<ul> <ul>

View file

@ -9,6 +9,7 @@ Require-Bundle: org.eclipse.ant.core;bundle-version="3.6.200",
org.eclipse.buildship.core;bundle-version="3.1.5", org.eclipse.buildship.core;bundle-version="3.1.5",
org.eclipse.core.expressions;bundle-version="3.8.100", org.eclipse.core.expressions;bundle-version="3.8.100",
org.eclipse.core.externaltools;bundle-version="1.2.100", org.eclipse.core.externaltools;bundle-version="1.2.100",
org.eclipse.core.resources;bundle-version="3.16.0",
org.eclipse.core.runtime;bundle-version="3.24.0", org.eclipse.core.runtime;bundle-version="3.24.0",
org.eclipse.debug.ui;bundle-version="3.15.200", org.eclipse.debug.ui;bundle-version="3.15.200",
org.eclipse.jdt.core;bundle-version="3.28.0", org.eclipse.jdt.core;bundle-version="3.28.0",

View file

@ -38,6 +38,23 @@
project="true"> project="true">
</wizard> </wizard>
</extension> </extension>
<extension
point="org.eclipse.ui.importWizards">
<category
id="GhidraCategory"
name="Ghidra">
</category>
<wizard
category="GhidraCategory"
class="ghidradev.ghidraprojectcreator.wizards.ImportGhidraModuleSourceWizard"
icon="icons/brick_add.png"
id="ghidradev.ghidraprojectcreator.wizards.ImportGhidraModuleSourceWizard"
name="Ghidra Module Source">
<description>
Imports a Ghidra module source directory to a new project.
</description>
</wizard>
</extension>
<extension <extension
point="org.eclipse.ui.exportWizards"> point="org.eclipse.ui.exportWizards">
<category <category
@ -50,6 +67,9 @@
icon="icons/brick_go.png" icon="icons/brick_go.png"
id="ghidradev.ghidraprojectcreator.wizards.ExportGhidraModuleWizard" id="ghidradev.ghidraprojectcreator.wizards.ExportGhidraModuleWizard"
name="Ghidra Module Extension"> name="Ghidra Module Extension">
<description>
Exports a Ghidra module project to a zipped Ghidra extension.
</description>
</wizard> </wizard>
</extension> </extension>
<extension <extension
@ -146,6 +166,19 @@
</parameter> </parameter>
</command> </command>
</menu> </menu>
<menu
label="Import">
<command
commandId="org.eclipse.ui.file.import"
icon="icons/brick_add.png"
label="Ghidra Module Source"
style="push">
<parameter
name="importWizardId"
value="ghidradev.ghidraprojectcreator.wizards.ImportGhidraModuleWizard">
</parameter>
</command>
</menu>
<menu <menu
label="Export"> label="Export">
<command <command

View file

@ -73,6 +73,15 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
return; return;
} }
// Make sure there isn't a build/ directory present...it messes up the classpath.
// The build directory could exist if the user built an extension from the command line
// rather than from the Eclipse wizard
if (javaProject.getProject().getFolder("build").exists()) {
EclipseMessageUtils.showErrorDialog("Failed to launch project \"" + projectName +
"\".\nDelete top-level 'build' directory and try again.");
return;
}
// Set program arguments // Set program arguments
String customProgramArgs = String customProgramArgs =
configuration.getAttribute(GhidraLaunchUtils.ATTR_PROGAM_ARGUMENTS, "").trim(); configuration.getAttribute(GhidraLaunchUtils.ATTR_PROGAM_ARGUMENTS, "").trim();

View file

@ -44,6 +44,12 @@ public class GhidraProjectCreatorPreferences {
*/ */
static final String GHIDRA_LAST_PROJECT_ROOT_PATH = "ghidradev.ghidraLastProjectRootPath"; static final String GHIDRA_LAST_PROJECT_ROOT_PATH = "ghidradev.ghidraLastProjectRootPath";
/**
* Path to the last used Ghidra module source directory.
*/
static final String GHIDRA_LAST_MODULE_SOURCE_DIR_PATH =
"ghidradev.ghidraLastModuleSourceDirPath";
/** /**
* The last used Gradle distribution. * The last used Gradle distribution.
*/ */
@ -123,6 +129,28 @@ public class GhidraProjectCreatorPreferences {
prefs.setValue(GHIDRA_LAST_PROJECT_ROOT_PATH, path); prefs.setValue(GHIDRA_LAST_PROJECT_ROOT_PATH, path);
} }
/**
* Gets the last used Ghidra module source directory path that's defined in the preferences.
*
* @return The last used Ghidra module source directory path that's defined in the preferences.
* Could be the empty string.
*/
public static String getGhidraLastModuleSourceDirPath() {
IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
return prefs.getString(GHIDRA_LAST_MODULE_SOURCE_DIR_PATH);
}
/**
* Sets the last used Ghidra module source directory path that's defined in the preferences.
*
* @param path The last used Ghidra module source directory path that's defined in the
* preferences.
*/
public static void setGhidraLastModuleSourceDirPath(String path) {
IPreferenceStore prefs = Activator.getDefault().getPreferenceStore();
prefs.setValue(GHIDRA_LAST_MODULE_SOURCE_DIR_PATH, path);
}
/** /**
* Gets the last used Ghidra Gradle distribution that's defined in the preferences. * Gets the last used Ghidra Gradle distribution that's defined in the preferences.
* *

View file

@ -32,6 +32,7 @@ import ghidra.GhidraLauncher;
/** /**
* Utility methods for working with Ghidra launchers in Eclipse. * Utility methods for working with Ghidra launchers in Eclipse.
*/ */
@SuppressWarnings("restriction")
public class GhidraLaunchUtils { public class GhidraLaunchUtils {
/** /**

View file

@ -94,6 +94,7 @@ public class GhidraModuleUtils {
sourceFolders.add(project.getFolder("src/main/java")); sourceFolders.add(project.getFolder("src/main/java"));
sourceFolders.add(project.getFolder("src/main/help")); sourceFolders.add(project.getFolder("src/main/help"));
sourceFolders.add(project.getFolder("src/main/resources")); sourceFolders.add(project.getFolder("src/main/resources"));
sourceFolders.add(project.getFolder("src/test/java"));
sourceFolders.add(project.getFolder("ghidra_scripts")); sourceFolders.add(project.getFolder("ghidra_scripts"));
for (IFolder sourceFolder : sourceFolders) { for (IFolder sourceFolder : sourceFolders) {
GhidraProjectUtils.createFolder(sourceFolder, monitor); GhidraProjectUtils.createFolder(sourceFolder, monitor);
@ -203,6 +204,76 @@ public class GhidraModuleUtils {
return null; return null;
} }
/**
* Imports a Ghidra module source directory to a new Ghidra module project with the given name.
*
* @param projectName The name of the project to create.
* @param moduleSourceDir The module source directory to import.
* @param createRunConfig Whether or not to create a new run configuration for the project.
* @param runConfigMemory The run configuration's desired memory. Could be null.
* @param ghidraLayout The Ghidra layout to link the project to.
* @param jythonInterpreterName The name of the Jython interpreter to use for Python support.
* Could be null if Python support is not wanted.
* @param monitor The progress monitor to use during project creation.
* @return The imported project.
* @throws IOException If there was a file-related problem with creating the project.
* @throws ParseException If there was a parse-related problem with creating the project.
* @throws CoreException If there was an Eclipse-related problem with creating the project.
*/
public static IJavaProject importGhidraModuleSource(String projectName, File moduleSourceDir,
boolean createRunConfig, String runConfigMemory, GhidraApplicationLayout ghidraLayout,
String jythonInterpreterName, IProgressMonitor monitor)
throws IOException, ParseException, CoreException {
// Create empty Ghidra project
IJavaProject javaProject =
GhidraProjectUtils.createEmptyGhidraProject(projectName, moduleSourceDir,
createRunConfig, runConfigMemory, ghidraLayout, jythonInterpreterName, monitor);
IProject project = javaProject.getProject();
// Find source directory paths
List<IPath> sourcePaths = new ArrayList<>();
IFolder srcFolder = project.getFolder("src");
List<IFolder> srcSubFolders = getSubFolders(srcFolder);
if (!srcSubFolders.isEmpty()) {
for (IFolder srcSubFolder : srcSubFolders) {
List<IFolder> subSubFolders = getSubFolders(srcSubFolder);
if (!subSubFolders.isEmpty()) {
sourcePaths.addAll(subSubFolders.stream().map(e -> e.getFullPath()).toList());
}
else {
sourcePaths.add(srcSubFolder.getFullPath());
}
}
}
else {
sourcePaths.add(srcFolder.getFullPath());
}
// Find jar file paths
List<IPath> jarPaths = new ArrayList<>();
IFolder libFolder = project.getFolder("lib");
if (libFolder.exists()) {
for (IResource resource : libFolder.members()) {
if (resource.getType() == IResource.FILE &&
resource.getFileExtension().equals("jar")) {
jarPaths.add(resource.getFullPath());
}
}
}
// Put the source and jar paths in the project's classpath
List<IClasspathEntry> cp = new ArrayList<>();
cp.addAll(sourcePaths.stream().map(e -> JavaCore.newSourceEntry(e)).toList());
cp.addAll(jarPaths.stream().map(e -> JavaCore.newLibraryEntry(e, null, null)).toList());
GhidraProjectUtils.addToClasspath(javaProject, cp, monitor);
// Update language ant properties file
GhidraModuleUtils.writeAntProperties(project, ghidraLayout);
return javaProject;
}
/** /**
* Writes project-specific ant properties, which get imported by the module project's language * Writes project-specific ant properties, which get imported by the module project's language
* build.xml file to allow building against a Ghidra that lives in an external location. If the * build.xml file to allow building against a Ghidra that lives in an external location. If the
@ -278,4 +349,24 @@ public class GhidraModuleUtils {
Change change = refactoring.createChange(monitor); Change change = refactoring.createChange(monitor);
change.perform(monitor); change.perform(monitor);
} }
/**
* Gets a {@link List} of sub-folders
*
* @param folder The folder to get the sub-folders of
* @return A {@link List} of
* @throws CoreException If there was an Eclipse-related problem getting the sub-folders
*/
private static List<IFolder> getSubFolders(IFolder folder) throws CoreException {
List<IFolder> subFolders = new ArrayList<>();
if (folder.exists()) {
for (IResource resource : folder.members()) {
if (resource.getType() == IResource.FOLDER) {
subFolders.add(folder.getFolder(resource.getName()));
}
}
}
return subFolders;
}
} }

View file

@ -16,6 +16,7 @@
package ghidradev.ghidraprojectcreator.utils; package ghidradev.ghidraprojectcreator.utils;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.ParseException; import java.text.ParseException;
import java.util.*; import java.util.*;
@ -43,6 +44,7 @@ import utility.module.ModuleUtilities;
/** /**
* Utility methods for working with Eclipse Ghidra projects. * Utility methods for working with Eclipse Ghidra projects.
*/ */
@SuppressWarnings("restriction")
public class GhidraProjectUtils { public class GhidraProjectUtils {
/** /**
@ -290,6 +292,9 @@ public class GhidraProjectUtils {
IJavaProject javaProject = JavaCore.create(project); IJavaProject javaProject = JavaCore.create(project);
project.open(monitor); project.open(monitor);
// Set the project's default encoding
project.setDefaultCharset(StandardCharsets.UTF_8.displayName(), monitor);
// Clear the project's classpath // Clear the project's classpath
javaProject.setRawClasspath(new IClasspathEntry[0], monitor); javaProject.setRawClasspath(new IClasspathEntry[0], monitor);

View file

@ -61,7 +61,7 @@ public class CreateGhidraModuleProjectWizard extends Wizard implements INewWizar
@Override @Override
public void init(IWorkbench wb, IStructuredSelection selection) { public void init(IWorkbench wb, IStructuredSelection selection) {
workbench = wb; workbench = wb;
projectPage = new CreateGhidraProjectWizardPage(); projectPage = new CreateGhidraProjectWizardPage(true);
projectConfigPage = new ConfigureGhidraModuleProjectWizardPage(); projectConfigPage = new ConfigureGhidraModuleProjectWizardPage();
ghidraInstallationPage = new ChooseGhidraInstallationWizardPage(); ghidraInstallationPage = new ChooseGhidraInstallationWizardPage();
pythonPage = new EnablePythonWizardPage(ghidraInstallationPage); pythonPage = new EnablePythonWizardPage(ghidraInstallationPage);

View file

@ -54,7 +54,7 @@ public class CreateGhidraScriptProjectWizard extends Wizard implements INewWizar
@Override @Override
public void init(IWorkbench wb, IStructuredSelection selection) { public void init(IWorkbench wb, IStructuredSelection selection) {
projectPage = new CreateGhidraProjectWizardPage("GhidraScripts"); projectPage = new CreateGhidraProjectWizardPage("GhidraScripts", true);
projectConfigPage = new ConfigureGhidraScriptProjectWizardPage(); projectConfigPage = new ConfigureGhidraScriptProjectWizardPage();
ghidraInstallationPage = new ChooseGhidraInstallationWizardPage(); ghidraInstallationPage = new ChooseGhidraInstallationWizardPage();
pythonPage = new EnablePythonWizardPage(ghidraInstallationPage); pythonPage = new EnablePythonWizardPage(ghidraInstallationPage);

View file

@ -41,6 +41,7 @@ import org.eclipse.ui.IWorkbench;
import ghidra.GhidraApplicationLayout; import ghidra.GhidraApplicationLayout;
import ghidra.launch.JavaConfig; import ghidra.launch.JavaConfig;
import ghidradev.EclipseMessageUtils;
import ghidradev.ghidraprojectcreator.utils.GhidraProjectUtils; import ghidradev.ghidraprojectcreator.utils.GhidraProjectUtils;
import ghidradev.ghidraprojectcreator.wizards.pages.ChooseGhidraModuleProjectWizardPage; import ghidradev.ghidraprojectcreator.wizards.pages.ChooseGhidraModuleProjectWizardPage;
import ghidradev.ghidraprojectcreator.wizards.pages.ConfigureGradleWizardPage; import ghidradev.ghidraprojectcreator.wizards.pages.ConfigureGradleWizardPage;
@ -77,6 +78,10 @@ public class ExportGhidraModuleWizard extends Wizard implements INewWizard {
@Override @Override
public boolean performFinish() { public boolean performFinish() {
if (!validate()) {
return false;
}
IJavaProject javaProject = projectPage.getGhidraModuleProject(); IJavaProject javaProject = projectPage.getGhidraModuleProject();
GradleDistribution gradleDist = gradlePage.getGradleDistribution(); GradleDistribution gradleDist = gradlePage.getGradleDistribution();
try { try {
@ -169,4 +174,26 @@ public class ExportGhidraModuleWizard extends Wizard implements INewWizard {
monitor.done(); monitor.done();
} }
} }
/**
* Validates the wizard pages. If they are invalid, an error popup will be displayed which
* will indicate the problem.
*
* @return True if the data returned from the wizard pages are valid; otherwise, false
*/
private boolean validate() {
String title = "Invalid Ghidra Module Extension";
IJavaProject javaProject = projectPage.getGhidraModuleProject();
if (!javaProject.getProject().getFile("extension.properties").exists()) {
EclipseMessageUtils.showErrorDialog(title,
"Cannot export extension because 'extension.properties' file does not exist.");
return false;
}
if (!javaProject.getProject().getFile("Module.manifest").exists()) {
EclipseMessageUtils.showErrorDialog(title,
"Cannot export extension because 'Module.manifest' file does not exist.");
return false;
}
return true;
}
} }

View file

@ -0,0 +1,150 @@
/* ###
* 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 ghidradev.ghidraprojectcreator.wizards;
import static ghidradev.EclipseMessageUtils.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.IImportWizard;
import org.eclipse.ui.IWorkbench;
import ghidra.GhidraApplicationLayout;
import ghidradev.EclipseMessageUtils;
import ghidradev.ghidraprojectcreator.utils.GhidraModuleUtils;
import ghidradev.ghidraprojectcreator.wizards.pages.*;
import utilities.util.FileUtilities;
/**
* Wizard for importing Ghidra module source to a new Ghidra module project.
*/
public class ImportGhidraModuleSourceWizard extends Wizard implements IImportWizard {
private ChooseGhidraModuleSourceWizardPage sourcePage;
private CreateGhidraProjectWizardPage projectPage;
private ChooseGhidraInstallationWizardPage ghidraInstallationPage;
private EnablePythonWizardPage pythonPage;
public ImportGhidraModuleSourceWizard() {
super();
}
@Override
public void init(IWorkbench wb, IStructuredSelection selection) {
sourcePage = new ChooseGhidraModuleSourceWizardPage();
projectPage = new CreateGhidraProjectWizardPage(false);
ghidraInstallationPage = new ChooseGhidraInstallationWizardPage();
pythonPage = new EnablePythonWizardPage(ghidraInstallationPage);
}
@Override
public void addPages() {
addPage(sourcePage);
addPage(projectPage);
addPage(ghidraInstallationPage);
addPage(pythonPage);
}
@Override
public boolean performFinish() {
if (!validate()) {
return false;
}
File moduleSourceDir = sourcePage.getSourceDir();
File ghidraInstallDir = ghidraInstallationPage.getGhidraInstallDir();
String projectName = projectPage.getProjectName();
boolean createRunConfig = projectPage.shouldCreateRunConfig();
String runConfigMemory = projectPage.getRunConfigMemory();
String jythonInterpreterName = pythonPage.getJythonInterpreterName();
try {
getContainer().run(true, false,
monitor -> importModuleSource(ghidraInstallDir, projectName, moduleSourceDir,
createRunConfig, runConfigMemory, jythonInterpreterName, monitor));
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
catch (InvocationTargetException e) {
error(showWizardErrorDialog(getShell(), e), e);
return false;
}
return true;
}
/**
* Imports a Ghidra module source directory to a new Ghidra module project.
*
* @param ghidraInstallDir The Ghidra installation directory to use.
* @param projectName The name of the project to create.
* @param moduleSourceDir The module source directory to import.
* @param createRunConfig Whether or not to create a new run configuration for the project.
* @param runConfigMemory The run configuration's desired memory. Could be null.
* @param jythonInterpreterName The name of the Jython interpreter to use for Python support.
* Could be null if Python support is not wanted.
* @param monitor The monitor to use during project creation.
* @throws InvocationTargetException if an error occurred during project creation.
*/
private void importModuleSource(File ghidraInstallDir, String projectName, File moduleSourceDir,
boolean createRunConfig, String runConfigMemory, String jythonInterpreterName,
IProgressMonitor monitor) throws InvocationTargetException {
try {
info("Importing " + projectName + " at " + moduleSourceDir);
monitor.beginTask("Importing " + projectName, 2);
GhidraApplicationLayout ghidraLayout = new GhidraApplicationLayout(ghidraInstallDir);
monitor.worked(1);
GhidraModuleUtils.importGhidraModuleSource(projectName, moduleSourceDir,
createRunConfig, runConfigMemory, ghidraLayout, jythonInterpreterName, monitor);
monitor.worked(1);
info("Finished importing " + projectName);
}
catch (IOException | ParseException | CoreException e) {
throw new InvocationTargetException(e);
}
finally {
monitor.done();
}
}
/**
* Validates the wizard pages. If they are invalid, an error popup will be displayed which
* will indicate the problem.
*
* @return True if the data returned from the wizard pages are valid; otherwise, false
*/
private boolean validate() {
if (FileUtilities.isPathContainedWithin(ghidraInstallationPage.getGhidraInstallDir(),
sourcePage.getSourceDir())) {
EclipseMessageUtils.showErrorDialog("Invalid Module Source Directory",
"Module source directory cannot reside inside of the selected Ghidra installation directory.");
return false;
}
return true;
}
}

View file

@ -0,0 +1,124 @@
/* ###
* 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 ghidradev.ghidraprojectcreator.wizards.pages;
import java.io.File;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import ghidradev.ghidraprojectcreator.preferences.GhidraProjectCreatorPreferences;
/**
* A wizard page that lets the user choose a Ghidra module source directory.
*/
public class ChooseGhidraModuleSourceWizardPage extends WizardPage {
private Text sourceDirText;
private Button sourceDirButton;
/**
* Creates a new Ghidra module source chooser wizard page.
*/
public ChooseGhidraModuleSourceWizardPage() {
super("ChooseGhidraModuleSourceWizardPage");
setTitle("Choose Ghidra Module Source");
setDescription("Choose a Ghidra module source directory.");
}
@Override
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
container.setLayout(new GridLayout(3, false));
// Source directory
Label sourceDirLabel = new Label(container, SWT.NULL);
String sourceDirToolTip = "The Ghidra module source directory.";
sourceDirLabel.setText("Source directory:");
sourceDirLabel.setToolTipText(sourceDirToolTip);
sourceDirText = new Text(container, SWT.BORDER | SWT.SINGLE);
sourceDirText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
sourceDirText.setText(GhidraProjectCreatorPreferences.getGhidraLastModuleSourceDirPath());
sourceDirText.addModifyListener(evt -> validate());
sourceDirText.setToolTipText(sourceDirToolTip);
sourceDirButton = new Button(container, SWT.BUTTON1);
sourceDirButton.setText("...");
sourceDirButton.setToolTipText("Browse to select source directory");
sourceDirButton.addListener(SWT.Selection, evt -> {
DirectoryDialog dialog = new DirectoryDialog(container.getShell());
String path = dialog.open();
if (path != null) {
sourceDirText.setText(path);
}
});
validate();
setControl(container);
}
/**
* Gets the module source directory.
*
* @return The module source directory. Could be null if unspecified, however, the page will not
* be valid until the module source directory is valid, so it should never be null when called
* by other classes.
*/
public File getSourceDir() {
if (sourceDirText.getText().isEmpty()) {
return null;
}
return new File(sourceDirText.getText());
}
/**
* Validates the fields on the page and updates the page's status.
* Should be called every time a field on the page changes.
*/
private void validate() {
String message = null;
File sourceDir = new File(sourceDirText.getText());
if (!sourceDir.isAbsolute()) {
message = "Source directory must be an absolute path";
}
else if (!sourceDir.isDirectory()) {
message = "Source directory does not exist";
}
else if (!new File(sourceDir, "Module.manifest").exists()) {
message = "Source directory does not contain a Module.manifest file";
}
else if (!new File(sourceDir, "build.gradle").exists()) {
message = "Source directory does not contain a build.gradle file";
}
else if (new File(sourceDir, ".project").exists()) {
message = "Source directory already contains a .project file";
}
else if (new File(sourceDir, ".classpath").exists()) {
message = "Source directory already contains a .classpath file";
}
setErrorMessage(message);
setPageComplete(message == null);
if (message == null) {
GhidraProjectCreatorPreferences
.setGhidraLastModuleSourceDirPath(sourceDirText.getText());
}
}
}

View file

@ -39,6 +39,7 @@ import ghidradev.ghidraprojectcreator.utils.GhidraProjectUtils;
public class CreateGhidraProjectWizardPage extends WizardPage { public class CreateGhidraProjectWizardPage extends WizardPage {
private String suggestedProjectName; private String suggestedProjectName;
private boolean showProjectDir;
private Text projectNameText; private Text projectNameText;
private Text projectRootDirText; private Text projectRootDirText;
@ -50,19 +51,25 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
* Creates a Ghidra new project wizard page with the given suggested project name. * Creates a Ghidra new project wizard page with the given suggested project name.
* *
* @param suggestedProjectName The suggested project name. * @param suggestedProjectName The suggested project name.
* @param showProjectDir True to show a component for selecting the root project directory;
* otherwise, false
*/ */
public CreateGhidraProjectWizardPage(String suggestedProjectName) { public CreateGhidraProjectWizardPage(String suggestedProjectName, boolean showProjectDir) {
super("CreateGhidraProjectWizardPage"); super("CreateGhidraProjectWizardPage");
setTitle("Create Ghidra Project"); setTitle("Create Ghidra Project");
setDescription("Create a new Ghidra project."); setDescription("Create a new Ghidra project.");
this.suggestedProjectName = suggestedProjectName; this.suggestedProjectName = suggestedProjectName;
this.showProjectDir = showProjectDir;
} }
/** /**
* Creates a Ghidra new project wizard page. * Creates a Ghidra new project wizard page.
*
* @param showProjectDir True to show a component for selecting the root project directory;
* otherwise, false
*/ */
public CreateGhidraProjectWizardPage() { public CreateGhidraProjectWizardPage(boolean showProjectDir) {
this(""); this("", showProjectDir);
} }
@Override @Override
@ -81,13 +88,15 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
new Label(container, SWT.NONE).setText(""); // empty grid cell new Label(container, SWT.NONE).setText(""); // empty grid cell
// Project directory // Project directory
if (showProjectDir) {
Label projectDirLabel = new Label(container, SWT.NULL); Label projectDirLabel = new Label(container, SWT.NULL);
String projectDirToolTip = "The directory where this project will be created."; String projectDirToolTip = "The directory where this project will be created.";
projectDirLabel.setText("Project root directory:"); projectDirLabel.setText("Project root directory:");
projectDirLabel.setToolTipText(projectDirToolTip); projectDirLabel.setToolTipText(projectDirToolTip);
projectRootDirText = new Text(container, SWT.BORDER | SWT.SINGLE); projectRootDirText = new Text(container, SWT.BORDER | SWT.SINGLE);
projectRootDirText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); projectRootDirText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
projectRootDirText.setText(GhidraProjectCreatorPreferences.getGhidraLastProjectRootPath()); projectRootDirText
.setText(GhidraProjectCreatorPreferences.getGhidraLastProjectRootPath());
projectRootDirText.addModifyListener(evt -> validate()); projectRootDirText.addModifyListener(evt -> validate());
projectRootDirText.setToolTipText(projectDirToolTip); projectRootDirText.setToolTipText(projectDirToolTip);
projectDirButton = new Button(container, SWT.BUTTON1); projectDirButton = new Button(container, SWT.BUTTON1);
@ -100,6 +109,7 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
projectRootDirText.setText(path); projectRootDirText.setText(path);
} }
}); });
}
// Create run configuration checkbox // Create run configuration checkbox
createRunConfigCheckboxButton = new Button(container, SWT.CHECK); createRunConfigCheckboxButton = new Button(container, SWT.CHECK);
@ -153,14 +163,13 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
* Gets the project directory. This is the directory where the .project file should live. * Gets the project directory. This is the directory where the .project file should live.
* *
* @return The project directory. This is the directory where the .project file should live. * @return The project directory. This is the directory where the .project file should live.
* Could be null if unspecified, however, the page will not be valid until the project * Could be null if unspecified.
* directory is valid, so it should never be null when called by other classes.
*/ */
public File getProjectDir() { public File getProjectDir() {
if (projectNameText.getText().isEmpty()) { if (projectNameText.getText().isEmpty()) {
return null; return null;
} }
if (projectRootDirText.getText().isEmpty()) { if (projectRootDirText == null || projectRootDirText.getText().isEmpty()) {
return null; return null;
} }
return new File(projectRootDirText.getText(), getProjectName()); return new File(projectRootDirText.getText(), getProjectName());
@ -227,10 +236,10 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
else if (BAD.chars().anyMatch(ch -> projectName.indexOf(ch) != -1)) { else if (BAD.chars().anyMatch(ch -> projectName.indexOf(ch) != -1)) {
message = "Project name cannot contain invalid characters:\n " + BAD; message = "Project name cannot contain invalid characters:\n " + BAD;
} }
else if (projectDir == null) { else if (showProjectDir && projectDir == null) {
message = "Project root directory must be specified"; message = "Project root directory must be specified";
} }
else if (projectDir.exists()) { else if (showProjectDir && projectDir.exists()) {
message = "Project already exists at: " + projectDir.getAbsolutePath(); message = "Project already exists at: " + projectDir.getAbsolutePath();
} }
else if (ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).exists()) { else if (ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).exists()) {
@ -250,8 +259,10 @@ public class CreateGhidraProjectWizardPage extends WizardPage {
setErrorMessage(message); setErrorMessage(message);
setPageComplete(message == null); setPageComplete(message == null);
if (message == null) { if (message == null) {
GhidraProjectCreatorPreferences.setGhidraLastProjectRootPath( if (projectRootDirText != null) {
projectRootDirText.getText()); GhidraProjectCreatorPreferences
.setGhidraLastProjectRootPath(projectRootDirText.getText());
}
} }
} }
} }

View file

@ -34,6 +34,7 @@ import org.eclipse.ui.ide.IDE;
import ghidradev.EclipseMessageUtils; import ghidradev.EclipseMessageUtils;
@SuppressWarnings("restriction")
public class OpenDeclarations { public class OpenDeclarations {
private IProject project; private IProject project;

View file

@ -37,6 +37,7 @@ rootProject.assembleDistribution {
exclude '.project' exclude '.project'
exclude 'build.gradle' exclude 'build.gradle'
rename "buildTemplate.gradle", "build.gradle" rename "buildTemplate.gradle", "build.gradle"
rename "gitignore", ".gitignore"
into "Extensions/Ghidra/Skeleton" into "Extensions/Ghidra/Skeleton"
} }
} }

View file

@ -11,6 +11,7 @@ data/languages/skel.slaspec||GHIDRA||||END|
data/sleighArgs.txt||GHIDRA||||END| data/sleighArgs.txt||GHIDRA||||END|
extension.properties||GHIDRA||||END| extension.properties||GHIDRA||||END|
ghidra_scripts/README.txt||GHIDRA||||END| ghidra_scripts/README.txt||GHIDRA||||END|
gitignore||GHIDRA||||END|
lib/README.txt||GHIDRA||||END| lib/README.txt||GHIDRA||||END|
os/linux_x86_64/README.txt||GHIDRA||||END| os/linux_x86_64/README.txt||GHIDRA||||END|
os/mac_x86_64/README.txt||GHIDRA||||END| os/mac_x86_64/README.txt||GHIDRA||||END|

View file

@ -0,0 +1,10 @@
.project
.pyproject
.classpath
.settings/
.gradle/
bin/
build/
dist/
*.swp
.antProperties.xml

View file

@ -41,7 +41,7 @@ public class SkeletonAnalyzer extends AbstractAnalyzer {
// TODO: Return true if analyzer should be enabled by default // TODO: Return true if analyzer should be enabled by default
return false; return true;
} }
@Override @Override
@ -50,7 +50,7 @@ public class SkeletonAnalyzer extends AbstractAnalyzer {
// TODO: Examine 'program' to determine of this analyzer should analyze it. Return true // TODO: Examine 'program' to determine of this analyzer should analyze it. Return true
// if it can. // if it can.
return false; return true;
} }
@Override @Override