mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Created the concept of graph types and display options for those graph types.
This commit is contained in:
parent
cf293853e8
commit
210cc0bca0
84 changed files with 4102 additions and 1822 deletions
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue