Created the concept of graph types and display options for those graph types.

This commit is contained in:
ghidravore 2021-08-09 13:18:23 -04:00
parent cf293853e8
commit 210cc0bca0
84 changed files with 4102 additions and 1822 deletions

View file

@ -489,16 +489,18 @@
</BLOCKQUOTE>
</BLOCKQUOTE>
<BR>
<BR>
<BR>
<BR>
<BR>
</ul>
<P class="providedbyplugin">Provided by: <I>Program Graph Plugin</I></P>
<P class="relatedtopic">Related Topics</P>
<H2><A NAME="Program_Graphs_Display_Options">Program Graphs Display Options</H2>
<BLOCKQUOTE>
<P>These are the display options for graphs that are types of "Program Graphs" such as
Call graphs, Block graphs, etc. These types of graphs
use program elements as vertices and reference types as edges. See
<A href="help/topics/GraphServices/GraphDisplay.htm#Graph_Type_Display_Options">Graph Type Display Options</A> for
general help on graph type display options.</P>
</BLOCKQUOTE>
<P class="providedbyplugin">Provided by: <I>Program Graph Plugin</I></P>
<P class="relatedtopic">Related Topics</P>
<UL>
<LI><A href="help/topics/GraphServices/GraphDisplay.htm">Default Graph Display</A></LI>
<LI><A href="help/topics/GraphServices/GraphExport.htm">Graph Export</A></LI>

View file

@ -15,7 +15,8 @@
*/
package ghidra.graph.program;
import java.awt.Color;
import static ghidra.graph.ProgramGraphType.*;
import java.util.*;
import docking.action.builder.ActionBuilder;
@ -23,6 +24,7 @@ import docking.widgets.EventTrigger;
import ghidra.app.plugin.core.colorizer.ColorizingService;
import ghidra.app.util.AddEditDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.graph.*;
import ghidra.program.model.address.*;
import ghidra.program.model.block.*;
import ghidra.program.model.listing.*;
@ -30,7 +32,8 @@ import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.service.graph.*;
import ghidra.util.*;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.GraphException;
import ghidra.util.task.Task;
@ -53,55 +56,6 @@ public class BlockGraphTask extends Task {
private ColorizingService colorizingService;
/**
* Edge flow tags
*/
protected final static int FALLTHROUGH = 0;
protected final static int CONDITIONAL_RETURN = 1;
protected final static int UNCONDITIONAL_JUMP = 2;
protected final static int CONDITIONAL_JUMP = 3;
protected final static int UNCONDITIONAL_CALL = 4;
protected final static int CONDITIONAL_CALL = 5;
protected final static int TERMINATOR = 6;
protected final static int COMPUTED = 7;
protected final static int INDIRECTION = 8;
protected final static int ENTRY = 9; // from Entry Nexus
protected final static String[] edgeNames =
{ "1", "2", "3", "4", "5", "6", "7", "13", "14", "15" };
// @formatter:off
protected final static String[] edgeTypes = {
"Fall-Through",
"Conditional-Return",
"Unconditional-Jump",
"Conditional-Jump",
"Unconditional-Call",
"Conditional-Call",
"Terminator",
"Computed",
"Indirection",
"Entry"
};
// @formatter:on
private final static String ENTRY_NODE = "Entry";
// "1"; // beginning of a block, someone calls it
private final static String BODY_NODE = "Body";
// "2"; // Body block, no flow
private final static String EXIT_NODE = "Exit";
// "3"; // Terminator
private final static String SWITCH_NODE = "Switch";
// "4"; // Switch/computed jump
private final static String BAD_NODE = "Bad";
// "5"; // Bad destination
private final static String DATA_NODE = "Data";
// "6"; // Data Node, used for indirection
private final static String ENTRY_NEXUS = "Entry-Nexus";
// "7"; //
private final static String EXTERNAL_NODE = "External";
// "8"; // node is external to program
private final static String ENTRY_NEXUS_NAME = "Entry Points";
private CodeBlockModel blockModel;
private AddressSetView selection;
@ -113,15 +67,17 @@ public class BlockGraphTask extends Task {
private Program program;
private AddressSetView graphScope;
private String graphTitle;
private ProgramGraphType graphType;
public BlockGraphTask(String actionName, boolean graphEntryPointNexus, boolean showCode,
boolean reuseGraph, boolean appendGraph, PluginTool tool, ProgramSelection selection,
ProgramLocation location, CodeBlockModel blockModel,
GraphDisplayProvider graphProvider) {
public BlockGraphTask(ProgramGraphType graphType,
boolean graphEntryPointNexus, boolean reuseGraph, boolean appendGraph,
PluginTool tool, ProgramSelection selection, ProgramLocation location,
CodeBlockModel blockModel, GraphDisplayProvider graphProvider) {
super("Graph Program", true, false, true);
this.graphType = graphType;
this.graphEntryPointNexus = graphEntryPointNexus;
this.showCode = showCode;
this.showCode = graphType instanceof CodeFlowGraphType;
this.reuseGraph = reuseGraph;
this.appendGraph = appendGraph;
this.tool = tool;
@ -131,7 +87,7 @@ public class BlockGraphTask extends Task {
this.selection = selection;
this.location = location;
this.program = blockModel.getProgram();
this.graphTitle = actionName + ": ";
this.graphTitle = graphType.getName() + ": ";
}
/**
@ -140,22 +96,25 @@ public class BlockGraphTask extends Task {
@Override
public void run(TaskMonitor monitor) throws CancelledException {
this.graphScope = getGraphScopeAndGenerateGraphTitle();
AttributedGraph graph = createGraph();
AttributedGraph graph = createGraph(graphTitle);
monitor.setMessage("Generating Graph...");
try {
GraphDisplay display = graphProvider.getGraphDisplay(reuseGraph, monitor);
GraphDisplay display =
graphProvider.getGraphDisplay(reuseGraph, monitor);
GraphDisplayOptions graphOptions = new ProgramGraphDisplayOptions(graphType, tool);
if (showCode) { // arrows need to be bigger as this generates larger vertices
graphOptions.setArrowLength(30);
}
BlockModelGraphDisplayListener listener =
new BlockModelGraphDisplayListener(tool, blockModel, display);
addActions(display, v -> listener.getAddress(v));
display.setGraphDisplayListener(listener);
if (showCode) {
display.defineVertexAttribute(CODE_ATTRIBUTE);
display.defineVertexAttribute(SYMBOLS_ATTRIBUTE);
display.setVertexLabelAttribute(CODE_ATTRIBUTE, GraphDisplay.ALIGN_LEFT, 12, true,
codeLimitPerBlock + 1);
graphOptions.setVertexLabelOverrideAttributeKey(CODE_ATTRIBUTE);
}
display.setGraph(graph, graphTitle, appendGraph, monitor);
display.setGraph(graph, graphOptions, graphTitle, appendGraph, monitor);
if (location != null) {
// initialize the graph location, but don't have the graph send an event
@ -217,9 +176,9 @@ public class BlockGraphTask extends Task {
codeLimitPerBlock = maxLines;
}
protected AttributedGraph createGraph() throws CancelledException {
protected AttributedGraph createGraph(String name) throws CancelledException {
int blockCount = 0;
AttributedGraph graph = new AttributedGraph();
AttributedGraph graph = new AttributedGraph(name, graphType);
CodeBlockIterator it = getBlockIterator();
List<AttributedVertex> entryPoints = new ArrayList<>();
@ -334,8 +293,7 @@ public class BlockGraphTask extends Task {
AttributedVertex entryNexusVertex = getEntryNexusVertex(graph);
for (AttributedVertex vertex : entries) {
AttributedEdge edge = graph.addEdge(entryNexusVertex, vertex);
edge.setAttribute("Name", edgeNames[ENTRY]);
edge.setAttribute("EdgeType", edgeTypes[ENTRY]);
edge.setAttribute("EdgeType", ENTRY_NEXUS);
}
}
@ -517,99 +475,36 @@ public class BlockGraphTask extends Task {
}
protected void setEdgeAttributes(AttributedEdge edge, CodeBlockReference ref) {
int edgeType;
FlowType flowType = ref.getFlowType();
if (flowType == RefType.FALL_THROUGH) {
edgeType = FALLTHROUGH;
}
else if (flowType == RefType.UNCONDITIONAL_JUMP) {
edgeType = UNCONDITIONAL_JUMP;
}
else if (flowType == RefType.CONDITIONAL_JUMP) {
edgeType = CONDITIONAL_JUMP;
}
else if (flowType == RefType.UNCONDITIONAL_CALL) {
edgeType = UNCONDITIONAL_CALL;
}
else if (flowType == RefType.CONDITIONAL_CALL) {
edgeType = CONDITIONAL_CALL;
}
else if (flowType.isComputed()) {
edgeType = COMPUTED;
}
else if (flowType.isIndirect()) {
edgeType = INDIRECTION;
}
else if (flowType == RefType.TERMINATOR) {
edgeType = TERMINATOR;
}
else { // only FlowType.CONDITIONAL_TERMINATOR remains unchecked
edgeType = CONDITIONAL_RETURN;
}
// set attributes on this edge
edge.setAttribute("Name", edgeNames[edgeType]);
edge.setAttribute("EdgeType", edgeTypes[edgeType]);
edge.setEdgeType(ProgramGraphType.getEdgeType(ref.getFlowType()));
}
protected void setVertexAttributes(AttributedVertex vertex, CodeBlock bb, boolean isEntry) {
String vertexType = BODY_NODE;
String vertexType = BODY;
Address firstStartAddress = bb.getFirstStartAddress();
if (firstStartAddress.isExternalAddress()) {
vertexType = EXTERNAL_NODE;
vertexType = EXTERNAL;
}
else if (isEntry) {
vertexType = ENTRY_NODE;
vertexType = ENTRY;
}
else {
FlowType flowType = bb.getFlowType();
if (flowType.isTerminal()) {
vertexType = EXIT_NODE;
vertexType = EXIT;
}
else if (flowType.isComputed()) {
vertexType = SWITCH_NODE;
vertexType = SWITCH;
}
else if (flowType == RefType.INDIRECTION) {
vertexType = DATA_NODE;
vertexType = DATA;
}
else if (flowType == RefType.INVALID) {
vertexType = BAD_NODE;
vertexType = BAD;
}
}
vertex.setAttribute("VertexType", vertexType);
setVertexColor(vertex, vertexType, firstStartAddress);
}
private void setVertexColor(AttributedVertex vertex, String vertexType, Address address) {
if (colorizingService == null) {
return;
}
Color color = colorizingService.getBackgroundColor(address);
if (color == null) {
return;
}
// color format: RGBrrrgggbbb
// -where rrr/ggg/bbb is a three digit int value for each respective color range
String rgb = "RGB" + HTMLUtilities.toRGBString(color);
vertex.setAttribute("Color", rgb); // sets the vertex color
// This value triggers the vertex to be painted with its color and not a
// while background.
if (showCode) {
// our own custom override of Labels/Icons
vertex.setAttribute("VertexType", "ColorFilled");
}
else {
// the default preferences for VertexType
vertex.setAttribute("VertexType", vertexType + ".Filled");
}
vertex.setVertexType(vertexType);
}
private AttributedVertex getEntryNexusVertex(AttributedGraph graph) {

View file

@ -15,6 +15,8 @@
*/
package ghidra.graph.program;
import ghidra.graph.DataFlowGraphType;
import ghidra.graph.ProgramGraphType;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Reference;
@ -53,6 +55,7 @@ public class DataReferenceGraph extends AttributedGraph {
* @param depth the number of hops to graph per call (0 for recursion until no more hops)
*/
public DataReferenceGraph(Program program, int depth) {
super("Data Reference", new DataFlowGraphType());
this.program = program;
this.depthPerStep = depth;
}
@ -106,13 +109,14 @@ public class DataReferenceGraph extends AttributedGraph {
private void setupEdge(AttributedEdge edge, Reference ref) {
edge.setAttribute(REF_SOURCE_ATTRIBUTE, ref.getSource().getDisplayString());
edge.setAttribute(REF_TYPE_ATTRIBUTE, ref.getReferenceType().toString());
edge.setEdgeType(ProgramGraphType.getEdgeType(ref.getReferenceType()));
if (ref.getSymbolID() != -1) {
edge.setAttribute(REF_SYMBOL_ATTRIBUTE,
program.getSymbolTable().getSymbol(ref.getSymbolID()).getName());
}
}
private void setupVertex(AttributedVertex vertex) {
Address address =
program.getAddressFactory().getAddress(vertex.getAttribute(ADDRESS_ATTRIBUTE));
@ -122,9 +126,13 @@ public class DataReferenceGraph extends AttributedGraph {
CodeUnit unit = program.getListing().getCodeUnitContaining(address);
if (unit instanceof Data) {
vertex.setAttribute(DATA_ATTRIBUTE, ((Data) unit).getBaseDataType().getName());
vertex.setVertexType(ProgramGraphType.DATA);
}
else if (unit instanceof Instruction) {
vertex.setAttribute("Icon", "TriangleDown");
vertex.setVertexType(ProgramGraphType.INSTRUCTION);
}
else {
vertex.setVertexType(ProgramGraphType.STACK);
}
}

View file

@ -17,6 +17,8 @@ package ghidra.graph.program;
import docking.widgets.EventTrigger;
import ghidra.framework.plugintool.PluginTool;
import ghidra.graph.DataFlowGraphType;
import ghidra.graph.ProgramGraphDisplayOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.CodeUnit;
@ -35,6 +37,7 @@ import ghidra.util.task.TaskMonitor;
*/
public class DataReferenceGraphTask extends Task {
private static final String VERTEX_COLOR_OVERRIDE = "Color";
private String graphTitle;
private GraphDisplayProvider graphProvider;
private boolean reuseGraph;
@ -117,7 +120,7 @@ public class DataReferenceGraphTask extends Task {
/* TODO
* Want to make initial vertex easy to find, is this the best way?
*/
centerVertex.setAttribute("Color", "Orange");
centerVertex.setAttribute(VERTEX_COLOR_OVERRIDE, "Orange");
}
}
catch (CancelledException e) {
@ -128,19 +131,16 @@ public class DataReferenceGraphTask extends Task {
try {
if (display == null) {
display = graphProvider.getGraphDisplay(reuseGraph, monitor);
display.defineEdgeAttribute(DataReferenceGraph.REF_SOURCE_ATTRIBUTE);
display.defineEdgeAttribute(DataReferenceGraph.REF_TYPE_ATTRIBUTE);
display.defineEdgeAttribute(DataReferenceGraph.REF_SYMBOL_ATTRIBUTE);
display.defineVertexAttribute(DataReferenceGraph.DATA_ATTRIBUTE);
display.setVertexLabelAttribute(DataReferenceGraph.LABEL_ATTRIBUTE,
GraphDisplay.ALIGN_LEFT, 12, true, maxLabelLength);
DataReferenceGraphDisplayListener listener =
new DataReferenceGraphDisplayListener(tool, display, program, totalMaxDepth);
display.setGraphDisplayListener(listener);
}
display.setGraph(graph, graphTitle, appendGraph, monitor);
GraphDisplayOptions graphDisplayOptions =
new ProgramGraphDisplayOptions(new DataFlowGraphType(), tool);
// set the vertex color override so that we can color "initial" vertices differently
graphDisplayOptions.setVertexColorOverrideAttributeKey(VERTEX_COLOR_OVERRIDE);
display.setGraph(graph, graphDisplayOptions, graphTitle, appendGraph, monitor);
if (location != null) {
// initialize the graph location, but don't have the graph send an event

View file

@ -35,6 +35,7 @@ import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.graph.*;
import ghidra.program.model.block.CodeBlockModel;
import ghidra.service.graph.GraphDisplayProvider;
import ghidra.util.HelpLocation;
@ -106,6 +107,16 @@ public class ProgramGraphPlugin extends ProgramPlugin
public ProgramGraphPlugin(PluginTool tool) {
super(tool, true, true);
intializeOptions();
registerProgramFlowGraphDisplayOptionsWithTool();
}
private void registerProgramFlowGraphDisplayOptionsWithTool() {
ProgramGraphDisplayOptions displayOptions =
new ProgramGraphDisplayOptions(new BlockFlowGraphType(), null);
// this will register Program Flow Graph Type options with the tool
HelpLocation help = new HelpLocation(getName(), "Program Graphs Display Options");
displayOptions.registerOptions(tool.getOptions("Graph"), help);
}
private void intializeOptions() {
@ -315,19 +326,19 @@ public class ProgramGraphPlugin extends ProgramPlugin
}
private void graphBlockFlow() {
graph("Block Flow Graph", blockModelService.getActiveBlockModelName(), false);
graph(new BlockFlowGraphType(), blockModelService.getActiveBlockModelName());
}
private void graphCodeFlow() {
graph("Code Flow Graph", blockModelService.getActiveBlockModelName(), true);
graph(new CodeFlowGraphType(), blockModelService.getActiveBlockModelName());
}
private void graphSubroutines() {
graph("Call Graph", blockModelService.getActiveSubroutineModelName(), false);
graph(new CallGraphType(), blockModelService.getActiveSubroutineModelName());
}
private void graphSubroutinesUsing(String modelName) {
graph("Call Graph (" + modelName + ")", modelName, false);
graph(new CallGraphType(), modelName);
}
private void graphDataReferences() {
@ -342,11 +353,12 @@ public class ProgramGraphPlugin extends ProgramPlugin
graphData(DataReferenceGraph.Directions.FROM_ONLY);
}
private void graph(String actionName, String modelName, boolean showCode) {
private void graph(ProgramGraphType graphType, String modelName) {
try {
CodeBlockModel model =
blockModelService.getNewModelByName(modelName, currentProgram, true);
BlockGraphTask task = new BlockGraphTask(actionName, graphEntryPointNexus, showCode,
BlockGraphTask task =
new BlockGraphTask(graphType, graphEntryPointNexus,
reuseGraph, appendToGraph, tool, currentSelection, currentLocation, model,
defaultGraphService);
task.setCodeLimitPerBlock(codeLimitPerBlock);