Moving 'AST Graph' from scripting to an action in the decompiler window so that options are persistent. Also renamed it to 'Graph Data Flow'. Renamed existing graph action to 'Graph Control Flow'.

This commit is contained in:
ghidravore 2022-02-22 14:26:01 -05:00
parent 073c726885
commit 467133de3e
18 changed files with 734 additions and 376 deletions

View file

@ -1,317 +0,0 @@
/* ###
* 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.
*/
//Decompile the function at the cursor, then build data-flow graph (AST)
//@category PCode
import java.util.*;
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*;
import ghidra.service.graph.*;
import ghidra.util.Msg;
import ghidra.util.WebColors;
public class GraphAST extends GhidraScript {
private static final String SHAPE_ATTRIBUTE = "Shape";
protected static final String DEFAULT = "Default";
protected static final String CONSTANT = "Constant";
protected static final String REGISTER = "Register";
protected static final String UNIQUE = "Unique";
protected static final String PERSISTENT = "Persistent";
protected static final String ADDRESS_TIED = "Address Tied";
protected static final String OP = "Op";
protected static final String WITHIN_BLOCK = "Within Block";
protected static final String BETWEEN_BLOCK = "Between Block";
private Function func;
private AttributedGraph graph;
protected HighFunction high;
@Override
public void run() throws Exception {
PluginTool tool = state.getTool();
if (tool == null) {
println("Script is not running in GUI");
}
GraphDisplayBroker graphDisplayBroker = tool.getService(GraphDisplayBroker.class);
if (graphDisplayBroker == null) {
Msg.showError(this, tool.getToolFrame(), "GraphAST Error",
"No graph display providers found: Please add a graph display provider to your tool");
return;
}
func = this.getFunctionContaining(this.currentAddress);
if (func == null) {
Msg.showWarn(this, state.getTool().getToolFrame(), "GraphAST Error",
"No Function at current location");
return;
}
buildAST();
GraphType graphType = new GraphTypeBuilder("AST")
.vertexType(DEFAULT)
.vertexType(CONSTANT)
.vertexType(REGISTER)
.vertexType(UNIQUE)
.vertexType(PERSISTENT)
.vertexType(ADDRESS_TIED)
.vertexType(OP)
.edgeType(DEFAULT)
.edgeType(WITHIN_BLOCK)
.edgeType(BETWEEN_BLOCK)
.build();
GraphDisplayOptions displayOptions = new GraphDisplayOptionsBuilder(graphType)
.vertexSelectionColor(WebColors.DEEP_PINK)
.edgeSelectionColor(WebColors.DEEP_PINK)
.defaultVertexColor(WebColors.RED)
.defaultEdgeColor(WebColors.NAVY)
.defaultVertexShape(VertexShape.ELLIPSE)
.defaultLayoutAlgorithm("Hierarchical MinCross Coffman Graham")
.useIcons(false)
.arrowLength(15)
.labelPosition(GraphLabelPosition.SOUTH)
.shapeOverrideAttribute(SHAPE_ATTRIBUTE)
.vertex(DEFAULT, VertexShape.ELLIPSE, WebColors.RED)
.vertex(CONSTANT, VertexShape.ELLIPSE, WebColors.DARK_GREEN)
.vertex(REGISTER, VertexShape.ELLIPSE, WebColors.NAVY)
.vertex(UNIQUE, VertexShape.ELLIPSE, WebColors.BLACK)
.vertex(PERSISTENT, VertexShape.ELLIPSE, WebColors.DARK_ORANGE)
.vertex(ADDRESS_TIED, VertexShape.ELLIPSE, WebColors.ORANGE)
.vertex(OP, VertexShape.RECTANGLE, WebColors.RED)
.edge(DEFAULT, WebColors.BLUE)
.edge(WITHIN_BLOCK, WebColors.BLACK)
.edge(BETWEEN_BLOCK, WebColors.RED)
.build();
graph = new AttributedGraph("AST Graph", graphType);
buildGraph();
GraphDisplay graphDisplay = graphDisplayBroker.getDefaultGraphDisplay(false, monitor);
String description = "AST Data Flow Graph For " + func.getName();
graphDisplay.setGraph(graph, displayOptions, description, false, monitor);
// Install a handler so the selection/location will map
graphDisplay.setGraphDisplayListener(
new ASTGraphDisplayListener(tool, graphDisplay, high, func.getProgram()));
}
private void buildAST() throws DecompileException {
DecompileOptions options = new DecompileOptions();
DecompInterface ifc = new DecompInterface();
ifc.setOptions(options);
if (!ifc.openProgram(this.currentProgram)) {
throw new DecompileException("Decompiler",
"Unable to initialize: " + ifc.getLastMessage());
}
ifc.setSimplificationStyle("normalize");
DecompileResults res = ifc.decompileFunction(func, 30, null);
high = res.getHighFunction();
}
private String getVarnodeKey(VarnodeAST vn) {
PcodeOp op = vn.getDef();
String id;
if (op != null) {
id = op.getSeqnum().getTarget().toString(true) + " v " +
Integer.toString(vn.getUniqueId());
}
else {
id = "i v " + Integer.toString(vn.getUniqueId());
}
return id;
}
private String getOpKey(PcodeOpAST op) {
SequenceNumber sq = op.getSeqnum();
String id =
sq.getTarget().toString(true) + " o " + Integer.toString(op.getSeqnum().getTime());
return id;
}
protected AttributedVertex createVarnodeVertex(VarnodeAST vn) {
String name = vn.getAddress().toString(true);
String id = getVarnodeKey(vn);
String vertexType = DEFAULT;
if (vn.isConstant()) {
vertexType = CONSTANT;
}
else if (vn.isRegister()) {
vertexType = REGISTER;
Register reg = func.getProgram().getRegister(vn.getAddress(), vn.getSize());
if (reg != null) {
name = reg.getName();
}
}
else if (vn.isUnique()) {
vertexType = UNIQUE;
}
else if (vn.isPersistent()) {
vertexType = PERSISTENT;
}
else if (vn.isAddrTied()) {
vertexType = ADDRESS_TIED;
}
AttributedVertex vert = graph.addVertex(id, name);
vert.setVertexType(vertexType);
// if it is an input override the shape to be a triangle
if (vn.isInput()) {
vert.setAttribute(SHAPE_ATTRIBUTE, VertexShape.TRIANGLE_DOWN.getName());
}
return vert;
}
protected AttributedVertex createOpVertex(PcodeOpAST op) {
String name = op.getMnemonic();
String id = getOpKey(op);
int opcode = op.getOpcode();
if ((opcode == PcodeOp.LOAD) || (opcode == PcodeOp.STORE)) {
Varnode vn = op.getInput(0);
AddressSpace addrspace =
func.getProgram().getAddressFactory().getAddressSpace((int) vn.getOffset());
name += ' ' + addrspace.getName();
}
else if (opcode == PcodeOp.INDIRECT) {
Varnode vn = op.getInput(1);
if (vn != null) {
PcodeOp indOp = high.getOpRef((int) vn.getOffset());
if (indOp != null) {
name += " (" + indOp.getMnemonic() + ')';
}
}
}
AttributedVertex vert = graph.addVertex(id, name);
vert.setVertexType(OP);
return vert;
}
protected AttributedVertex getVarnodeVertex(Map<Integer, AttributedVertex> vertices,
VarnodeAST vn) {
AttributedVertex res;
res = vertices.get(vn.getUniqueId());
if (res == null) {
res = createVarnodeVertex(vn);
vertices.put(vn.getUniqueId(), res);
}
return res;
}
protected AttributedEdge createEdge(AttributedVertex in, AttributedVertex out) {
AttributedEdge newEdge = graph.addEdge(in, out);
newEdge.setEdgeType(DEFAULT);
return newEdge;
}
protected void buildGraph() {
HashMap<Integer, AttributedVertex> vertices = new HashMap<>();
Iterator<PcodeOpAST> opiter = getPcodeOpIterator();
while (opiter.hasNext()) {
PcodeOpAST op = opiter.next();
AttributedVertex o = createOpVertex(op);
for (int i = 0; i < op.getNumInputs(); ++i) {
int opcode = op.getOpcode();
if ((i == 0) && ((opcode == PcodeOp.LOAD) || (opcode == PcodeOp.STORE))) {
continue;
}
if ((i == 1) && (opcode == PcodeOp.INDIRECT)) {
continue;
}
VarnodeAST vn = (VarnodeAST) op.getInput(i);
if (vn != null) {
AttributedVertex v = getVarnodeVertex(vertices, vn);
createEdge(v, o);
}
}
VarnodeAST outvn = (VarnodeAST) op.getOutput();
if (outvn != null) {
AttributedVertex outv = getVarnodeVertex(vertices, outvn);
if (outv != null) {
createEdge(o, outv);
}
}
}
}
protected Iterator<PcodeOpAST> getPcodeOpIterator() {
Iterator<PcodeOpAST> opiter = high.getPcodeOps();
return opiter;
}
class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
HighFunction highfunc;
public ASTGraphDisplayListener(PluginTool tool, GraphDisplay display, HighFunction high,
Program program) {
super(tool, program, display);
highfunc = high;
}
@Override
protected Set<AttributedVertex> getVertices(AddressSetView selection) {
return Collections.emptySet();
}
@Override
protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
AddressSet set = new AddressSet();
for (AttributedVertex vertex : vertices) {
Address address = getAddress(vertex);
if (address != null) {
set.add(address);
}
}
return set;
}
@Override
protected Address getAddress(AttributedVertex vertex) {
if (vertex == null) {
return null;
}
String vertexId = vertex.getId();
int firstcolon = vertexId.indexOf(':');
if (firstcolon == -1) {
return null;
}
int firstSpace = vertexId.indexOf(' ');
String addrString = vertexId.substring(0, firstSpace);
return getAddress(addrString);
}
@Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
return new ASTGraphDisplayListener(tool, graphDisplay, highfunc, currentProgram);
}
}
}

View file

@ -13,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Decompile the function at the cursor, then build data-flow graph (AST) for the current address
//Decompile the function at the cursor, then build data-flow graph (AST) with flow edges
//@category PCode
import java.util.Iterator;
import ghidra.app.plugin.core.decompile.actions.PCodeCombinedGraphTask;
import ghidra.app.plugin.core.decompile.actions.PCodeDfgGraphTask;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.program.model.pcode.PcodeOpAST;
public class GraphASTAndFlowScript extends GraphASTScript {
public class GraphSelectedAST extends GraphAST {
protected Iterator<PcodeOpAST> getPcodeOpIterator() {
Iterator<PcodeOpAST> opiter = high.getPcodeOps(this.currentAddress);
return opiter;
protected PCodeDfgGraphTask createTask(GraphDisplayBroker graphDisplayBroker) {
return new PCodeCombinedGraphTask(state.getTool(), graphDisplayBroker, high);
}
}

View file

@ -0,0 +1,77 @@
/* ###
* 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.
*/
//Decompile the function at the cursor, then build data-flow graph (AST)
//@category PCode
import ghidra.app.decompiler.*;
import ghidra.app.plugin.core.decompile.actions.PCodeDfgGraphTask;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.HighFunction;
import ghidra.util.Msg;
public class GraphASTScript extends GhidraScript {
private Function func;
protected HighFunction high;
@Override
public void run() throws Exception {
PluginTool tool = state.getTool();
if (tool == null) {
println("Script is not running in GUI");
}
GraphDisplayBroker graphDisplayBroker = tool.getService(GraphDisplayBroker.class);
if (graphDisplayBroker == null) {
Msg.showError(this, tool.getToolFrame(), "GraphAST Error",
"No graph display providers found: Please add a graph display provider to your tool");
return;
}
func = this.getFunctionContaining(this.currentAddress);
if (func == null) {
Msg.showWarn(this, state.getTool().getToolFrame(), "GraphAST Error",
"No Function at current location");
return;
}
buildAST();
PCodeDfgGraphTask astGraphTask = createTask(graphDisplayBroker);
astGraphTask.monitoredRun(monitor);
}
protected PCodeDfgGraphTask createTask(GraphDisplayBroker graphDisplayBroker) {
return new PCodeDfgGraphTask(state.getTool(), graphDisplayBroker, high);
}
private void buildAST() throws DecompileException {
DecompileOptions options = new DecompileOptions();
DecompInterface ifc = new DecompInterface();
ifc.setOptions(options);
if (!ifc.openProgram(this.currentProgram)) {
throw new DecompileException("Decompiler",
"Unable to initialize: " + ifc.getLastMessage());
}
ifc.setSimplificationStyle("normalize");
DecompileResults res = ifc.decompileFunction(func, 30, null);
high = res.getHighFunction();
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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.
*/
//Decompile the function at the cursor, then build data flow graph for the current address
//@category PCode
import ghidra.app.plugin.core.decompile.actions.PCodeDfgGraphTask;
import ghidra.app.plugin.core.decompile.actions.SelectedPCodeDfgGraphTask;
import ghidra.app.services.GraphDisplayBroker;
public class GraphSelectedASTScript extends GraphASTScript {
protected PCodeDfgGraphTask createTask(GraphDisplayBroker graphDisplayBroker) {
return new SelectedPCodeDfgGraphTask(state.getTool(), graphDisplayBroker, high,
currentAddress);
}
}

View file

@ -3872,14 +3872,26 @@
</para>
</sect2>
<sect2 id="ToolBarGraph">
<title>Graph AST Control Flow</title>
<sect2 id="DataFlowGraph">
<title>Graph Data Flow</title>
<para>
This action is located in the drop-down menu on the right side of the Decompiler
window tool/title bar.
</para>
<para>
Generate a control-flow graph based upon the results in the active Decompiler window,
Generate a data flow graph based upon the results in the active Decompiler window,
and render it using the current Graph Service.
</para>
</sect2>
<sect2 id="ControlFlowGraph">
<title>Graph Control Flow</title>
<para>
This action is located in the drop-down menu on the right side of the Decompiler
window tool/title bar.
</para>
<para>
Generate a control flow graph based upon the results in the active Decompiler window,
and render it using the current Graph Service.
</para>
</sect2>

View file

@ -342,16 +342,28 @@
<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ToolBarGraph"></a>Graph AST Control Flow</h3></div></div></div>
<a name="DataFlowGraph"></a>Graph Data Flow</h3></div></div></div>
<p>
This action is located in the drop-down menu on the right side of the Decompiler
window tool/title bar.
</p>
<p>
Generate a control-flow graph based upon the results in the active Decompiler window,
Generate an data flow graph based upon the results in the active Decompiler window,
and render it using the current Graph Service.
</p>
<div class="titlepage"><div><div><h3 class="title">
<a name="ControlFlowGraph"></a>Graph Control Flow</h3></div></div></div>
</div>
<p>
This action is located in the drop-down menu on the right side of the Decompiler
window tool/title bar.
</p>
<p>
Generate a control flow graph based upon the results in the active Decompiler window,
and render it using the current Graph Service.
</p>
</div>
</div>

View file

@ -63,7 +63,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
private static final ImageIcon C_SOURCE_ICON =
ResourceManager.loadImage("images/decompileFunction.gif");
private DockingAction graphASTControlFlowAction;
private DockingAction pcodeGraphAction;
private DockingAction astGraphAction;
private final DecompilePlugin plugin;
private ClipboardService clipboardService;
@ -1023,21 +1024,26 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
}
private void graphServiceRemoved() {
if (graphASTControlFlowAction == null) {
if (pcodeGraphAction == null) {
return;
}
if (tool.getService(GraphDisplayBroker.class) == null) {
tool.removeAction(graphASTControlFlowAction);
graphASTControlFlowAction.dispose();
graphASTControlFlowAction = null;
tool.removeAction(pcodeGraphAction);
tool.removeAction(astGraphAction);
astGraphAction.dispose();
pcodeGraphAction.dispose();
pcodeGraphAction = null;
astGraphAction = null;
}
}
private void graphServiceAdded() {
GraphDisplayBroker service = tool.getService(GraphDisplayBroker.class);
if (service != null && service.getDefaultGraphDisplayProvider() != null) {
graphASTControlFlowAction = new GraphASTControlFlowAction();
addLocalAction(graphASTControlFlowAction);
pcodeGraphAction = new PCodeCfgAction();
addLocalAction(pcodeGraphAction);
astGraphAction = new PCodeDfgAction();
addLocalAction(astGraphAction);
}
}

View file

@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.decompile.actions;
import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.AstGraphSubType.*;
import static ghidra.app.plugin.core.decompile.actions.PCodeCfgGraphTask.PcodeGraphSubType.*;
import docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
@ -28,12 +28,13 @@ import ghidra.program.model.pcode.HighFunction;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.task.TaskLauncher;
public class GraphASTControlFlowAction extends AbstractDecompilerAction {
public GraphASTControlFlowAction() {
super("Graph AST Control Flow");
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ToolBarGraph"));
setMenuBarData(new MenuData(new String[] { "Graph AST Control Flow" }, "graph"));
public class PCodeCfgAction extends AbstractDecompilerAction {
public PCodeCfgAction() {
super("Graph PCode Control Flow");
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ControlFlowGraph"));
setMenuBarData(new MenuData(new String[] { "Graph Control Flow" }, "graph"));
}
@Override
@ -57,8 +58,9 @@ public class GraphASTControlFlowAction extends AbstractDecompilerAction {
int codeLimitPerBlock = options.getInt("Max Code Lines Displayed", 10);
HighFunction highFunction = context.getHighFunction();
Address locationAddr = context.getLocation().getAddress();
ASTGraphTask task = new ASTGraphTask(service, !reuseGraph, codeLimitPerBlock, locationAddr,
highFunction, CONTROL_FLOW_GRAPH, tool);
PCodeCfgGraphTask task =
new PCodeCfgGraphTask(tool, service, !reuseGraph, codeLimitPerBlock,
locationAddr, highFunction, CONTROL_FLOW_GRAPH);
new TaskLauncher(task, tool.getToolFrame());
}

View file

@ -15,11 +15,11 @@
*/
package ghidra.app.plugin.core.decompile.actions;
import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.AstGraphSubType.*;
import static ghidra.app.plugin.core.decompile.actions.PCodeCfgGraphTask.PcodeGraphSubType.*;
import java.util.*;
import ghidra.app.plugin.core.decompile.actions.ASTGraphTask.AstGraphSubType;
import ghidra.app.plugin.core.decompile.actions.PCodeCfgGraphTask.PcodeGraphSubType;
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
@ -31,12 +31,12 @@ import ghidra.util.exception.AssertException;
/**
* Listener for when an AST graph's nodes are selected.
*/
public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
public class PCodeCfgDisplayListener extends AddressBasedGraphDisplayListener {
private HighFunction hfunction;
private AstGraphSubType graphType;
private PcodeGraphSubType graphType;
ASTGraphDisplayListener(PluginTool tool, GraphDisplay display, HighFunction hfunction,
AstGraphSubType graphType) {
PCodeCfgDisplayListener(PluginTool tool, GraphDisplay display, HighFunction hfunction,
PcodeGraphSubType graphType) {
super(tool, hfunction.getFunction().getProgram(), display);
this.hfunction = hfunction;
this.graphType = graphType;
@ -117,7 +117,7 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
@Override
public GraphDisplayListener cloneWith(GraphDisplay display) {
return new ASTGraphDisplayListener(tool, graphDisplay, hfunction, graphType);
return new PCodeCfgDisplayListener(tool, graphDisplay, hfunction, graphType);
}
}

View file

@ -36,12 +36,16 @@ import ghidra.util.exception.GraphException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
public class ASTGraphTask extends Task {
enum AstGraphSubType {
/**
* Task to create a PCode control flow graph based on decompiler output
*/
public class PCodeCfgGraphTask extends Task {
enum PcodeGraphSubType {
CONTROL_FLOW_GRAPH("AST Control Flow"), DATA_FLOW_GRAPH("AST Data Flow");
private String name;
AstGraphSubType(String name) {
PcodeGraphSubType(String name) {
this.name = name;
}
@ -57,13 +61,14 @@ public class ASTGraphTask extends Task {
private int codeLimitPerBlock;
private Address location;
private HighFunction hfunction;
private AstGraphSubType astGraphType;
private PcodeGraphSubType pcodeGraphType;
private int uniqueNum = 0;
private PluginTool tool;
public ASTGraphTask(GraphDisplayBroker graphService, boolean newGraph, int codeLimitPerBlock,
Address location, HighFunction hfunction, AstGraphSubType graphType, PluginTool tool) {
public PCodeCfgGraphTask(PluginTool tool, GraphDisplayBroker graphService, boolean newGraph,
int codeLimitPerBlock, Address location, HighFunction hfunction,
PcodeGraphSubType graphType) {
super("Graph " + graphType.getName(), true, false, true);
this.graphService = graphService;
@ -71,19 +76,19 @@ public class ASTGraphTask extends Task {
this.codeLimitPerBlock = codeLimitPerBlock;
this.location = location;
this.hfunction = hfunction;
this.astGraphType = graphType;
this.pcodeGraphType = graphType;
this.tool = tool;
}
@Override
public void run(TaskMonitor monitor) {
GraphType graphType = new AstGraphType();
GraphType graphType = new PCodeCfgGraphType();
// get a new graph
AttributedGraph graph = new AttributedGraph(astGraphType.getName(), graphType);
AttributedGraph graph = new AttributedGraph("PCode Graph", graphType);
try {
monitor.setMessage("Computing Graph...");
if (astGraphType == AstGraphSubType.DATA_FLOW_GRAPH) {
if (pcodeGraphType == PcodeGraphSubType.DATA_FLOW_GRAPH) {
createDataFlowGraph(graph, monitor);
}
else {
@ -93,8 +98,8 @@ public class ASTGraphTask extends Task {
GraphDisplay display =
graphService.getDefaultGraphDisplay(!newGraph, monitor);
ASTGraphDisplayListener displayListener =
new ASTGraphDisplayListener(tool, display, hfunction, astGraphType);
PCodeCfgDisplayListener displayListener =
new PCodeCfgDisplayListener(tool, display, hfunction, pcodeGraphType);
display.setGraphDisplayListener(displayListener);
monitor.setMessage("Obtaining handle to graph provider...");
@ -106,10 +111,11 @@ public class ASTGraphTask extends Task {
monitor.setMessage("Rendering Graph...");
String description =
astGraphType == AstGraphSubType.DATA_FLOW_GRAPH ? "AST Data Flow" : "AST Control Flow";
pcodeGraphType == PcodeGraphSubType.DATA_FLOW_GRAPH ? "AST Data Flow"
: "AST Control Flow";
description = description + " for " + hfunction.getFunction().getName();
GraphDisplayOptions graphDisplayOptions =
new ProgramGraphDisplayOptions(new AstGraphType(), tool);
new ProgramGraphDisplayOptions(new PCodeCfgGraphType(), tool);
graphDisplayOptions.setVertexLabelOverrideAttributeKey(CODE_ATTRIBUTE);
display.setGraph(graph, graphDisplayOptions, description, false, monitor);
setGraphLocation(display, displayListener);
@ -124,7 +130,7 @@ public class ASTGraphTask extends Task {
}
private void setGraphLocation(GraphDisplay display, ASTGraphDisplayListener displayListener) {
private void setGraphLocation(GraphDisplay display, PCodeCfgDisplayListener displayListener) {
if (location == null) {
return;
}

View file

@ -17,10 +17,10 @@ package ghidra.app.plugin.core.decompile.actions;
import ghidra.graph.ProgramGraphType;
public class AstGraphType extends ProgramGraphType {
public class PCodeCfgGraphType extends ProgramGraphType {
protected AstGraphType() {
super("AST", "Graph to show pcode for function");
protected PCodeCfgGraphType() {
super("Pcode", "Graph to show pcode for function");
}
}

View file

@ -13,16 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Decompile the function at the cursor, then build data-flow graph (AST) with flow edges
//@category PCode
package ghidra.app.plugin.core.decompile.actions;
import java.util.*;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.pcode.*;
import ghidra.service.graph.AttributedEdge;
import ghidra.service.graph.AttributedVertex;
public class GraphASTAndFlow extends GraphAST {
/**
* Task to create a combined PCode control flow and data flow graph based on decompiler output
*/
public class PCodeCombinedGraphTask extends PCodeDfgGraphTask {
public PCodeCombinedGraphTask(PluginTool tool, GraphDisplayBroker graphService,
HighFunction hfunction) {
super(tool, graphService, hfunction);
}
@Override
protected void buildGraph() {
@ -77,7 +86,7 @@ public class GraphASTAndFlow extends GraphAST {
}
if (prev != null && map.containsKey(prev) && map.containsKey(next)) {
AttributedEdge edge = createEdge(map.get(prev), map.get(next));
edge.setEdgeType(WITHIN_BLOCK);
edge.setEdgeType(PCodeDfgGraphType.WITHIN_BLOCK);
}
prev = next;
}
@ -92,7 +101,7 @@ public class GraphASTAndFlow extends GraphAST {
PcodeBlock in = block.getIn(i);
if (last.containsKey(in)) {
AttributedEdge edge = createEdge(last.get(in), first.get(block));
edge.setEdgeType(BETWEEN_BLOCK);
edge.setEdgeType(PCodeDfgGraphType.BETWEEN_BLOCKS);
}
}
}

View file

@ -0,0 +1,60 @@
/* ###
* 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.plugin.core.decompile.actions;
import docking.action.MenuData;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.pcode.HighFunction;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.task.TaskLauncher;
/**
* Action to create a PCode control data graph based on decompiler output
*/
public class PCodeDfgAction extends AbstractDecompilerAction {
public PCodeDfgAction() {
super("Graph PCode Data Flow");
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "DataFlowGraph"));
setMenuBarData(new MenuData(new String[] { "Graph Data Flow" }, "graph"));
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return context.getFunction() != null;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
PluginTool tool = context.getTool();
GraphDisplayBroker service = tool.getService(GraphDisplayBroker.class);
if (service == null) {
Msg.showError(this, tool.getToolFrame(), "AST Graph Failed",
"Graph Display Broker service not found!\n" +
"Please add a Graph Display Broker service");
return;
}
HighFunction highFunction = context.getHighFunction();
PCodeDfgGraphTask task = new PCodeDfgGraphTask(tool, service, highFunction);
new TaskLauncher(task, tool.getToolFrame());
}
}

View file

@ -0,0 +1,78 @@
/* ###
* 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.plugin.core.decompile.actions;
import java.util.Collections;
import java.util.Set;
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.service.graph.*;
/**
* GraphDisplayListener for a PCode data flow graph
*/
class PCodeDfgDisplayListener extends AddressBasedGraphDisplayListener {
HighFunction highfunc;
public PCodeDfgDisplayListener(PluginTool tool, GraphDisplay display, HighFunction high,
Program program) {
super(tool, program, display);
highfunc = high;
}
@Override
protected Set<AttributedVertex> getVertices(AddressSetView selection) {
return Collections.emptySet();
}
@Override
protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
AddressSet set = new AddressSet();
for (AttributedVertex vertex : vertices) {
Address address = getAddress(vertex);
if (address != null) {
set.add(address);
}
}
return set;
}
@Override
protected Address getAddress(AttributedVertex vertex) {
if (vertex == null) {
return null;
}
String vertexId = vertex.getId();
int firstColon = vertexId.indexOf(':');
if (firstColon == -1) {
return null;
}
int firstSpace = vertexId.indexOf(' ');
String addrString = vertexId.substring(0, firstSpace);
return getAddress(addrString);
}
@Override
public GraphDisplayListener cloneWith(GraphDisplay display) {
return new PCodeDfgDisplayListener(tool, display, highfunc, program);
}
}

View file

@ -0,0 +1,65 @@
/* ###
* 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.plugin.core.decompile.actions;
import static ghidra.app.plugin.core.decompile.actions.PCodeDfgGraphType.*;
import static ghidra.service.graph.VertexShape.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.service.graph.*;
import ghidra.util.WebColors;
/**
* {@link GraphDisplayOptions} for {@link PCodeDfgGraphType}
*/
public class PCodeDfgDisplayOptions extends GraphDisplayOptions {
public static final String SHAPE_ATTRIBUTE = "Shape";
/**
* constructor
* @param tool if non-null, will load values from tool options
*/
public PCodeDfgDisplayOptions(PluginTool tool) {
super(new PCodeDfgGraphType(), tool);
}
@Override
protected void initializeDefaults() {
setDefaultVertexShape(ELLIPSE);
setDefaultVertexColor(WebColors.RED);
setDefaultEdgeColor(WebColors.NAVY);
setVertexSelectionColor(WebColors.DEEP_PINK);
setEdgeSelectionColor(WebColors.DEEP_PINK);
setDefaultLayoutAlgorithmName(LayoutAlgorithmNames.MIN_CROSS_COFFMAN_GRAHAM);
setUsesIcons(false);
setArrowLength(15);
setLabelPosition(GraphLabelPosition.SOUTH);
setVertexShapeOverrideAttributeKey(SHAPE_ATTRIBUTE);
setMaxNodeCount(1000);
configureVertexType(DEFAULT_VERTEX, VertexShape.ELLIPSE, WebColors.RED);
configureVertexType(CONSTANT, VertexShape.ELLIPSE, WebColors.DARK_GREEN);
configureVertexType(REGISTER, VertexShape.ELLIPSE, WebColors.NAVY);
configureVertexType(UNIQUE, VertexShape.ELLIPSE, WebColors.BLACK);
configureVertexType(PERSISTENT, VertexShape.ELLIPSE, WebColors.DARK_ORANGE);
configureVertexType(ADDRESS_TIED, VertexShape.ELLIPSE, WebColors.ORANGE);
configureVertexType(OP, VertexShape.ELLIPSE, WebColors.RED);
configureEdgeType(DEFAULT_EDGE, WebColors.BLUE);
configureEdgeType(WITHIN_BLOCK, WebColors.BLACK);
configureEdgeType(BETWEEN_BLOCKS, WebColors.RED);
}
}

View file

@ -0,0 +1,220 @@
/* ###
* 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.plugin.core.decompile.actions;
import static ghidra.app.plugin.core.decompile.actions.PCodeDfgDisplayOptions.*;
import java.util.*;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.pcode.*;
import ghidra.service.graph.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.GraphException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
/**
* Task for creating PCode data flow graphs from decompiler output
*/
public class PCodeDfgGraphTask extends Task {
private GraphDisplayBroker graphService;
protected HighFunction hfunction;
private AttributedGraph graph;
private PluginTool tool;
public PCodeDfgGraphTask(PluginTool tool, GraphDisplayBroker graphService,
HighFunction hfunction) {
super("Graph AST", true, false, true);
this.graphService = graphService;
this.hfunction = hfunction;
this.tool = tool;
}
@Override
public void run(TaskMonitor monitor) {
try {
buildAndDisplayGraph(monitor);
}
catch (GraphException e) {
Msg.showError(this, null, "Graph Error",
"Can't create graph display: " + e.getMessage(), e);
}
catch (CancelledException e1) {
// do nothing
}
}
private void buildAndDisplayGraph(TaskMonitor monitor)
throws GraphException, CancelledException {
GraphType graphType = new PCodeDfgGraphType();
Function func = hfunction.getFunction();
graph = new AttributedGraph("Data Flow Graph", graphType);
buildGraph();
GraphDisplay graphDisplay = graphService.getDefaultGraphDisplay(false, monitor);
GraphDisplayOptions displayOptions = new PCodeDfgDisplayOptions(tool);
String description = "AST Data Flow Graph For " + func.getName();
graphDisplay.setGraph(graph, displayOptions, description, false, monitor);
// Install a handler so the selection/location will map
graphDisplay.setGraphDisplayListener(new PCodeDfgDisplayListener(tool, graphDisplay,
hfunction, func.getProgram()));
}
protected void buildGraph() {
HashMap<Integer, AttributedVertex> vertices = new HashMap<>();
Iterator<PcodeOpAST> opiter = getPcodeOpIterator();
while (opiter.hasNext()) {
PcodeOpAST op = opiter.next();
AttributedVertex o = createOpVertex(op);
for (int i = 0; i < op.getNumInputs(); ++i) {
int opcode = op.getOpcode();
if ((i == 0) && ((opcode == PcodeOp.LOAD) || (opcode == PcodeOp.STORE))) {
continue;
}
if ((i == 1) && (opcode == PcodeOp.INDIRECT)) {
continue;
}
VarnodeAST vn = (VarnodeAST) op.getInput(i);
if (vn != null) {
AttributedVertex v = getVarnodeVertex(vertices, vn);
createEdge(v, o);
}
}
VarnodeAST outvn = (VarnodeAST) op.getOutput();
if (outvn != null) {
AttributedVertex outv = getVarnodeVertex(vertices, outvn);
if (outv != null) {
createEdge(o, outv);
}
}
}
}
private String getVarnodeKey(VarnodeAST vn) {
PcodeOp op = vn.getDef();
String id;
if (op != null) {
id = op.getSeqnum().getTarget().toString(true) + " v " +
Integer.toString(vn.getUniqueId());
}
else {
id = "i v " + Integer.toString(vn.getUniqueId());
}
return id;
}
protected AttributedVertex createVarnodeVertex(VarnodeAST vn) {
String name = vn.getAddress().toString(true);
String id = getVarnodeKey(vn);
String vertexType = PCodeDfgGraphType.DEFAULT_VERTEX;
if (vn.isConstant()) {
vertexType = PCodeDfgGraphType.CONSTANT;
}
else if (vn.isRegister()) {
vertexType = PCodeDfgGraphType.REGISTER;
Register reg =
hfunction.getFunction().getProgram().getRegister(vn.getAddress(), vn.getSize());
if (reg != null) {
name = reg.getName();
}
}
else if (vn.isUnique()) {
vertexType = PCodeDfgGraphType.UNIQUE;
}
else if (vn.isPersistent()) {
vertexType = PCodeDfgGraphType.PERSISTENT;
}
else if (vn.isAddrTied()) {
vertexType = PCodeDfgGraphType.ADDRESS_TIED;
}
AttributedVertex vert = graph.addVertex(id, name);
vert.setVertexType(vertexType);
// if it is an input override the shape to be a triangle
if (vn.isInput()) {
vert.setAttribute(SHAPE_ATTRIBUTE, VertexShape.TRIANGLE_DOWN.getName());
}
return vert;
}
protected AttributedVertex getVarnodeVertex(Map<Integer, AttributedVertex> vertices,
VarnodeAST vn) {
AttributedVertex res;
res = vertices.get(vn.getUniqueId());
if (res == null) {
res = createVarnodeVertex(vn);
vertices.put(vn.getUniqueId(), res);
}
return res;
}
protected AttributedEdge createEdge(AttributedVertex in, AttributedVertex out) {
AttributedEdge newEdge = graph.addEdge(in, out);
newEdge.setEdgeType(PCodeDfgGraphType.DEFAULT_EDGE);
return newEdge;
}
protected AttributedVertex createOpVertex(PcodeOpAST op) {
String name = op.getMnemonic();
String id = getOpKey(op);
int opcode = op.getOpcode();
if ((opcode == PcodeOp.LOAD) || (opcode == PcodeOp.STORE)) {
Varnode vn = op.getInput(0);
AddressSpace addrspace =
hfunction.getFunction()
.getProgram()
.getAddressFactory()
.getAddressSpace((int) vn.getOffset());
name += ' ' + addrspace.getName();
}
else if (opcode == PcodeOp.INDIRECT) {
Varnode vn = op.getInput(1);
if (vn != null) {
PcodeOp indOp = hfunction.getOpRef((int) vn.getOffset());
if (indOp != null) {
name += " (" + indOp.getMnemonic() + ')';
}
}
}
AttributedVertex vert = graph.addVertex(id, name);
vert.setVertexType(PCodeDfgGraphType.OP);
return vert;
}
protected Iterator<PcodeOpAST> getPcodeOpIterator() {
Iterator<PcodeOpAST> opiter = hfunction.getPcodeOps();
return opiter;
}
private String getOpKey(PcodeOpAST op) {
SequenceNumber sq = op.getSeqnum();
String id =
sq.getTarget().toString(true) + " o " + Integer.toString(op.getSeqnum().getTime());
return id;
}
}

View file

@ -0,0 +1,59 @@
/* ###
* 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.plugin.core.decompile.actions;
import java.util.ArrayList;
import java.util.List;
import ghidra.service.graph.GraphType;
/**
* GraphType for a PCode data flow graph
*/
public class PCodeDfgGraphType extends GraphType {
private static List<String> vertexTypes = new ArrayList<>();
private static List<String> edgeTypes = new ArrayList<>();
// Vertex Types
public static final String DEFAULT_VERTEX = vertex("Default");
public static final String CONSTANT = vertex("Constant");
public static final String REGISTER = vertex("Register");
public static final String UNIQUE = vertex("Unique");
public static final String PERSISTENT = vertex("Persistent");
public static final String ADDRESS_TIED = vertex("Address Tied");
public static final String OP = vertex("Op");
// Edge Types
public static final String DEFAULT_EDGE = edge("Default");
public static final String WITHIN_BLOCK = edge("Within Block");
public static final String BETWEEN_BLOCKS = edge("Between Blocks");
public PCodeDfgGraphType() {
super("AST Graph", "Displays an AST graph for the current function", vertexTypes,
edgeTypes);
}
private static String edge(String edgeType) {
edgeTypes.add(edgeType);
return edgeType;
}
private static String vertex(String vertexType) {
vertexTypes.add(vertexType);
return vertexType;
}
}

View file

@ -0,0 +1,43 @@
/* ###
* 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.plugin.core.decompile.actions;
import java.util.Iterator;
import ghidra.app.services.GraphDisplayBroker;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeOpAST;
/**
* Task for creating a PCode data flow graph from a selected address
*/
public class SelectedPCodeDfgGraphTask extends PCodeDfgGraphTask {
private Address address;
public SelectedPCodeDfgGraphTask(PluginTool tool, GraphDisplayBroker graphService,
HighFunction hfunction, Address address) {
super(tool, graphService, hfunction);
this.address = address;
}
protected Iterator<PcodeOpAST> getPcodeOpIterator() {
Iterator<PcodeOpAST> opiter = hfunction.getPcodeOps(address);
return opiter;
}
}