mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 08:05:41 +08:00
GP-3064 added feature and options to navigate programs after the initial analysis is complete
This commit is contained in:
@@ -805,7 +805,7 @@
|
||||
lowest code block, and finally lowest overall address</LI>
|
||||
</UL>
|
||||
|
||||
<P><B>Start Symbols</B> - A comma separated list of symbol names to be be used as the
|
||||
<P><B><A name="Start_Symbols"></A>Start Symbols</B> - A comma separated list of symbol names to be be used as the
|
||||
starting location for the program if the "Preferred Symbol Name" option is selected
|
||||
above. The first matching symbol found will be used as the starting location for
|
||||
newly opened programs.</P>
|
||||
@@ -816,6 +816,24 @@
|
||||
when trying to find a starting symbol.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
<H3><A name="After_Initial_Analysis"></A>Initial Analysis Navigation Options</H3>
|
||||
<P>These options control the behavior of the tool after the initial analysis has
|
||||
completed.</P>
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Ask To Reposition Program</B> - If selected, the user will be prompted if they
|
||||
would like the program to be positioned to any newly discovered starting symbols as
|
||||
specified in the <A href="#Start_Symbols">Start Symbols</A> option.</P>
|
||||
|
||||
<P><B>Auto Reposition If Not Moved</B> - If selected, the program will automatically
|
||||
be reposition to any newly discovered starting symbols as specified in the
|
||||
<A href="#Start_Symbols">Start Symbols</A> option, provided the user has
|
||||
not manually moved the cursor off the starting location address. If they have manually
|
||||
moved the cursor, then the behavior will revert to the setting of the "Ask To
|
||||
Reposition Program" option above.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.events;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ghidra.framework.plugintool.PluginEvent;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Plugin event class for notification of when programs have completed being analyzed for the first
|
||||
* time.
|
||||
*/
|
||||
public class FirstTimeAnalyzedPluginEvent extends PluginEvent {
|
||||
public static final String EVENT_NAME = "FirstTimeAnalyzed";
|
||||
|
||||
private WeakReference<Program> programRef;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param sourceName source name of the plugin that created this event
|
||||
* @param program the program that has been analyzed for the first time
|
||||
*/
|
||||
public FirstTimeAnalyzedPluginEvent(String sourceName, Program program) {
|
||||
super(sourceName, EVENT_NAME);
|
||||
this.programRef = new WeakReference<Program>(program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Program} that has just been analyzed for the first time. This method
|
||||
* can return null, but only if the program has been closed and is no longer in use which
|
||||
* can't happen if the method is called during the original event notification.
|
||||
* @return the {@link Program} that has just been analyzed for the first time.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return programRef.get();
|
||||
}
|
||||
}
|
||||
+22
-26
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.events;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ghidra.framework.plugintool.PluginEvent;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Plugin event class for notification of programs being created, opened, or
|
||||
* closed.
|
||||
@@ -29,30 +28,27 @@ import java.lang.ref.WeakReference;
|
||||
public class ProgramActivatedPluginEvent extends PluginEvent {
|
||||
|
||||
static final String NAME = "Program Activated";
|
||||
// static final String TOOL_EVENT_NAME = "Program Activated";
|
||||
//
|
||||
// static {
|
||||
// registerPluginEventMapping(OpenProgramPluginEvent.class, TOOL_EVENT_NAME);
|
||||
// }
|
||||
|
||||
private WeakReference<Program> newProgramRef;
|
||||
|
||||
/**
|
||||
* Construct a new plugin event.
|
||||
* @param source name of the plugin that created this event
|
||||
* @param activeProgram the program associated with this event
|
||||
*/
|
||||
public ProgramActivatedPluginEvent(String source, Program activeProgram) {
|
||||
super(source, NAME);
|
||||
this.newProgramRef = new WeakReference<Program>(activeProgram);
|
||||
}
|
||||
private WeakReference<Program> newProgramRef;
|
||||
|
||||
/**
|
||||
* Return the new activated program. May be null.
|
||||
* @return null if the event if for a program closing.
|
||||
*/
|
||||
public Program getActiveProgram () {
|
||||
return newProgramRef.get();
|
||||
}
|
||||
/**
|
||||
* Construct a new plugin event.
|
||||
* @param source name of the plugin that created this event
|
||||
* @param activeProgram the program associated with this event
|
||||
*/
|
||||
public ProgramActivatedPluginEvent(String source, Program activeProgram) {
|
||||
super(source, NAME);
|
||||
this.newProgramRef = new WeakReference<Program>(activeProgram);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Program} that has is being activated. This method
|
||||
* can return null, but it is unlikely. It will only return null if the program has been closed
|
||||
* and is no longer in use.
|
||||
* @return the {@link Program} that has just been analyzed for the first time.
|
||||
*/
|
||||
public Program getActiveProgram() {
|
||||
return newProgramRef.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,8 +40,10 @@ public class ProgramClosedPluginEvent extends PluginEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the program on this event.
|
||||
* @return null if the event if for a program closing.
|
||||
* Returns the {@link Program} that has just been opened. This method
|
||||
* can return null, but only if the method is called some time after the original event
|
||||
* notification.
|
||||
* @return the {@link Program} that has just been analyzed for the first time.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return programRef.get();
|
||||
|
||||
+19
-20
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,11 +24,11 @@ import ghidra.program.util.ProgramSelection;
|
||||
/**
|
||||
* Plugin event generated when the highlight in a program changes.
|
||||
*/
|
||||
public final class ProgramHighlightPluginEvent extends PluginEvent {
|
||||
public static final String NAME = "ProgramHighlight";
|
||||
public final class ProgramHighlightPluginEvent extends PluginEvent {
|
||||
public static final String NAME = "ProgramHighlight";
|
||||
|
||||
private ProgramSelection highlight;
|
||||
private WeakReference<Program> programRef;
|
||||
private ProgramSelection highlight;
|
||||
private WeakReference<Program> programRef;
|
||||
|
||||
/**
|
||||
* Construct a new event.
|
||||
@@ -37,25 +36,25 @@ public final class ProgramHighlightPluginEvent extends PluginEvent {
|
||||
* @param hl Program selection containing the selected address set.
|
||||
* @param program program being highlighted
|
||||
*/
|
||||
public ProgramHighlightPluginEvent(String src,ProgramSelection hl,
|
||||
Program program) {
|
||||
super(src, NAME);
|
||||
this.highlight = hl;
|
||||
this.programRef = new WeakReference<Program>(program);
|
||||
}
|
||||
public ProgramHighlightPluginEvent(String src, ProgramSelection hl, Program program) {
|
||||
super(src, NAME);
|
||||
this.highlight = hl;
|
||||
this.programRef = new WeakReference<Program>(program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the program selection contained in this event.
|
||||
* @return ProgramSelection contained in this event.
|
||||
*/
|
||||
public ProgramSelection getHighlight() {
|
||||
return highlight;
|
||||
}
|
||||
public ProgramSelection getHighlight() {
|
||||
return highlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Program object that the highlight refers to.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return programRef.get();
|
||||
}
|
||||
/**
|
||||
* Returns the Program object that the highlight refers to.
|
||||
* @return the Program object that the highlight refers to.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return programRef.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,10 @@ public class ProgramOpenedPluginEvent extends PluginEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the program on this event.
|
||||
* @return null if the event if for a program closing.
|
||||
* Returns the {@link Program} that has just been opened. This method
|
||||
* can return null, but only if the program has been closed and is no longer in use which
|
||||
* can't happen if the method is called during the original event notification.
|
||||
* @return the {@link Program} that has just been analyzed for the first time.
|
||||
*/
|
||||
public Program getProgram() {
|
||||
return programRef.get();
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.events;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ghidra.framework.plugintool.PluginEvent;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
/**
|
||||
* Plugin event class for notification that plugin first pass processing of a newly activated
|
||||
* program is complete. More specifically, all plugins have received and had a chance
|
||||
* to react to a {@link ProgramActivatedPluginEvent}.
|
||||
*/
|
||||
public class ProgramPostActivatedPluginEvent extends PluginEvent {
|
||||
|
||||
static final String NAME = "Post Program Activated";
|
||||
private WeakReference<Program> newProgramRef;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param source name of the plugin that created this event
|
||||
* @param activeProgram the program that has been activated
|
||||
*/
|
||||
public ProgramPostActivatedPluginEvent(String source, Program activeProgram) {
|
||||
super(source, NAME);
|
||||
this.newProgramRef = new WeakReference<Program>(activeProgram);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new activated program. May be null.
|
||||
* @return null if the event if for a program closing.
|
||||
*/
|
||||
public Program getActiveProgram() {
|
||||
return newProgramRef.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,6 +55,7 @@ public abstract class ProgramPlugin extends Plugin {
|
||||
public ProgramPlugin(PluginTool plugintool) {
|
||||
super(plugintool);
|
||||
internalRegisterEventConsumed(ProgramActivatedPluginEvent.class);
|
||||
internalRegisterEventConsumed(ProgramPostActivatedPluginEvent.class);
|
||||
internalRegisterEventConsumed(ProgramLocationPluginEvent.class);
|
||||
internalRegisterEventConsumed(ProgramSelectionPluginEvent.class);
|
||||
internalRegisterEventConsumed(ProgramHighlightPluginEvent.class);
|
||||
@@ -160,12 +161,16 @@ public abstract class ProgramPlugin extends Plugin {
|
||||
}
|
||||
highlightChanged(currentHighlight);
|
||||
}
|
||||
else if (event instanceof ProgramPostActivatedPluginEvent ev) {
|
||||
postProgramActivated(ev.getActiveProgram());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass should override this method if it is interested when programs become active.
|
||||
* Note: this method is called in response to a ProgramActivatedPluginEvent.
|
||||
*
|
||||
* <P>
|
||||
* At the time this method is called,
|
||||
* the "currentProgram" variable will be set the new active program.
|
||||
*
|
||||
@@ -175,6 +180,20 @@ public abstract class ProgramPlugin extends Plugin {
|
||||
// override
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass should override this method if it is interested when programs become active and
|
||||
* all plugins have had a chance to process the {@link ProgramActivatedPluginEvent}.
|
||||
* Note: this method is called in response to a {@link ProgramPostActivatedPluginEvent}
|
||||
* <P>
|
||||
* At the time this method is called,
|
||||
* the "currentProgram" variable will be set the new active program.
|
||||
*
|
||||
* @param program the new program going active.
|
||||
*/
|
||||
protected void postProgramActivated(Program program) {
|
||||
// override
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should override this method if it is interested when a program is closed.
|
||||
*
|
||||
|
||||
+7
-5
@@ -18,6 +18,7 @@ package ghidra.app.plugin.core.analysis;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
@@ -132,7 +133,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
||||
|
||||
private MessageLog log = new MessageLog();
|
||||
|
||||
private List<AutoAnalysisManagerListener> listeners = new ArrayList<>();
|
||||
private List<AutoAnalysisManagerListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
private EventQueueID eventQueueID;
|
||||
|
||||
@@ -850,6 +851,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
||||
for (AnalysisTaskList list : taskArray) {
|
||||
list.notifyAnalysisEnded(program);
|
||||
}
|
||||
|
||||
for (AutoAnalysisManagerListener listener : listeners) {
|
||||
listener.analysisEnded(this);
|
||||
}
|
||||
@@ -1268,13 +1270,13 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
||||
if (testLen > spacer.length()) {
|
||||
testLen = spacer.length() - 5;
|
||||
}
|
||||
taskTimesStringBuf.append(
|
||||
" " + element + spacer.substring(testLen) + secString + "\n");
|
||||
taskTimesStringBuf
|
||||
.append(" " + element + spacer.substring(testLen) + secString + "\n");
|
||||
}
|
||||
|
||||
taskTimesStringBuf.append("-----------------------------------------------------\n");
|
||||
taskTimesStringBuf.append(
|
||||
" Total Time " + (int) (totalTaskTime / 1000.00) + " secs\n");
|
||||
taskTimesStringBuf
|
||||
.append(" Total Time " + (int) (totalTaskTime / 1000.00) + " secs\n");
|
||||
taskTimesStringBuf.append("-----------------------------------------------------\n");
|
||||
|
||||
return taskTimesStringBuf.toString();
|
||||
|
||||
+37
-35
@@ -17,8 +17,6 @@ package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.action.DockingAction;
|
||||
@@ -59,7 +57,7 @@ import ghidra.util.task.TaskLauncher;
|
||||
category = PluginCategoryNames.ANALYSIS,
|
||||
shortDescription = "Manages auto-analysis",
|
||||
description = "Provides coordination and a service for All Auto Analysis tasks.",
|
||||
eventsConsumed = { ProgramOpenedPluginEvent.class, ProgramClosedPluginEvent.class, ProgramActivatedPluginEvent.class }
|
||||
eventsConsumed = { ProgramOpenedPluginEvent.class, ProgramClosedPluginEvent.class, ProgramActivatedPluginEvent.class, ProgramPostActivatedPluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerListener {
|
||||
@@ -180,16 +178,20 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
||||
private void analyzeCallback(Program program, ProgramSelection selection) {
|
||||
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||
|
||||
analysisMgr.initializeOptions(); // get initial options
|
||||
analysisMgr.initializeOptions(); // this allows analyzers to register options with defaults
|
||||
|
||||
if (!showOptionsDialog(program)) {
|
||||
return;
|
||||
}
|
||||
|
||||
analysisMgr.initializeOptions(); // options may have changed
|
||||
analysisMgr.initializeOptions(); // reloads the options in case the user changed them
|
||||
|
||||
// At this point, any analysis that is done is consider to be true for analyzed.
|
||||
GhidraProgramUtilities.setAnalyzedFlag(program, true);
|
||||
// check if this is the first time this program is being analyzed. If so,
|
||||
// schedule a callback when it is completed to send a FirstTimeAnalyzedPluginEvent
|
||||
boolean isAnalyzed = GhidraProgramUtilities.isAnalyzedFlagSet(program);
|
||||
if (!isAnalyzed) {
|
||||
analysisMgr.addListener(new FirstTimeAnalyzedCallback());
|
||||
}
|
||||
|
||||
// start analysis to set the flag, but it probably won't do more. A bit goofy but better
|
||||
// than the way it was
|
||||
@@ -224,16 +226,13 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
if (event instanceof ProgramClosedPluginEvent) {
|
||||
ProgramClosedPluginEvent ev = (ProgramClosedPluginEvent) event;
|
||||
if (event instanceof ProgramClosedPluginEvent ev) {
|
||||
programClosed(ev.getProgram());
|
||||
}
|
||||
else if (event instanceof ProgramOpenedPluginEvent) {
|
||||
ProgramOpenedPluginEvent ev = (ProgramOpenedPluginEvent) event;
|
||||
else if (event instanceof ProgramOpenedPluginEvent ev) {
|
||||
programOpened(ev.getProgram());
|
||||
}
|
||||
else if (event instanceof ProgramActivatedPluginEvent) {
|
||||
ProgramActivatedPluginEvent ev = (ProgramActivatedPluginEvent) event;
|
||||
else if (event instanceof ProgramActivatedPluginEvent ev) {
|
||||
Program program = ev.getActiveProgram();
|
||||
if (program == null) {
|
||||
removeOneShotActions();
|
||||
@@ -243,6 +242,12 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
||||
addOneShotActions(program);
|
||||
}
|
||||
}
|
||||
else if (event instanceof ProgramPostActivatedPluginEvent ev) {
|
||||
Program program = ev.getActiveProgram();
|
||||
if (program != null) {
|
||||
postProgramActivated(program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void programOpened(final Program program) {
|
||||
@@ -256,30 +261,18 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
||||
new HelpLocation("AutoAnalysisPlugin", "Auto_Analysis_Option"));
|
||||
}
|
||||
|
||||
private void programActivated(final Program program) {
|
||||
|
||||
private void programActivated(Program program) {
|
||||
program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST)
|
||||
.registerOption(
|
||||
StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
|
||||
"Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
|
||||
.registerOption(StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
|
||||
"Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
|
||||
|
||||
// invokeLater() to ensure that all other plugins have been notified of the program
|
||||
// activated. This makes sure plugins like the Listing have opened and painted the
|
||||
// program.
|
||||
//
|
||||
// If the user decided to instantly close the code browser before we get to run anything,
|
||||
// an exception could be thrown! Therefore, we must check to see if the program is closed
|
||||
// at this point before we run anything.
|
||||
//
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (program.isClosed()) {
|
||||
return;
|
||||
}
|
||||
final AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||
if (analysisMgr.askToAnalyze(tool)) {
|
||||
analyzeCallback(program, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void postProgramActivated(Program program) {
|
||||
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
|
||||
if (analysisMgr.askToAnalyze(tool)) {
|
||||
analyzeCallback(program, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,4 +366,13 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
||||
return canAnalyze;
|
||||
}
|
||||
}
|
||||
|
||||
private class FirstTimeAnalyzedCallback implements AutoAnalysisManagerListener {
|
||||
@Override
|
||||
public void analysisEnded(AutoAnalysisManager manager) {
|
||||
manager.removeListener(this);
|
||||
tool.firePluginEvent(new FirstTimeAnalyzedPluginEvent(AutoAnalysisPlugin.this.getName(),
|
||||
manager.getProgram()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+65
-5
@@ -29,10 +29,18 @@ import ghidra.util.HelpLocation;
|
||||
public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
|
||||
static final String NAVIGATION_TOPIC = "Navigation";
|
||||
public static final String SUB_OPTION = "Starting Program Location";
|
||||
public static final String START_LOCATION_TYPE_OPTION = SUB_OPTION + ".Start At: ";
|
||||
public static final String START_SYMBOLS_OPTION = SUB_OPTION + ".Start Symbols: ";
|
||||
public static final String UNDERSCORE_OPTION = SUB_OPTION + ".Use Underscores:";
|
||||
public static final String START_LOCATION_SUB_OPTION = "Starting Program Location";
|
||||
public static final String START_LOCATION_TYPE_OPTION =
|
||||
START_LOCATION_SUB_OPTION + ".Start At: ";
|
||||
public static final String START_SYMBOLS_OPTION =
|
||||
START_LOCATION_SUB_OPTION + ".Start Symbols: ";
|
||||
public static final String UNDERSCORE_OPTION = START_LOCATION_SUB_OPTION + ".Use Underscores:";
|
||||
|
||||
public static final String AFTER_ANALYSIS_SUB_OPTION = "After Initial Analysis";
|
||||
public static final String ASK_TO_MOVE_OPTION =
|
||||
AFTER_ANALYSIS_SUB_OPTION + ".Ask To Reposition Program";
|
||||
public static final String AUTO_MOVE_OPTION =
|
||||
AFTER_ANALYSIS_SUB_OPTION + ".Auto Reposition If Not Moved";
|
||||
|
||||
private static final String START_LOCATION_DESCRIPTION =
|
||||
"Determines the start location for newly opened programs.\n" +
|
||||
@@ -44,6 +52,12 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
"(Used when option above is set to \"Preferred Symbol Name\")";
|
||||
private static final String SYMBOL_PREFIX_DESCRIPTION =
|
||||
"When searching for symbols, also search for the names prepended with \"_\" and \"__\".";
|
||||
public static final String ASK_TO_MOVE_DESCRIPTION =
|
||||
"When initial analysis completed, asks the user if they want to reposition the" +
|
||||
" program to a newly discovered starting symbol.";
|
||||
public static final String AUTO_MOVE_DESCRIPTION =
|
||||
"When initial analysis is completed, automatically repositions the program to " +
|
||||
"a newly discovered starting symbol, provided the user hasn't manually moved.";
|
||||
|
||||
private static final String DEFAULT_STARTING_SYMBOLS =
|
||||
"main, WinMain, libc_start_main, WinMainStartup, start, entry";
|
||||
@@ -71,13 +85,15 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
private boolean useUnderscorePrefixes;
|
||||
|
||||
private ToolOptions options;
|
||||
private boolean askToMove;
|
||||
private boolean autoMove;
|
||||
|
||||
public ProgramStartingLocationOptions(PluginTool tool) {
|
||||
options = tool.getOptions(GhidraOptions.NAVIGATION_OPTIONS);
|
||||
HelpLocation help = new HelpLocation(NAVIGATION_TOPIC, "Starting_Program_Location");
|
||||
|
||||
// set a help location on the group
|
||||
Options subOptions = options.getOptions(SUB_OPTION);
|
||||
Options subOptions = options.getOptions(START_LOCATION_SUB_OPTION);
|
||||
subOptions.setOptionsHelpLocation(help);
|
||||
|
||||
options.registerOption(START_LOCATION_TYPE_OPTION, StartLocationType.LAST_LOCATION, help,
|
||||
@@ -88,6 +104,13 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
|
||||
options.registerOption(UNDERSCORE_OPTION, true, help, SYMBOL_PREFIX_DESCRIPTION);
|
||||
|
||||
help = new HelpLocation(NAVIGATION_TOPIC, "After_Initial_Analysis");
|
||||
subOptions = options.getOptions(AFTER_ANALYSIS_SUB_OPTION);
|
||||
subOptions.setOptionsHelpLocation(help);
|
||||
|
||||
options.registerOption(ASK_TO_MOVE_OPTION, true, help, ASK_TO_MOVE_DESCRIPTION);
|
||||
options.registerOption(AUTO_MOVE_OPTION, true, help, AUTO_MOVE_DESCRIPTION);
|
||||
|
||||
startLocationType =
|
||||
options.getEnum(START_LOCATION_TYPE_OPTION, StartLocationType.SYMBOL_NAME);
|
||||
|
||||
@@ -95,6 +118,10 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
startSymbols = parse(symbolNames);
|
||||
|
||||
useUnderscorePrefixes = options.getBoolean(UNDERSCORE_OPTION, true);
|
||||
|
||||
askToMove = options.getBoolean(ASK_TO_MOVE_OPTION, true);
|
||||
autoMove = options.getBoolean(AUTO_MOVE_OPTION, true);
|
||||
|
||||
options.addOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
@@ -145,6 +172,32 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
options.removeOptionsChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user should be asked after first analysis if they would like the
|
||||
* program to be repositioned to a newly discovered starting symbol (e.g. "main")
|
||||
*
|
||||
* @return true if the user should be asked after first analysis if they would like the
|
||||
* program to be repositioned to a newly discovered starting symbol (e.g. "main")
|
||||
*/
|
||||
public boolean shouldAskToRepostionAfterAnalysis() {
|
||||
return askToMove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the program should be repositioned to a newly discovered starting symbol
|
||||
* (e.g. "main") when the first analysis is completed, provided the user hasn't manually
|
||||
* changed the program's location. Note that this option has precedence over the
|
||||
* {@link #shouldAskToRepostionAfterAnalysis()} option and the user will only be asked
|
||||
* if they have manually moved the program.
|
||||
*
|
||||
* @return true if the program should be repositioned to a newly discovered starting symbol
|
||||
* (e.g. "main") when the first analysis is completed, provided the user hasn't manually
|
||||
* changed the program's location.
|
||||
*/
|
||||
public boolean shouldAutoRepositionIfNotMoved() {
|
||||
return autoMove;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions toolOptions, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
@@ -157,5 +210,12 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
|
||||
else if (UNDERSCORE_OPTION.equals(optionName)) {
|
||||
useUnderscorePrefixes = (Boolean) newValue;
|
||||
}
|
||||
else if (ASK_TO_MOVE_OPTION.equals(optionName)) {
|
||||
askToMove = (Boolean) newValue;
|
||||
}
|
||||
else if (AUTO_MOVE_OPTION.equals(optionName)) {
|
||||
autoMove = (Boolean) newValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+108
-26
@@ -21,14 +21,14 @@ import java.util.*;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.JDOMException;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.FirstTimeAnalyzedPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.ProgramPlugin;
|
||||
import ghidra.app.plugin.core.navigation.ProgramStartingLocationOptions.StartLocationType;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginInfo;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
@@ -36,7 +36,6 @@ import ghidra.program.model.listing.ProgramUserData;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
|
||||
//@formatter:off
|
||||
@@ -46,37 +45,62 @@ import ghidra.util.xml.XmlUtilities;
|
||||
category = PluginCategoryNames.COMMON,
|
||||
shortDescription = "Determines the starting location when a program is opened.",
|
||||
description = "This plugin watches for new programs being opened and determines the best starting location for the listing view.",
|
||||
servicesRequired = { GoToService.class }
|
||||
servicesRequired = { GoToService.class },
|
||||
eventsConsumed = { FirstTimeAnalyzedPluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||
|
||||
public static enum NonActiveProgramState {
|
||||
NEWLY_OPENED,
|
||||
RESTORED,
|
||||
FIRST_ANALYSIS_COMPLETED
|
||||
}
|
||||
|
||||
private static final String LAST_LOCATION_PROPERTY = "LAST_PROGRAM_LOCATION";
|
||||
private Program lastOpenedProgram;
|
||||
private ProgramStartingLocationOptions startOptions;
|
||||
private Map<Program, ProgramLocation> lastLocationMap = new HashMap<>();
|
||||
private WeakHashMap<Program, ProgramLocation> currentLocationsMap = new WeakHashMap<>();
|
||||
private WeakHashMap<Program, ProgramLocation> startLocationsMap = new WeakHashMap<>();
|
||||
private WeakHashMap<Program, NonActiveProgramState> programStateMap = new WeakHashMap<>();
|
||||
|
||||
public ProgramStartingLocationPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
startOptions = new ProgramStartingLocationOptions(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
|
||||
if (event instanceof FirstTimeAnalyzedPluginEvent ev) {
|
||||
Program program = ev.getProgram();
|
||||
if (program != null) {
|
||||
firstAnalysisCompleted(program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void firstAnalysisCompleted(Program program) {
|
||||
if (program.equals(currentProgram)) {
|
||||
processFirstAnalysisCompleted();
|
||||
}
|
||||
else {
|
||||
programStateMap.put(program, NonActiveProgramState.FIRST_ANALYSIS_COMPLETED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void programOpened(Program program) {
|
||||
// if the open program event is a result of restoring the tool's data state, don't
|
||||
// interfere with the tool's restoration of the last location for that program
|
||||
if (tool.isRestoringDataState()) {
|
||||
return;
|
||||
programStateMap.put(program, NonActiveProgramState.RESTORED);
|
||||
}
|
||||
if (startOptions.getStartLocationType() == StartLocationType.LOWEST_ADDRESS) {
|
||||
// this is what happens by default, so no need to do anything
|
||||
return;
|
||||
else {
|
||||
programStateMap.put(program, NonActiveProgramState.NEWLY_OPENED);
|
||||
}
|
||||
lastOpenedProgram = program;
|
||||
}
|
||||
|
||||
protected void programClosed(Program program) {
|
||||
ProgramLocation lastLocation = lastLocationMap.remove(program);
|
||||
ProgramLocation lastLocation = currentLocationsMap.remove(program);
|
||||
if (lastLocation == null) {
|
||||
return;
|
||||
}
|
||||
@@ -87,22 +111,66 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||
String xmlString = XmlUtilities.toString(saveState.saveToXml());
|
||||
programUserData.setStringProperty(LAST_LOCATION_PROPERTY, xmlString);
|
||||
|
||||
programStateMap.remove(program);
|
||||
currentLocationsMap.remove(program);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void programActivated(Program program) {
|
||||
super.programActivated(program);
|
||||
if (program == lastOpenedProgram) {
|
||||
Swing.runLater(this::setStartingLocationForNewProgram);
|
||||
protected void postProgramActivated(Program program) {
|
||||
NonActiveProgramState state = programStateMap.remove(program);
|
||||
if (state == NonActiveProgramState.NEWLY_OPENED) {
|
||||
setStartingLocationForNewProgram();
|
||||
}
|
||||
lastOpenedProgram = null;
|
||||
else if (state == NonActiveProgramState.FIRST_ANALYSIS_COMPLETED) {
|
||||
processFirstAnalysisCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
private void processFirstAnalysisCompleted() {
|
||||
boolean shouldAskToRepostion = startOptions.shouldAskToRepostionAfterAnalysis();
|
||||
boolean autoRepositionIfNotMoved = startOptions.shouldAutoRepositionIfNotMoved();
|
||||
|
||||
if (!shouldAskToRepostion && !autoRepositionIfNotMoved) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if analysis didn't find any starting symbol, nothing to do
|
||||
Symbol symbol = findStartingSymbol(currentProgram);
|
||||
if (symbol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if already at the symbol's address, don't do anything
|
||||
if (currentLocation != null && currentLocation.getAddress().equals(symbol.getAddress())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoRepositionIfNotMoved && isProgramAtStartingLocation()) {
|
||||
gotoLocation(symbol.getProgramLocation());
|
||||
}
|
||||
else if (shouldAskToRepostion && askToPositionProgram(symbol)) {
|
||||
gotoLocation(symbol.getProgramLocation());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean askToPositionProgram(Symbol symbol) {
|
||||
int result = OptionDialog.showYesNoDialog(null, "Reposition Program?",
|
||||
"Analysis found the symbol \"" + symbol.getName() +
|
||||
"\". Would you like to go to that symbol?");
|
||||
return result == OptionDialog.YES_OPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void locationChanged(ProgramLocation loc) {
|
||||
if (loc != null) {
|
||||
Program program = loc.getProgram();
|
||||
lastLocationMap.put(program, loc);
|
||||
currentLocationsMap.put(program, loc);
|
||||
|
||||
// the startLocationsMap only gets updated with the first location
|
||||
if (!startLocationsMap.containsKey(program)) {
|
||||
startLocationsMap.put(program, loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,15 +179,29 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
GoToService gotoService = tool.getService(GoToService.class);
|
||||
|
||||
ProgramLocation location = getStartingProgramLocation(currentProgram);
|
||||
if (location != null) {
|
||||
gotoService.goTo(location);
|
||||
gotoLocation(location);
|
||||
startLocationsMap.put(currentProgram, location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void gotoLocation(ProgramLocation location) {
|
||||
GoToService gotoService = tool.getService(GoToService.class);
|
||||
gotoService.goTo(location);
|
||||
}
|
||||
|
||||
private boolean isProgramAtStartingLocation() {
|
||||
ProgramLocation startLocation = startLocationsMap.get(currentProgram);
|
||||
if (startLocation == null || currentLocation == null) {
|
||||
return true;
|
||||
}
|
||||
// just compare address, analysis may have tweaked the current location even
|
||||
// the user didn't move
|
||||
return startLocation.getAddress().equals(currentLocation.getAddress());
|
||||
}
|
||||
|
||||
private ProgramLocation getStartingProgramLocation(Program program) {
|
||||
switch (startOptions.getStartLocationType()) {
|
||||
case LAST_LOCATION:
|
||||
@@ -129,7 +211,7 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||
}
|
||||
// fall through and try symbol name
|
||||
case SYMBOL_NAME:
|
||||
Symbol symbol = fingStartingSymbol(program);
|
||||
Symbol symbol = findStartingSymbol(program);
|
||||
if (symbol != null) {
|
||||
return symbol.getProgramLocation();
|
||||
}
|
||||
@@ -158,7 +240,7 @@ public class ProgramStartingLocationPlugin extends ProgramPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private Symbol fingStartingSymbol(Program program) {
|
||||
private Symbol findStartingSymbol(Program program) {
|
||||
List<String> symbolNames = startOptions.getStartingSymbolNames();
|
||||
boolean useUnderscores = startOptions.useUnderscorePrefixes();
|
||||
for (String symbolName : symbolNames) {
|
||||
|
||||
+24
-12
@@ -51,6 +51,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||
|
||||
private Runnable programChangedRunnable;
|
||||
private boolean hasUnsavedPrograms;
|
||||
private String pluginName;
|
||||
|
||||
// These data structures are accessed from multiple threads. Rather than synchronizing all
|
||||
// accesses, we have chosen to be weakly consistent. We assume that any out-of-date checks
|
||||
@@ -64,6 +65,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||
MultiProgramManager(ProgramManagerPlugin programManagerPlugin) {
|
||||
this.plugin = programManagerPlugin;
|
||||
this.tool = programManagerPlugin.getTool();
|
||||
this.pluginName = plugin.getName();
|
||||
|
||||
txMonitor = new TransactionMonitor();
|
||||
txMonitor.setName("Transaction Open (Program being modified)");
|
||||
@@ -173,9 +175,9 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||
Program[] getOtherPrograms() {
|
||||
Program currentProgram = getCurrentProgram();
|
||||
List<Program> list = openPrograms.stream()
|
||||
.map(info -> info.program)
|
||||
.filter(program -> program != currentProgram)
|
||||
.collect(Collectors.toList());
|
||||
.map(info -> info.program)
|
||||
.filter(program -> program != currentProgram)
|
||||
.collect(Collectors.toList());
|
||||
return list.toArray(new Program[list.size()]);
|
||||
}
|
||||
|
||||
@@ -260,25 +262,35 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||
if (toolState != null) {
|
||||
toolState.restoreTool();
|
||||
}
|
||||
// only fire the post activated event when a program is activated (we send activated with
|
||||
// null program to represent a phantom de-activated event)
|
||||
if (newProgram != null) {
|
||||
firePostActivatedEvent(newProgram);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireOpenEvents(Program program) {
|
||||
plugin.firePluginEvent(new ProgramOpenedPluginEvent("", program));
|
||||
plugin.firePluginEvent(new OpenProgramPluginEvent("", program));
|
||||
plugin.firePluginEvent(new ProgramOpenedPluginEvent(pluginName, program));
|
||||
plugin.firePluginEvent(new OpenProgramPluginEvent(pluginName, program));
|
||||
}
|
||||
|
||||
private void fireCloseEvents(Program program) {
|
||||
plugin.firePluginEvent(new ProgramClosedPluginEvent("", program));
|
||||
plugin.firePluginEvent(new CloseProgramPluginEvent("", program, true));
|
||||
plugin.firePluginEvent(new ProgramClosedPluginEvent(pluginName, program));
|
||||
plugin.firePluginEvent(new CloseProgramPluginEvent(pluginName, program, true));
|
||||
// tool.contextChanged();
|
||||
}
|
||||
|
||||
private void fireActivatedEvent(Program newProgram) {
|
||||
plugin.firePluginEvent(new ProgramActivatedPluginEvent("", newProgram));
|
||||
plugin.firePluginEvent(new ProgramActivatedPluginEvent(pluginName, newProgram));
|
||||
}
|
||||
|
||||
private void firePostActivatedEvent(Program newProgram) {
|
||||
plugin.firePluginEvent(new ProgramPostActivatedPluginEvent(pluginName, newProgram));
|
||||
}
|
||||
|
||||
private void fireVisibilityChangeEvent(Program program, boolean isVisible) {
|
||||
plugin.firePluginEvent(new ProgramVisibilityChangePluginEvent("", program, isVisible));
|
||||
plugin.firePluginEvent(
|
||||
new ProgramVisibilityChangePluginEvent(pluginName, program, isVisible));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -503,13 +515,13 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||
private static final AtomicInteger nextAvailableId = new AtomicInteger();
|
||||
|
||||
public final Program program;
|
||||
|
||||
|
||||
// NOTE: domainFile and ghidraURL use are mutually exclusive and reflect how program was
|
||||
// opened. Supported cases include:
|
||||
// 1. Opened via Program file
|
||||
// 2. Opened via ProgramLink file
|
||||
// 3. Opened via Program URL
|
||||
|
||||
|
||||
public final DomainFile domainFile; // may be link file
|
||||
public final URL ghidraURL;
|
||||
|
||||
@@ -532,7 +544,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||
this.visible = visible;
|
||||
instance = nextAvailableId.incrementAndGet();
|
||||
}
|
||||
|
||||
|
||||
ProgramInfo(Program p, URL ghidraURL, boolean visible) {
|
||||
this.program = p;
|
||||
this.domainFile = null;
|
||||
|
||||
@@ -89,4 +89,23 @@ public class GhidraProgramUtilities {
|
||||
program.endTransaction(transactionID, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the program has been analyzed at least once.
|
||||
* @param program the program to test to see if it has been analyzed
|
||||
* @return true if the program has been analyzed at least once.
|
||||
*/
|
||||
public static boolean isAnalyzedFlagSet(Program program) {
|
||||
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||
|
||||
// we first have to check if the flag has even been created because checking the flag
|
||||
// directly causes it to be created if it doesn't exist and we unfortunately use the
|
||||
// existence of the flag to know whether or not to ask the user if they want to start
|
||||
// analysis
|
||||
if (!options.isRegistered(Program.ANALYZED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return options.getBoolean(Program.ANALYZED, false);
|
||||
}
|
||||
}
|
||||
|
||||
+127
-11
@@ -19,7 +19,9 @@ import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.GhidraOptions;
|
||||
import ghidra.app.events.FirstTimeAnalyzedPluginEvent;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@@ -57,7 +59,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolByDefault() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
builder.createLabel("0x105", "main");
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
@@ -66,7 +68,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOpensToLowestCodeBlock() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
MemoryBlock block = builder.createMemory(".text", "0x200", 0x200);
|
||||
builder.setExecute(block, true);
|
||||
builder.createLabel("0x105", "main");
|
||||
@@ -80,7 +82,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolNotFirstInSymbolList() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
setSymbolListOption("main, foobar, start");
|
||||
builder.createLabel("0x107", "start");
|
||||
|
||||
@@ -91,7 +93,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOpensToFirstSymbolWhenMutlipesAreFoud() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
setSymbolListOption("main, start");
|
||||
builder.createLabel("0x110", "start");
|
||||
builder.createLabel("0x105", "start");
|
||||
@@ -103,7 +105,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolWithOneUndercore() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
setSymbolListOption("main, start");
|
||||
builder.createLabel("0x105", "_main");
|
||||
builder.createLabel("0x107", "start");
|
||||
@@ -115,7 +117,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOpensToStartingSymbolWithTwoUndercores() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
setSymbolListOption("main, start");
|
||||
builder.createLabel("0x105", "__main");
|
||||
builder.createLabel("0x107", "_start");
|
||||
@@ -128,7 +130,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testNoUnderscoresSearching() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
setSymbolListOption("main, start");
|
||||
setUnderscoreOption(false);
|
||||
builder.createLabel("0x105", "__main");
|
||||
@@ -142,7 +144,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOptionToStartAtLowestAddress() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
builder.createLabel("0x105", "main");
|
||||
setOptionToLowestAddress();
|
||||
loadProgram(builder.getProgram());
|
||||
@@ -152,7 +154,7 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
|
||||
@Test
|
||||
public void testOptionToStartAtLastLocation() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("0x100");
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
ProgramDB program = builder.getProgram();
|
||||
loadProgram(program);
|
||||
cb.goTo(new ProgramLocation(program, addr("0x107")));
|
||||
@@ -162,10 +164,124 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
assertEquals(addr("0x107"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramAutoRepostionsAfterAnalysis() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
|
||||
simulateAnaysisCreatingMain(builder, "0x105");
|
||||
|
||||
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramAsksToRepostionsAfterAnalysisYes() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
cb.goToField(addr("0x102"), "Address", 0, 0);
|
||||
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||
|
||||
simulateAnaysisCreatingMain(builder, "0x105");
|
||||
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(dialog, "Yes");
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x105"), cb.getCurrentAddress());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgramAsksToRepostionsAfterAnalysisNo() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
loadProgram(builder.getProgram());
|
||||
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
cb.goToField(addr("0x102"), "Address", 0, 0);
|
||||
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||
|
||||
simulateAnaysisCreatingMain(builder, "0x105");
|
||||
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(dialog, "No");
|
||||
waitForSwing();
|
||||
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoMoveOff() throws Exception {
|
||||
ProgramBuilder builder = getProgramBuilder("program 1", "0x100");
|
||||
loadProgram(builder.getProgram());
|
||||
setOptionToNotAutoMove();
|
||||
setOptionToNotAsk();
|
||||
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
|
||||
simulateAnaysisCreatingMain(builder, "0x105");
|
||||
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnalysisHappensWhenProgramIsNotActiveNoUserMove() throws Exception {
|
||||
ProgramBuilder builder1 = getProgramBuilder("program 1", "0x100");
|
||||
loadProgram(builder1.getProgram());
|
||||
ProgramBuilder builder2 = getProgramBuilder("program 2", "0x100");
|
||||
loadProgram(builder2.getProgram());
|
||||
|
||||
simulateAnaysisCreatingMain(builder1, "0x104");
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
|
||||
env.close(builder2.getProgram());
|
||||
|
||||
assertEquals(addr("0x104"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnalysisHappensWhenProgramIsNotActiveWithUserMove() throws Exception {
|
||||
ProgramBuilder builder1 = getProgramBuilder("program 1", "0x100");
|
||||
loadProgram(builder1.getProgram());
|
||||
cb.goToField(addr("0x102"), "Address", 0, 0);
|
||||
assertEquals(addr("0x102"), cb.getCurrentAddress());
|
||||
|
||||
ProgramBuilder builder2 = getProgramBuilder("program 2", "0x100");
|
||||
loadProgram(builder2.getProgram());
|
||||
|
||||
simulateAnaysisCreatingMain(builder1, "0x104");
|
||||
assertEquals(addr("0x100"), cb.getCurrentAddress());
|
||||
|
||||
runSwingLater(() -> env.close(builder2.getProgram()));
|
||||
OptionDialog dialog = waitForDialogComponent(OptionDialog.class);
|
||||
pressButtonByText(dialog, "Yes");
|
||||
waitForSwing();
|
||||
|
||||
assertEquals(addr("0x104"), cb.getCurrentAddress());
|
||||
}
|
||||
|
||||
private void simulateAnaysisCreatingMain(ProgramBuilder builder, String address) {
|
||||
builder.createLabel(address, "main");
|
||||
runSwingLater(() -> tool
|
||||
.firePluginEvent(new FirstTimeAnalyzedPluginEvent("test", builder.getProgram())));
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void setUnderscoreOption(boolean b) {
|
||||
options.setBoolean(ProgramStartingLocationOptions.UNDERSCORE_OPTION, b);
|
||||
}
|
||||
|
||||
private void setOptionToNotAsk() {
|
||||
options.setBoolean(ProgramStartingLocationOptions.ASK_TO_MOVE_OPTION, false);
|
||||
}
|
||||
|
||||
private void setOptionToNotAutoMove() {
|
||||
options.setBoolean(ProgramStartingLocationOptions.AUTO_MOVE_OPTION, false);
|
||||
}
|
||||
|
||||
private void setOptionToLowestAddress() {
|
||||
options.setEnum(ProgramStartingLocationOptions.START_LOCATION_TYPE_OPTION,
|
||||
ProgramStartingLocationOptions.StartLocationType.LOWEST_ADDRESS);
|
||||
@@ -180,8 +296,8 @@ public class ProgramStartPluginTest extends AbstractGhidraHeadedIntegrationTest
|
||||
options.setString(ProgramStartingLocationOptions.START_SYMBOLS_OPTION, symbolListString);
|
||||
}
|
||||
|
||||
private ProgramBuilder getProgramBuilder(String baseAddress) throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder();
|
||||
private ProgramBuilder getProgramBuilder(String name, String baseAddress) throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder(name, ProgramBuilder._TOY);
|
||||
builder.createMemory(".data", baseAddress, 0x100);
|
||||
return builder;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user