mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Changes Program Graph actions to not graph entire program when there is no selection
This commit is contained in:
parent
5b8be18b40
commit
c02ab0160c
4 changed files with 135 additions and 65 deletions
|
@ -45,11 +45,41 @@
|
||||||
systems that have convoluted control flow structure, it may be beneficial to graph the entire
|
systems that have convoluted control flow structure, it may be beneficial to graph the entire
|
||||||
program.</P>
|
program.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>Graph Scope</H2>
|
||||||
|
<P>When creating any of the graphs described below, the scope of the graph is determined by the
|
||||||
|
current location and selection present in the listing. </P>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<H3>If there is a selection:</H3>
|
||||||
|
<BLOCKQUOTE><P> The graph will
|
||||||
|
include all the code that is selected and exclude all the code that is not selected.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
<H3>If there is no selection:</H3>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P> The scope is determined by the current cursor location and the type of graph:</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<H4>Current Location is in a Function</H4>
|
||||||
|
<UL>
|
||||||
|
<LI>Block Flow Graphs: The scope is the body of the containing functions</LI>
|
||||||
|
<LI>Call Graphs: The scope is the containing function and all the functions that
|
||||||
|
either call it or it calls</LI>
|
||||||
|
</UL>
|
||||||
|
<H4>Current Location is not in a Function</H4>
|
||||||
|
<UL>
|
||||||
|
<LI>the scope will be the entire program</LI>
|
||||||
|
</UL>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
<P><IMG src="../../shared/tip.png"> To graph the entire program, press "<ctrl> a" to
|
||||||
|
select all before creating the graph.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2>Synchronization</H2>
|
||||||
|
|
||||||
<P>Selection and Location events are synchronized between each
|
<P>Selection and Location events are synchronized between each
|
||||||
graph and the other windows in the tool.
|
graph and the other windows in the tool.
|
||||||
|
<BLOCKQUOTE>
|
||||||
<H2>Selection</H2>
|
<H3>Selection</H3>
|
||||||
|
|
||||||
<P>The current selection within the graph display is represented by a red box around selected
|
<P>The current selection within the graph display is represented by a red box around selected
|
||||||
nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the
|
nodes as shown below on the node labeled "00408133". A node is selected if any addresses it represents are contained within the
|
||||||
|
@ -74,7 +104,7 @@
|
||||||
from the basic blocks found within the selected subroutine.</P>
|
from the basic blocks found within the selected subroutine.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2>Location</H2>
|
<H3>Location</H3>
|
||||||
|
|
||||||
<P>The node containing the current address location is marked with a large red arrow as shown
|
<P>The node containing the current address location is marked with a large red arrow as shown
|
||||||
below on the graph node labeled "00408133".</P>
|
below on the graph node labeled "00408133".</P>
|
||||||
|
@ -94,7 +124,7 @@
|
||||||
<P align="left">Clicking on a node in the graph display causes the
|
<P align="left">Clicking on a node in the graph display causes the
|
||||||
current address location within Ghidra to change to the minimum address represented by the
|
current address location within Ghidra to change to the minimum address represented by the
|
||||||
graph node.</P>
|
graph node.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
<H2>Graph Representation</H2>
|
<H2>Graph Representation</H2>
|
||||||
|
|
||||||
<P>By Default, the graphs use the following icons and colors to represent the nodes and edges.</P>
|
<P>By Default, the graphs use the following icons and colors to represent the nodes and edges.</P>
|
||||||
|
|
|
@ -21,8 +21,7 @@ import java.util.*;
|
||||||
import docking.widgets.EventTrigger;
|
import docking.widgets.EventTrigger;
|
||||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
|
||||||
import ghidra.program.model.block.*;
|
import ghidra.program.model.block.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
@ -110,8 +109,9 @@ public class BlockGraphTask extends Task {
|
||||||
private boolean reuseGraph;
|
private boolean reuseGraph;
|
||||||
private boolean appendGraph;
|
private boolean appendGraph;
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private String actionName;
|
|
||||||
private Program program;
|
private Program program;
|
||||||
|
private AddressSetView graphScope;
|
||||||
|
private String graphTitle;
|
||||||
|
|
||||||
public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode,
|
public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode,
|
||||||
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
|
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
|
||||||
|
@ -119,8 +119,6 @@ public class BlockGraphTask extends Task {
|
||||||
GraphDisplayProvider graphProvider) {
|
GraphDisplayProvider graphProvider) {
|
||||||
|
|
||||||
super("Graph Program", true, false, true);
|
super("Graph Program", true, false, true);
|
||||||
this.actionName = actionName;
|
|
||||||
|
|
||||||
this.graphEntryPointNexus = graphEntryPointNexus;
|
this.graphEntryPointNexus = graphEntryPointNexus;
|
||||||
this.showCode = showCode;
|
this.showCode = showCode;
|
||||||
this.reuseGraph = reuseGraph;
|
this.reuseGraph = reuseGraph;
|
||||||
|
@ -132,6 +130,7 @@ public class BlockGraphTask extends Task {
|
||||||
this.selection = selection;
|
this.selection = selection;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.program = blockModel.getProgram();
|
this.program = blockModel.getProgram();
|
||||||
|
this.graphTitle = actionName + ": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,6 +138,7 @@ public class BlockGraphTask extends Task {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
|
this.graphScope = getGraphScopeAndGenerateGraphTitle();
|
||||||
AttributedGraph graph = createGraph();
|
AttributedGraph graph = createGraph();
|
||||||
monitor.setMessage("Generating Graph...");
|
monitor.setMessage("Generating Graph...");
|
||||||
try {
|
try {
|
||||||
|
@ -153,7 +153,7 @@ public class BlockGraphTask extends Task {
|
||||||
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
display.setVertexLabel(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
|
||||||
codeLimitPerBlock + 1);
|
codeLimitPerBlock + 1);
|
||||||
}
|
}
|
||||||
display.setGraph(graph, getDescription(), appendGraph, monitor);
|
display.setGraph(graph, graphTitle, appendGraph, monitor);
|
||||||
|
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
// initialize the graph location, but don't have the graph send an event
|
// initialize the graph location, but don't have the graph send an event
|
||||||
|
@ -175,17 +175,6 @@ public class BlockGraphTask extends Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getDescription() {
|
|
||||||
String description = actionName;
|
|
||||||
if (selection != null && !selection.isEmpty()) {
|
|
||||||
description += ": " + selection.getMinAddress();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
description += " (Entire Program)";
|
|
||||||
}
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the maximum number of code lines which will be used per block when
|
* Set the maximum number of code lines which will be used per block when
|
||||||
* showCode is enabled.
|
* showCode is enabled.
|
||||||
|
@ -221,10 +210,62 @@ public class BlockGraphTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
private CodeBlockIterator getBlockIterator() throws CancelledException {
|
private CodeBlockIterator getBlockIterator() throws CancelledException {
|
||||||
if (selection == null || selection.isEmpty()) {
|
return blockModel.getCodeBlocksContaining(graphScope, taskMonitor);
|
||||||
return blockModel.getCodeBlocks(taskMonitor);
|
}
|
||||||
|
|
||||||
|
private AddressSetView getGraphScopeAndGenerateGraphTitle() {
|
||||||
|
if (selection != null && !selection.isEmpty()) {
|
||||||
|
graphTitle += selection.getMinAddress().toString();
|
||||||
|
return selection;
|
||||||
}
|
}
|
||||||
return blockModel.getCodeBlocksContaining(selection, taskMonitor);
|
Function function = getContainingFunction(location);
|
||||||
|
if (function != null) {
|
||||||
|
graphTitle += function.getName();
|
||||||
|
if (isCallGraph()) {
|
||||||
|
return getScopeForCallGraph(function);
|
||||||
|
}
|
||||||
|
return function.getBody();
|
||||||
|
}
|
||||||
|
graphTitle += "(Entire Program)";
|
||||||
|
return blockModel.getProgram().getMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCallGraph() {
|
||||||
|
return blockModel instanceof SubroutineBlockModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AddressSetView getScopeForCallGraph(Function function) {
|
||||||
|
AddressSet set = new AddressSet();
|
||||||
|
set.add(function.getBody());
|
||||||
|
try {
|
||||||
|
CodeBlock block = blockModel.getCodeBlockAt(function.getEntryPoint(), taskMonitor);
|
||||||
|
CodeBlockReferenceIterator it = blockModel.getDestinations(block, taskMonitor);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
CodeBlockReference next = it.next();
|
||||||
|
set.add(next.getDestinationBlock());
|
||||||
|
}
|
||||||
|
it = blockModel.getSources(block, taskMonitor);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
CodeBlockReference next = it.next();
|
||||||
|
set.add(next.getSourceBlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
// just return, the task is being cancelled.
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function getContainingFunction(ProgramLocation cursorLocation) {
|
||||||
|
if (cursorLocation == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Address address = cursorLocation.getAddress();
|
||||||
|
if (address == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return blockModel.getProgram().getFunctionManager().getFunctionContaining(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Address graphBlock(AttributedGraph graph, CodeBlock curBB,
|
private Address graphBlock(AttributedGraph graph, CodeBlock curBB,
|
||||||
|
@ -285,7 +326,7 @@ public class BlockGraphTask extends Task {
|
||||||
|
|
||||||
// don't include destination if it does not overlap selection
|
// don't include destination if it does not overlap selection
|
||||||
// always include if selection is empty
|
// always include if selection is empty
|
||||||
if (selection != null && !selection.isEmpty() && !selection.intersects(db)) {
|
if (graphScope != null && !graphScope.isEmpty() && !graphScope.intersects(db)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,49 +177,49 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
|
|
||||||
new ActionBuilder("Graph Block Flow", getName())
|
new ActionBuilder("Graph Block Flow", getName())
|
||||||
.menuPath(MENU_GRAPH, "&Block Flow")
|
.menuPath(MENU_GRAPH, "&Block Flow")
|
||||||
.menuGroup("Graph", "A")
|
.menuGroup("Graph", "A")
|
||||||
.onAction(c -> graphBlockFlow())
|
.onAction(c -> graphBlockFlow())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph Code Flow", getName())
|
new ActionBuilder("Graph Code Flow", getName())
|
||||||
.menuPath(MENU_GRAPH, "C&ode Flow")
|
.menuPath(MENU_GRAPH, "C&ode Flow")
|
||||||
.menuGroup("Graph", "B")
|
.menuGroup("Graph", "B")
|
||||||
.onAction(c -> graphCodeFlow())
|
.onAction(c -> graphCodeFlow())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
new ActionBuilder("Graph Calls Using Default Model", getName())
|
new ActionBuilder("Graph Calls Using Default Model", getName())
|
||||||
.menuPath(MENU_GRAPH, "&Calls")
|
.menuPath(MENU_GRAPH, "&Calls")
|
||||||
.menuGroup("Graph", "C")
|
.menuGroup("Graph", "C")
|
||||||
.onAction(c -> graphSubroutines())
|
.onAction(c -> graphSubroutines())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
reuseGraphAction = new ToggleActionBuilder("Reuse Graph", getName())
|
reuseGraphAction = new ToggleActionBuilder("Reuse Graph", getName())
|
||||||
.menuPath(MENU_GRAPH, "Reuse Graph")
|
.menuPath(MENU_GRAPH, "Reuse Graph")
|
||||||
.menuGroup("Graph Options")
|
.menuGroup("Graph Options")
|
||||||
.selected(reuseGraph)
|
.selected(reuseGraph)
|
||||||
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
.onAction(c -> reuseGraph = reuseGraphAction.isSelected())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
appendGraphAction = new ToggleActionBuilder("Append Graph", getName())
|
appendGraphAction = new ToggleActionBuilder("Append Graph", getName())
|
||||||
.menuPath(MENU_GRAPH, "Append Graph")
|
.menuPath(MENU_GRAPH, "Append Graph")
|
||||||
.menuGroup("Graph Options")
|
.menuGroup("Graph Options")
|
||||||
.selected(false)
|
.selected(false)
|
||||||
.onAction(c -> updateAppendAndReuseGraph())
|
.onAction(c -> updateAppendAndReuseGraph())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
forceLocationVisibleAction = new ToggleActionBuilder("Show Location in Graph", getName())
|
forceLocationVisibleAction = new ToggleActionBuilder("Show Location in Graph", getName())
|
||||||
.menuPath(MENU_GRAPH, "Show Location")
|
.menuPath(MENU_GRAPH, "Show Location")
|
||||||
.description("Tell the graph to pan/scale as need to keep location changes visible")
|
.description("Tell the graph to pan/scale as need to keep location changes visible")
|
||||||
.menuGroup("Graph Options")
|
.menuGroup("Graph Options")
|
||||||
.onAction(c -> toggleForceLocationVisible())
|
.onAction(c -> toggleForceLocationVisible())
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
|
|
||||||
updateSubroutineActions();
|
updateSubroutineActions();
|
||||||
}
|
}
|
||||||
|
@ -268,12 +268,12 @@ public class ProgramGraphPlugin extends ProgramPlugin
|
||||||
|
|
||||||
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
private DockingAction buildGraphActionWithModel(String blockModelName, HelpLocation helpLoc) {
|
||||||
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
return new ActionBuilder("Graph Calls using " + blockModelName, getName())
|
||||||
.menuPath("Graph", "Calls Using Model", blockModelName)
|
.menuPath("Graph", "Calls Using Model", blockModelName)
|
||||||
.menuGroup("Graph")
|
.menuGroup("Graph")
|
||||||
.helpLocation(helpLoc)
|
.helpLocation(helpLoc)
|
||||||
.onAction(c -> graphSubroutinesUsing(blockModelName))
|
.onAction(c -> graphSubroutinesUsing(blockModelName))
|
||||||
.enabledWhen(this::canGraph)
|
.enabledWhen(this::canGraph)
|
||||||
.buildAndInstall(tool);
|
.buildAndInstall(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void graphBlockFlow() {
|
private void graphBlockFlow() {
|
||||||
|
|
|
@ -65,7 +65,6 @@ public class BlockGraphEventTest extends AbstractBlockGraphTest {
|
||||||
assertEquals("01002239", display.getFocusedVertex().getId());
|
assertEquals("01002239", display.getFocusedVertex().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private AddressSet addrSet(long start, long end) {
|
private AddressSet addrSet(long start, long end) {
|
||||||
return new AddressSet(addr(start), addr(end));
|
return new AddressSet(addr(start), addr(end));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue