mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
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:
parent
073c726885
commit
467133de3e
18 changed files with 734 additions and 376 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue