6 Commits
v1.1 ... v1.2

Author SHA1 Message Date
evm
3953e9d357 tweak Readme 2024-09-25 15:15:44 -04:00
evm
046f4706d2 Modernize DeepCut script's torch usage, update README 2024-09-25 15:07:47 -04:00
evm-sec
f2901905ea Merge pull request #3 from AndreQuimper/dev
fixed to use eventType enum class and addition of graphing module
2024-09-10 11:13:00 -04:00
AndreQuimper
bec42aa376 addition of graphcut module
Addition of GraphCut module allows graphing of Namespaces and their relationships. Also allows for filtering through the CodeCut table.
2024-06-24 15:26:59 -04:00
AndreQuimper
9400cc6c69 updated to use of eventType enum
Since eventType objects changed from constant integers to enum classes we can no longer do switch statements with them (their IDs are not constant).
2024-05-29 14:35:26 -04:00
evm
44dbcdce55 Final minor tweaks for CodeCut 1.1 release 2023-11-22 15:37:45 -05:00
35 changed files with 4771 additions and 128 deletions

View File

@@ -1,12 +1,14 @@
# CodeCut Plugin for Ghidra
Ghidra Plugin for DeepCut / CodeCut GUI
## Theory of Operation
CodeCut allows a user to assign functions to object files in Ghidra, and then interact with the binary at the object file level. Functions are assigned to object files by setting the `Namespace` field in the Ghidra database. DeepCut attempts to establish initial object file boundaries which the user can then adjust using the CodeCut Table window.
CodeCut allows a user to assign functions to object files in Ghidra, and then interact with the binary at the object file level. Functions are assigned to object files by setting the `Namespace` field in the Ghidra database. DeepCut attempts to establish initial object file boundaries which the user can then adjust using the CodeCut Table window.
## Why Is This Useful?
Most binaries originate from multiple source code files. When reversing, RE analysts often develop a sense of which functions are related, and therefore likely belonged to the same source file. CodeCut allows the analyst to develop a hypothesis like "I think this block of functions are OS functions." Now if there are unexplored functions in that region, they will show up in disassembly and decompilation as OS::FUN_XXXXXXXX instead of just FUN_XXXXXXXX. This leads to a more rapid understanding of what functions might be used for when viewed in context.
## Plugin Installation
Follow normal Ghidra extension installation procedures. Copy the CodeCut and DeepCut extension zip into `$GHIDRA_INSTALL_DIR/Extensions` then in the main Ghidra window selection **File -> Install Extensions** and select the CodeCut and DeepCut boxes. Ghidra will tell you it needs to restart.
Follow normal Ghidra extension installation procedures. Copy the CodeCut and DeepCut extension zip into `$GHIDRA_INSTALL_DIR/Extensions` then in the main Ghidra window selection **File -> Install Extensions** and select the CodeCut and DeepCut boxes. Ghidra will tell you it needs to restart. Now that Ghidra only requires plugins to match the base version's major/minor and not patch versions, we expect to only release even major/minor versions.
**NOTE:** After restarting and loading a CodeBrowser window, Ghidra will tell you it has found new plugins and ask if you want to configure them. Only CodeCut shows up in this window. This is because DeepCut is a "one-shot" analyzer (it is still installed).
@@ -18,25 +20,20 @@ CodeCut:
- nltk
DeepCut:
- torch 1.7.1
- torch-geometric 1.6.3
- torch-cluster 1.5.8
- torch-sparse 0.6.8
- torch-scatter 2.0.5
- torch-spline-conv 1.2.0
- torch
- torch-geometric
To install dependencies run:
```
pip3 install nltk
pip3 install torch==1.7.1+cpu torch-geometric==1.6.3 torch-cluster==1.5.8 torch-spare==0.6.8 torch-scatter==2.0.5 torch-spline-conv==1.2.0
pip3 install torch torch-geometric
```
(assuming that pip3 points to the version of Python you plan to use below)
### Configuring CodeCut Python Path
![](img/codecut-config.png)
Configure the native Python path for CodeCut by choosing **Edit -> Tool Options** and selecting "Python Executable."
### Configuring DeepCut Python Path
@@ -50,7 +47,40 @@ DeepCut is best run as a one-shot analyzer *after* initial auto-analysis. Selec
## Using CodeCut
![](img/codecut-run.png)
After DeepCut runs, you can interact at an object file level with the **CodeCut Table** view. Select **Window -> CodeCut Table** You can have CodeCut guess the module names (based on string references) by choosing **Analysis -> Guess Module Names** in the CodeCut Table window. You can split/combine object files by right clicking on an object and choosing "Split Namespace Here" / "Combine Namespaces." You can move functions between object files (changing the boundaries of the object files) by dragging and dropping them.
After DeepCut runs, you can interact at an object file level with the **CodeCut Table** view. Select **Window -> CodeCut Table** to display the table. The CodeCut table is essentially a combined set of symbol tables, one for each module.
You can have CodeCut guess the module names (based on string references) by choosing **Analysis -> Guess Module Names** in the CodeCut Table window. This is helpful if your target executable has a lot of debug strings. The name guessing script attempts to find source file names first, then falls back to strings (including bigrams and trigrams) that are repeated multiple times.
You can split/combine object files by right clicking on an object and choosing **Split Namespace Here** / **Combine Namespaces.** You can move functions between object files (changing the boundaries of the object files) by dragging and dropping them.
## Exporting to Recompilable C
CodeCut contains two options for exporting a full module or a subrange of a module as recompilable C under the **Export** menu when you right click on a module in the CodeCut table. This feature is marked EXPERIMENTAL and is not well-tested.
## The CodeCut Graph
![](img/graph-pic.png)
CodeCut now has a graph option for viewing interactions / hierarchy of the modules within a binary. Add namespaces to the graph view by right clicking a function in the CodeCut Table and choosing **Add Namespace to Graph**. You can remove modules from the graph by right clicking and choosing **Show Namespaces in Graph Filter**.
## Limitations
### Careful Where You Click
While it looks like you can right click on the module names on the right side, CodeCut is actually paying attention to which function is highlighted in the table. So e.g. if you have a function in object3 highlighted and you right click on the grey object2 box to the left and click Combine/Split or Rename Namespaces, CodeCut will perform the operation from the function you have highlighted in object3.
### Address Range Peculiarities
CodeCut attempts to order the namespaces by their starting address. CodeCut may appear to list the modules out of order, however this is usually due to disassembly irregularities from Ghidra. E.g. let's say we have a memory map that looks like:
```
object2: 0x00010000 - 0x00018000
object3: 0x00018004 - 0x00020000
...
object50: 0x00480000 - 0x00480a00
```
Say the function at 0x00480000 makes a branch or jump to an address 0x00016008. This should be a new function entry point, but if only one function uses it, Ghidra might not label this as a function entry point, and just consider it part of FUN_00480000. This means that object50's actual range is 0x00016008 - 0x00480a00. And so object50 will show up in between object2 and object3 in the table. CodeCut outputs the module ranges into Ghidra's application log for help with debugging this. In this case defining 0x00016008 as a function will cause object50's bounds to be the right values and object50 will show up at the right place in the table.
### Defining Functions Later
Functions that get defined after DeepCut analysis has been run are by default added to the Global namespace. In the future we plan to add a feature that automatically adds them to the nearest namespace, or give a mechanism to more easily assign single functions directly to a specific namespace.
## Building
Specific build instructions are provided in the DeepCut and CodeCut subfolders.

View File

@@ -10,7 +10,7 @@
<META name="ProgId" content="FrontPage.Editor.Document">
<TITLE>Skeleton Help File for a Module</TITLE>
<LINK rel="stylesheet" type="text/css" href="../../shared/Frontpage.css">
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
</HEAD>
<BODY>

View File

@@ -0,0 +1,215 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
package codecutguiv2;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Set;
import java.util.Vector;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import docking.ActionContext;
import docking.WindowPosition;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.services.BlockModelService;
import ghidra.feature.vt.api.db.TableColumn;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.HelpLocation;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableCellRenderer;
import ghidra.util.table.GhidraThreadedTablePanel;
import graphcut.GraphCutProvider;
import resources.ResourceManager;
public class ChecklistProvider extends ComponentProviderAdapter implements ActionListener {
private static final ImageIcon ICON = ResourceManager.loadImage("images/textfield.png");
private static final String BUTTON_STRING = "Apply Changes";
private static final String RESET_STRING = "Reset Graph Filter";
private CodeCutGUIPlugin plugin;
private GraphCutProvider graphProvider;
GhidraTable table;
DefaultTableModel model;
private JPanel boxPanel;
ChecklistProvider(CodeCutGUIPlugin plugin){
super(plugin.getTool(), "Namespaces in Graph", plugin.getName(), ProgramActionContext.class);
this.plugin = plugin;
this.graphProvider = plugin.getGraphProvider();
setIcon(ICON);
addToToolbar();
setHelpLocation(new HelpLocation(plugin.getName(), "CodeCut_Table"));
setWindowGroup("codecutTable");
setIntraGroupPosition(WindowPosition.BOTTOM);
boxPanel = new JPanel();
boxPanel.setLayout(new BoxLayout(boxPanel, BoxLayout.Y_AXIS));
Object[] columnNames = {"Namespace", "Added to Graph"};
Object[][] data = {};
model = new DefaultTableModel(data, columnNames) {
@Override
public boolean isCellEditable(int row, int column) {
if(column == 1) {
return true;
}
return false;
}
};
table = new GhidraTable(model) {
@Override
public Class getColumnClass(int column) {
switch(column) {
case 0:
return Namespace.class;
case 1:
return Boolean.class;
default:
return String.class;
}
}
};
table.getTableHeader().setReorderingAllowed(false);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
table.setDefaultRenderer(Namespace.class, new DefaultTableCellRenderer() {
@Override
public void setValue(Object value) {
setText(((Namespace) value).getName());
}
});
JScrollPane scrollPane = new JScrollPane(table);
boxPanel.add(scrollPane);
boxPanel.setSize(boxPanel.getPreferredSize());
JPanel buttonPane = new JPanel();
JButton applyButton = new JButton(BUTTON_STRING);
applyButton.setVerticalTextPosition(AbstractButton.CENTER);
applyButton.setActionCommand(BUTTON_STRING);
applyButton.addActionListener(this);
JButton resetButton = new JButton(RESET_STRING);
resetButton.setVerticalTextPosition(AbstractButton.CENTER);
resetButton.setActionCommand(RESET_STRING);
resetButton.addActionListener(this);
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
buttonPane.add(Box.createHorizontalGlue());
buttonPane.add(applyButton);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
buttonPane.add(resetButton);
boxPanel.add(buttonPane);
setIntraGroupPosition(WindowPosition.RIGHT);
buildTable();
}
void buildTable() {
model.setRowCount(0);
Set<Namespace>inGraph = graphProvider.getNamespacesInFilter();
for(Namespace ns: inGraph) {
model.addRow(new Object[]{ns, true});
}
}
void dispose() {
plugin = null;
}
@Override
public void actionPerformed(ActionEvent e) {
if(BUTTON_STRING.equals(e.getActionCommand())) {
Vector<Vector> data = model.getDataVector();
for(int i = 0; i < model.getRowCount(); i++) {
Namespace victim = (Namespace)data.elementAt(i).elementAt(0);
if(!(boolean)data.elementAt(i).elementAt(1)) {
graphProvider.removeFromWhitelist(victim);
}
}
}
else if(RESET_STRING.equals(e.getActionCommand())) {
graphProvider.resetWhitelist();
}
buildTable();
}
@Override
public JComponent getComponent() {
return boxPanel;
}
@Override
public ActionContext getActionContext(MouseEvent event) {
Program program = plugin.getProgram();
if(program == null) {
return null;
}
return new ProgramActionContext(this, program);
}
public void open() {
if (!isVisible()) {
setVisible(true);
}
}
}

View File

@@ -27,6 +27,7 @@
* under Contract Number N66001-20-C-4024.
*/
package codecutguiv2;
import java.awt.Cursor;
@@ -48,6 +49,7 @@ import docking.action.*;
import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import functioncalls.plugin.FcgProvider;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
@@ -55,6 +57,7 @@ import ghidra.app.decompiler.component.DecompilerController;
import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.decompile.DecompilerProvider;
import ghidra.app.plugin.core.symboltree.actions.*;
import ghidra.app.script.GhidraScript;
@@ -64,11 +67,14 @@ import ghidra.app.services.GoToService;
//import ghidra.app.services.DecExtendService;
import ghidra.app.util.SymbolInspector;
import ghidra.framework.model.*;
import ghidra.framework.options.Options;
import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.framework.preferences.Preferences;
import ghidra.graph.viewer.options.VisualGraphOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.AddressRangeImpl;
@@ -87,13 +93,18 @@ import ghidra.util.HTMLUtilities;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.Swing;
import ghidra.util.SystemUtilities;
import ghidra.util.bean.opteditor.OptionsVetoException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.table.GhidraTable;
import ghidra.util.task.SwingUpdateManager;
import graphcut.*;
import ghidra.util.task.*;
import ghidra.util.datastruct.*;
import resources.Icons;
import resources.ResourceManager;
import static ghidra.program.util.ProgramEvent.*;
/**
* Plugin to display the symbol table for a program.
@@ -109,20 +120,20 @@ import resources.ResourceManager;
" provides navigation to the symbols in the Code Browser, and " +
"allows symbols to be renamed and deleted. This plugin also " +
"shows references to a symbol. Filters can be set " +
"to show subsets of the symbols.",
"to show subsets of the symbols." +
" Allows the graphing of namespaces and their relations.",
servicesRequired = { GoToService.class, BlockModelService.class },
eventsProduced = { ProgramLocationPluginEvent.class },
eventsConsumed = { ProgramActivatedPluginEvent.class }
)
//@formatter:on
public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
public class CodeCutGUIPlugin extends ProgramPlugin implements DomainObjectListener, OptionsChangeListener{
final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
private final static String OPTION_NAME_PYTHON_EXEC = "Python Executable";
private final static String OPTION_DEFAULT_PYTHON_EXEC = "/projects/venv/bin/python3";
//private final static String OPTION_DEFAULT_PYTHON_EXEC = "/usr/local/bin/python3"; // for testing
private final static String OPTION_DEFAULT_PYTHON_EXEC = "/usr/bin/python3";
private String pythonExec = OPTION_DEFAULT_PYTHON_EXEC;
private DockingAction openRefsAction;
@@ -152,6 +163,20 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
private Map<Namespace, String> suggestedModuleNames;
//private DecExtendService decExtService;
//GraphCut Variables
public static final String GRAPH_NAME = "CodeCut Object Graph";
static final String SHOW_PROVIDER_ACTION_NAME = "Display CodeCut Object Graph";
public static final HelpLocation DEFAULT_HELP =
new HelpLocation(CodeCutGUIPlugin.class.getSimpleName(),
CodeCutGUIPlugin.class.getSimpleName());
private GraphCutProvider graphProvider;
private VisualGraphOptions vgOptions = new VisualGraphOptions();
private static final int MIN_UPDATE_DELAY = 750;
private SwingUpdateManager locationUpdater = new SwingUpdateManager(MIN_UPDATE_DELAY, () ->{
doLocationChanged();
});
private ChecklistProvider checklistProvider;
public CodeCutGUIPlugin(PluginTool tool) {
super(tool);
@@ -178,21 +203,29 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
createProvider = new CreateProvider(this);
combineProvider = new CombineProvider(this);
decompProvider = new DecompileRangeProvider(this);
graphProvider = new GraphCutProvider(tool, this);
checklistProvider = new ChecklistProvider(this);
createNamespaceActions();
createSymActions();
createRefActions();
createMapActions();
createExportActions();
createGraphActions();
initializeGraphOptions();
createChecklistActions();
//decExtService = tool.getService(DecExtendService.class);
//if (decExtService == null) {
// Msg.info(new Object(), "ERROR: Decompiler Extension is not installed");
//}
inspector = new SymbolInspector(getTool(), symProvider.getComponent());
}
/**
* Tells a plugin that it is no longer needed.
* The plugin should remove itself from anything that
@@ -248,20 +281,27 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
suggestedModuleNames.clear();
suggestedModuleNames = null;
}
graphProvider.dispose();
checklistProvider.dispose();
}
@Override
public void readConfigState(SaveState saveState) {
symProvider.readConfigState(saveState);
graphProvider.readConfigState(saveState);
}
@Override
public void writeConfigState(SaveState saveState) {
symProvider.writeConfigState(saveState);
graphProvider.writeConfigState(saveState);
}
@Override
public void processEvent(PluginEvent event) {
super.processEvent(event);
if (event instanceof ProgramActivatedPluginEvent) {
ProgramActivatedPluginEvent progEvent = (ProgramActivatedPluginEvent) event;
Program oldProg = currentProgram;
@@ -312,7 +352,7 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
for (int i = 0; i < eventCnt; ++i) {
DomainObjectChangeRecord doRecord = ev.getChangeRecord(i);
int eventType = doRecord.getEventType();
int eventType = doRecord.getEventType().getId();
if (!(doRecord instanceof ProgramChangeRecord)) {
continue;
}
@@ -320,105 +360,104 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
ProgramChangeRecord rec = (ProgramChangeRecord) doRecord;
Symbol symbol = null;
SymbolTable symbolTable = currentProgram.getSymbolTable();
switch (eventType) {
case ChangeManager.DOCR_CODE_ADDED:
case ChangeManager.DOCR_CODE_REMOVED:
if (rec.getNewValue() instanceof Data) {
symbol = symbolTable.getPrimarySymbol(rec.getStart());
if (symbol != null && symbol.isDynamic()) {
symProvider.symbolChanged(symbol);
refProvider.symbolChanged(symbol);
}
}
break;
case ChangeManager.DOCR_SYMBOL_ADDED:
Address addAddr = rec.getStart();
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(addAddr);
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
symProvider.symbolRemoved(primaryAtAdd);
}
symbol = (Symbol) rec.getNewValue();
symProvider.symbolAdded(symbol);
refProvider.symbolAdded(symbol);
break;
case ChangeManager.DOCR_SYMBOL_REMOVED:
Address removeAddr = rec.getStart();
Long symbolID = (Long) rec.getNewValue();
Symbol removedSymbol = new SymbolPlaceholder(symbolID, removeAddr, getProgram());
symProvider.symbolRemoved(removedSymbol);
refProvider.symbolRemoved(removedSymbol);
Symbol primaryAtRemove = symbolTable.getPrimarySymbol(removeAddr);
if (primaryAtRemove != null && primaryAtRemove.isDynamic()) {
symProvider.symbolAdded(primaryAtRemove);
refProvider.symbolRemoved(primaryAtRemove);
}
break;
case ChangeManager.DOCR_SYMBOL_RENAMED:
case ChangeManager.DOCR_SYMBOL_SCOPE_CHANGED:
case ChangeManager.DOCR_SYMBOL_DATA_CHANGED:
symbol = (Symbol) rec.getObject();
if (!CodecutUtils.nsUpdating()) {
if (!symbol.isDeleted()) {
symProvider.symbolChanged(symbol);
refProvider.symbolChanged(symbol);
}
}
break;
case ChangeManager.DOCR_SYMBOL_SOURCE_CHANGED:
symbol = (Symbol) rec.getObject();
symProvider.symbolChanged(symbol);
break;
case ChangeManager.DOCR_SYMBOL_SET_AS_PRIMARY:
symbol = (Symbol) rec.getNewValue();
symProvider.symbolChanged(symbol);
Symbol oldSymbol = (Symbol) rec.getOldValue();
if (oldSymbol != null) {
symProvider.symbolChanged(oldSymbol);
}
break;
case ChangeManager.DOCR_SYMBOL_ASSOCIATION_ADDED:
case ChangeManager.DOCR_SYMBOL_ASSOCIATION_REMOVED:
break;
case ChangeManager.DOCR_MEM_REFERENCE_ADDED:
Reference ref = (Reference) rec.getObject();
symbol = symbolTable.getSymbol(ref);
if (symbol != null) {
if(eventType == CODE_ADDED.getId() || eventType == CODE_REMOVED.getId()) {
if (rec.getNewValue() instanceof Data) {
symbol = symbolTable.getPrimarySymbol(rec.getStart());
if (symbol != null && symbol.isDynamic()) {
symProvider.symbolChanged(symbol);
refProvider.symbolChanged(symbol);
}
break;
case ChangeManager.DOCR_MEM_REFERENCE_REMOVED:
ref = (Reference) rec.getObject();
Address toAddr = ref.getToAddress();
if (toAddr.isMemoryAddress()) {
symbol = symbolTable.getSymbol(ref);
if (symbol == null) {
long id = symbolTable.getDynamicSymbolID(ref.getToAddress());
removedSymbol = new SymbolPlaceholder(id, toAddr, getProgram());
symProvider.symbolRemoved(removedSymbol);
}
else {
refProvider.symbolChanged(symbol);
}
}
break;
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_ADDED:
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_REMOVED:
Symbol[] symbols = symbolTable.getSymbols(rec.getStart());
for (Symbol element : symbols) {
symProvider.symbolChanged(element);
refProvider.symbolChanged(element);
}
break;
}
}
else if(eventType == SYMBOL_ADDED.getId()) {
Address addAddr = rec.getStart();
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(addAddr);
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
symProvider.symbolRemoved(primaryAtAdd);
}
symbol = (Symbol) rec.getNewValue();
symProvider.symbolAdded(symbol);
refProvider.symbolAdded(symbol);
}
else if(eventType == SYMBOL_REMOVED.getId()) {
Address removeAddr = rec.getStart();
Long symbolID = (Long) rec.getNewValue();
Symbol removedSymbol;
removedSymbol = new SymbolPlaceholder(symbolID, removeAddr, getProgram());
symProvider.symbolRemoved(removedSymbol);
refProvider.symbolRemoved(removedSymbol);
Symbol primaryAtRemove = symbolTable.getPrimarySymbol(removeAddr);
if (primaryAtRemove != null && primaryAtRemove.isDynamic()) {
symProvider.symbolAdded(primaryAtRemove);
refProvider.symbolRemoved(primaryAtRemove);
}
}
else if((eventType == SYMBOL_RENAMED.getId())
|| (eventType == SYMBOL_SCOPE_CHANGED.getId())
|| (eventType == SYMBOL_DATA_CHANGED.getId())) {
symbol = (Symbol) rec.getObject();
if (!CodecutUtils.nsUpdating()) {
if (!symbol.isDeleted()) {
symProvider.symbolChanged(symbol);
refProvider.symbolChanged(symbol);
}
}
}
else if(eventType == SYMBOL_SOURCE_CHANGED.getId()) {
symbol = (Symbol) rec.getObject();
symProvider.symbolChanged(symbol);
}
else if(eventType == SYMBOL_PRIMARY_STATE_CHANGED.getId()) {
symbol = (Symbol) rec.getNewValue();
symProvider.symbolChanged(symbol);
Symbol oldSymbol = (Symbol) rec.getOldValue();
if (oldSymbol != null) {
symProvider.symbolChanged(oldSymbol);
}
}
else if(eventType == REFERENCE_ADDED.getId()) {
Reference ref = (Reference) rec.getObject();
symbol = symbolTable.getSymbol(ref);
if (symbol != null) {
symProvider.symbolChanged(symbol);
refProvider.symbolChanged(symbol);
}
}
else if(eventType == REFERENCE_REMOVED.getId()) {
Reference ref = (Reference) rec.getObject();
Address toAddr = ref.getToAddress();
if (toAddr.isMemoryAddress()) {
symbol = symbolTable.getSymbol(ref);
if (symbol == null) {
Symbol removedSymbol;
long id = symbolTable.getDynamicSymbolID(ref.getToAddress());
removedSymbol = new SymbolPlaceholder(id, toAddr, getProgram());
symProvider.symbolRemoved(removedSymbol);
}
else {
refProvider.symbolChanged(symbol);
}
}
}
else if(eventType == EXTERNAL_ENTRY_ADDED.getId() || eventType == EXTERNAL_ENTRY_REMOVED.getId()) {
Symbol[] symbols = symbolTable.getSymbols(rec.getStart());
for (Symbol element : symbols) {
symProvider.symbolChanged(element);
refProvider.symbolChanged(element);
}
}
}
}
@@ -438,6 +477,10 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
SymbolProvider getSymbolProvider() {
return symProvider;
}
GraphCutProvider getGraphProvider() {
return graphProvider;
}
ReferenceProvider getReferenceProvider() {
return refProvider;
@@ -750,6 +793,8 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
moduleNameGuessingAction.setHelpLocation(new HelpLocation("Map", moduleNameGuessingAction.getName()));
moduleNameGuessingAction.setAddToAllWindows(true);
tool.addAction(moduleNameGuessingAction);
}
private void createExportActions() {
@@ -892,7 +937,7 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
}
private void guessModuleNames() {
Task guessNamesTask = new Task("Guess Module Names", true, true, true) {
@Override
@@ -1412,4 +1457,131 @@ public class CodeCutGUIPlugin extends Plugin implements DomainObjectListener {
}
}
private void createGraphActions() {
DockingAction showProviderAction = new DockingAction(SHOW_PROVIDER_ACTION_NAME, getName(), true) {
@Override
public void actionPerformed(ActionContext context) {
graphProvider.setVisible(true);
}
};
tool.addAction(showProviderAction);
DockingAction addToGraphAction = new DockingAction("Add Namespace to Graph", getName(), KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
graphProvider.addToWhitelist(symProvider.getCurrentSymbol().getParentNamespace());
checklistProvider.buildTable();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return symProvider.getCurrentSymbol() != null;
}
};
addToGraphAction.setPopupMenuData(
new MenuData(new String[] { "Add Namespace to Graph" }, "0"));
addToGraphAction.setDescription("Add this Namespace to the CodeCut Graph");
tool.addLocalAction(symProvider, addToGraphAction);
DockingAction displayCodeCutGraphAction =
new DockingAction("Display_CodeCut_Graph", this.getName()) {
@Override
public boolean isEnabledForContext(ActionContext context) {
return symProvider.getCurrentSymbol() != null;
}
@Override
public void actionPerformed(ActionContext context) {
graphProvider.setVisible(true);
}
};
MenuData graphModule = new MenuData(new String[] {ToolConstants.MENU_GRAPH, "Display CodeCut Object Graph"}, null, "Display CodeCut Object Graph");
graphModule.setMenuSubGroup("1");
displayCodeCutGraphAction.setMenuBarData(graphModule);
displayCodeCutGraphAction.setHelpLocation(null);
displayCodeCutGraphAction.setAddToAllWindows(true);
tool.addAction(displayCodeCutGraphAction);
}
void showProvider() {
graphProvider.setVisible(true);
}
public Address getCurrentAddress() {
if (currentLocation == null) {
return null;
}
return currentLocation.getAddress();
}
public VisualGraphOptions getOptions() {
return vgOptions;
}
public ProgramLocation getCurrentLocation() {
return currentLocation;
}
private void initializeGraphOptions() {
ToolOptions options = tool.getOptions(ToolConstants.GRAPH_OPTIONS);
options.addOptionsChangeListener(this);
HelpLocation help = new HelpLocation(getName(), "Options");
Options graphOptions = options.getOptions(GRAPH_NAME);
vgOptions.registerOptions(graphOptions, help);
vgOptions.loadOptions(graphOptions);
graphProvider.optionsChanged();
}
@Override
public void optionsChanged(ToolOptions options, String optionName, Object oldValue, Object newValue) throws OptionsVetoException {
Options graphOptions = options.getOptions(GRAPH_NAME);
vgOptions.loadOptions(graphOptions);
graphProvider.optionsChanged();
}
@Override
public void locationChanged(ProgramLocation loc) {
locationUpdater.update();
}
private void doLocationChanged() {
graphProvider.locationChanged(getCurrentLocation());
}
public void handleProviderLocationChanged(ProgramLocation location) {
GoToService goTo = getGoToService();
if (goTo == null) {
return;
}
SystemUtilities.runSwingLater(() -> {
goTo.goTo(location);
});
}
public void createChecklistActions() {
String popupGroup = "0";
DockingAction showChecklistAction = new DockingAction("Show Namespaces in Graph Filter", getName(), KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
checklistProvider.open();
checklistProvider.buildTable();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
return symProvider.getCurrentSymbol() != null;
}
};
showChecklistAction.setPopupMenuData(
new MenuData(new String[] { "Show Namespaces in Graph Filter" }, popupGroup));
showChecklistAction.setDescription("Show Namespaces that have been added to the CodeCut Graph");
tool.addLocalAction(symProvider, showChecklistAction);
}
}

View File

@@ -186,8 +186,11 @@ public class CodecutUtils {
SymbolType type = symbol.getSymbolType();
if (type == SymbolType.FUNCTION) {
Namespace ns = ((Function)symbol.getObject()).getParentNamespace();
if (!namespaceList.contains(ns)) {
namespaceList.add(ns);
//make sure it's a "real" namespace with a body - otherwise we can't work with it
if ((ns.getBody().getMinAddress() != null) && (ns.getBody().getMaxAddress() != null)) {
if (!namespaceList.contains(ns)) {
namespaceList.add(ns);
}
}
}
}

View File

@@ -79,10 +79,12 @@ class CombineProvider extends ComponentProviderAdapter implements ActionListener
private Namespace firstNamespace;
private Namespace secondNamespace;
private Namespace combinedNamespace;
private SymbolProvider symProvider;
CombineProvider(CodeCutGUIPlugin plugin) {
super(plugin.getTool(), "Combine Namespaces", plugin.getName(), ProgramActionContext.class);
this.plugin = plugin;
symProvider = plugin.getSymbolProvider();
setIcon(ICON);
addToToolbar();
@@ -241,12 +243,12 @@ class CombineProvider extends ComponentProviderAdapter implements ActionListener
}
}
}
}
this.closeComponent();
symProvider.reload();
}

View File

@@ -73,10 +73,13 @@ class CreateProvider extends ComponentProviderAdapter implements ActionListener
private Namespace namespace;
private Symbol symbol;
private SymbolProvider symProvider;
CreateProvider(CodeCutGUIPlugin plugin) {
super(plugin.getTool(), "Create Namespace", plugin.getName(), ProgramActionContext.class);
this.plugin = plugin;
symProvider = plugin.getSymbolProvider();
setIcon(ICON);
addToToolbar();
@@ -141,9 +144,10 @@ class CreateProvider extends ComponentProviderAdapter implements ActionListener
Msg.info(this, "Exception when attempting to change namespace of symbol "
+ this.symbol.getName() + " to " + nS.getName());
}
this.closeComponent();
symProvider.reload();
}
}

View File

@@ -123,7 +123,12 @@ class NamespacePanel extends JPanel {
Namespace ns = it.next();
AddressSetView ar = ns.getBody();
Msg.info(this, "Add: " + ns.getName() + " " + ar.getMinAddress().toString() + " " + ar.getMaxAddress().toString());
if ((ar.getMinAddress() != null) && (ar.getMaxAddress() != null)) {
Msg.info(this, "Add: " + ns.getName() + " " + ar.getMinAddress().toString() + " " + ar.getMaxAddress().toString());
}
else {
Msg.info(this, "Add: " + ns.getName() + " null min or max??");
}
SymbolIterator symIter = this.program.getSymbolTable().getSymbols(ns);
SymbolTableModel model = new SymbolTableModel(this.program, this.symProvider, this.tool, symIter, ns);

View File

@@ -73,10 +73,12 @@ class RenameProvider extends ComponentProviderAdapter implements ActionListener
private JButton button;
private Namespace namespace;
private SymbolProvider symProvider;
RenameProvider(CodeCutGUIPlugin plugin) {
super(plugin.getTool(), "Rename Namespace", plugin.getName(), ProgramActionContext.class);
this.plugin = plugin;
symProvider = plugin.getSymbolProvider();
setIcon(ICON);
addToToolbar();
@@ -172,6 +174,7 @@ class RenameProvider extends ComponentProviderAdapter implements ActionListener
this.closeComponent();
}
}
symProvider.reload();
}
}

View File

@@ -0,0 +1,88 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/plugin/EmptyFcgData.java
*/
package graphcut;
import ghidra.graph.viewer.GraphPerspectiveInfo;
import ghidra.program.model.symbol.Namespace;
/**
* empty data used to avoid null checks
*/
public class EmptyGraphCutData implements GraphCutData {
@Override
public Namespace getNamespace() {
throw new UnsupportedOperationException("Empty data has no namespace");
}
@Override
public boolean isNamespace(Namespace ns) {
return false;
}
@Override
public GraphCutGraph getGraph() {
throw new UnsupportedOperationException("Empty data has no graph");
}
@Override
public NamespaceEdgeCache getNamespaceEdgeCache() {
throw new UnsupportedOperationException("Empty data has no namespace edge cache");
}
@Override
public boolean hasResults() {
return false;
}
@Override
public void dispose() {
//nothing
}
@Override
public boolean isInitialized() {
return false;
}
@Override
public GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> getGraphPerspective(){
throw new UnsupportedOperationException("Empty data does not need view information");
}
@Override
public void setGraphPerspective(GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> info) {
throw new UnsupportedOperationException("Empty data does not need view information");
}
}

View File

@@ -0,0 +1,126 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrows from /Features Graph FunctionCalls/src/main/java/functioncalls/graph/view/FcgComponent.java
*/
package graphcut;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import functioncalls.graph.FcgEdge;
import functioncalls.graph.FcgVertex;
import generic.theme.GColor;
import ghidra.graph.viewer.GraphComponent;
import ghidra.graph.viewer.GraphViewer;
import ghidra.graph.viewer.SatelliteGraphViewer;
import ghidra.graph.viewer.VisualGraphViewUpdater;
import ghidra.graph.viewer.edge.VisualEdgeRenderer;
import ghidra.graph.viewer.layout.VisualGraphLayout;
import ghidra.graph.viewer.renderer.VisualVertexSatelliteRenderer;
import ghidra.graph.viewer.vertex.VisualVertexRenderer;
/**
* A graph component for GraphCut
*/
public class GraphCutComponent extends GraphComponent<GraphCutVertex, GraphCutEdge, GraphCutGraph> {
private GraphCutGraph gcGraph;
private GraphCutVertexPaintTransformer vertexPaintTransformer =
new GraphCutVertexPaintTransformer(GraphCutVertex.DEFAULT_VERTEX_SHAPE_COLOR);
private GraphCutEdgePaintTransformer edgePaintTransformer =
new GraphCutEdgePaintTransformer(new GColor("color.bg.plugin.fcg.edge.primary.direct"),
new GColor("color.bg.plugin.fcg.edge.primary.indirect"));
private GraphCutEdgePaintTransformer selectedEdgePaintTransformer =
new GraphCutEdgePaintTransformer(new GColor("color.bg.plugin.fcg.edge.primary.direct.selected"),
new GColor("color.bg.plugin.fcg.edge.primary.indirect.selected"));
private GraphCutEdgePaintTransformer satelliteEdgePaintTransformer =
new GraphCutEdgePaintTransformer(new GColor("color.bg.plugin.fcg.edge.satellite.direct"),
new GColor("color.bg.plugin.fcg.edge.satellite.indirect"));
GraphCutComponent(GraphCutGraph g){
setGraph(g);
build();
}
@Override
protected GraphCutVertex getInitialVertex() {
return gcGraph.getSource();
}
@Override
protected void decoratePrimaryViewer(GraphViewer<GraphCutVertex, GraphCutEdge> viewer,
VisualGraphLayout<GraphCutVertex, GraphCutEdge> layout) {
super.decoratePrimaryViewer(viewer, layout);
Renderer<GraphCutVertex, GraphCutEdge> renderer = viewer.getRenderer();
VisualVertexRenderer<GraphCutVertex, GraphCutEdge> vertexRenderer =
(VisualVertexRenderer<GraphCutVertex, GraphCutEdge>) renderer.getVertexRenderer();
vertexRenderer.setVertexFillPaintTransformer(vertexPaintTransformer);
VisualEdgeRenderer<GraphCutVertex, GraphCutEdge> edgeRenderer =
(VisualEdgeRenderer<GraphCutVertex, GraphCutEdge>) renderer.getEdgeRenderer();
edgeRenderer.setDrawColorTransformer(edgePaintTransformer);
edgeRenderer.setSelectedColorTransformer(selectedEdgePaintTransformer);
}
@Override
protected void decorateSatelliteViewer(SatelliteGraphViewer<GraphCutVertex, GraphCutEdge> viewer,
VisualGraphLayout<GraphCutVertex, GraphCutEdge> layout) {
super.decorateSatelliteViewer(viewer, layout);
Renderer<GraphCutVertex, GraphCutEdge> renderer = viewer.getRenderer();
VisualVertexSatelliteRenderer<GraphCutVertex, GraphCutEdge> vertexRenderer =
(VisualVertexSatelliteRenderer<GraphCutVertex, GraphCutEdge>) renderer.getVertexRenderer();
vertexRenderer.setVertexFillPaintTransformer(vertexPaintTransformer);
VisualEdgeRenderer<GraphCutVertex, GraphCutEdge> edgeRenderer =
(VisualEdgeRenderer<GraphCutVertex, GraphCutEdge>) renderer.getEdgeRenderer();
edgeRenderer.setDrawColorTransformer(satelliteEdgePaintTransformer);
}
@Override
public void dispose() {
gcGraph = null;
super.dispose();
}
@Override
public VisualGraphViewUpdater<GraphCutVertex, GraphCutEdge> getViewUpdater(){
return super.getViewUpdater();
}
}

View File

@@ -0,0 +1,97 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/plugin/FcgData.java
*/
package graphcut;
import ghidra.graph.viewer.GraphPerspectiveInfo;
import ghidra.program.model.symbol.Namespace;
/**
* Allows us to retrieve and work on the graph. Makes caching data simple
*/
public interface GraphCutData {
/**
* The namespace of this data
* @return the namespace
*/
Namespace getNamespace();
/**
* The graph of this data
* @return the graph
*/
GraphCutGraph getGraph();
/**
* Returns the cache of edges. Not in the graph but used to track existing edges that are not yet in the graph.
* @return
*/
NamespaceEdgeCache getNamespaceEdgeCache();
/**
* True if this data has a valid namespace
* @return true if this data has a valid namespace
*/
boolean hasResults();
/**
* False if the graph in this data has not yet been loaded
*/
boolean isInitialized();
/**
* Dispose the contents of this data
*/
void dispose();
/**
* Returns the view's graph perspective. this is used by the view to restore itself.
* @return the view's graph perspective
*/
GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> getGraphPerspective();
/**
* Set the view information for this graph data
* @param info the perspective to set
*/
void setGraphPerspective(GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> info);
/**
* Returns true if this data's namespace is equal to the given one
* @param ns the namespace to test
* @return true if this data's namespace is equal to the given one
*/
boolean isNamespace(Namespace ns);
}

View File

@@ -0,0 +1,79 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from /Features Graph FunctionCalls/src/main/java/functioncalls/plugin/FcgDataFactory.java
*/
package graphcut;
import com.google.common.cache.*;
import ghidra.program.model.symbol.Namespace;
/**
* A factory that will create GraphCutGraph data objects for a given namespace
*/
public class GraphCutDataFactory {
private LoadingCache<Namespace, GraphCutData> cache;
GraphCutDataFactory(RemovalListener<Namespace, GraphCutData> listener){
cache = CacheBuilder
.newBuilder()
.maximumSize(5)
.removalListener(listener)
.build(new CacheLoader<Namespace, GraphCutData> () {
@Override
public GraphCutData load(Namespace ns) throws Exception {
return new ValidGraphCutData(ns, new GraphCutGraph());
}
});
}
GraphCutData create(Namespace ns) {
if (ns == null) {
return new EmptyGraphCutData();
}
GraphCutData data = cache.getUnchecked(ns);
return data;
}
void remove(Namespace ns) {
cache.invalidate(ns);
}
void dispose() {
cache.invalidateAll();
}
}

View File

@@ -0,0 +1,70 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
package graphcut;
import ghidra.graph.viewer.edge.AbstractVisualEdge;
import graphcut.GraphCutVertex;
/**
* A GraphCut Edge
*/
public class GraphCutEdge extends AbstractVisualEdge<GraphCutVertex> {
public GraphCutEdge(GraphCutVertex start, GraphCutVertex end) {
super(start, end);
}
@SuppressWarnings("unchecked")
@Override
public GraphCutEdge cloneEdge(GraphCutVertex start, GraphCutVertex end) {
return new GraphCutEdge(start, end);
}
/**
* Returns true if an edge is direct from a lower level.
* @return true if this edge is a direct edge
*/
public boolean isDirectEdge() {
GraphCutLevel startLevel = getStart().getLevel();
GraphCutLevel endLevel = getEnd().getLevel();
if(startLevel.isSource() || endLevel.isSource()) {
return true;
}
GraphCutLevel parent = startLevel.parent();
if (parent.equals(endLevel)) {
return true;
}
GraphCutLevel child = startLevel.child();
return child.equals(endLevel);
}
}

View File

@@ -0,0 +1,76 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/renderer/FcgEdgePaintTransformer.java
*/
package graphcut;
import com.google.common.base.Function;
import java.awt.Color;
import ghidra.util.ColorUtils;
public class GraphCutEdgePaintTransformer implements Function<GraphCutEdge, Color> {
private Color directColor;
private Color indirectColor;
private Color[] directColorWithAlpha = new Color[10];
public GraphCutEdgePaintTransformer(Color directColor, Color indirectColor) {
this.directColor = directColor;
this.indirectColor = indirectColor;
directColorWithAlpha = alphatize(directColor);
}
private Color[] alphatize(Color c) {
Color[] alphad = new Color[10];
alphad[0] = c;
for (int i = 1; i < 10; i++) {
double newAlpha = 255 - (i * 25.5);
alphad[i] = ColorUtils.withAlpha(c, (int) newAlpha);
}
return alphad;
}
@Override
public Color apply(GraphCutEdge e) {
if (e.isDirectEdge()) {
return getDirectEdgeColor(e);
}
return indirectColor;
}
private Color getDirectEdgeColor(GraphCutEdge e) {
return directColor;
}
}

View File

@@ -0,0 +1,67 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/job/FcgEmphasizeEdgesJob.java
*/
package graphcut;
import java.util.Set;
import ghidra.graph.job.AbstractGraphVisibilityTransitionJob;
import ghidra.graph.viewer.GraphViewer;
public class GraphCutEmphasizeEdgesJob extends AbstractGraphVisibilityTransitionJob<GraphCutVertex, GraphCutEdge> {
private Set<GraphCutEdge> edges;
public GraphCutEmphasizeEdgesJob(GraphViewer<GraphCutVertex, GraphCutEdge> viewer, Set<GraphCutEdge> edges) {
super(viewer, true);
this.edges = edges;
}
@Override
protected void updateOpacity(double percentComplete) {
double remaining = percentComplete;
if(percentComplete > 0.5){
remaining = 1 - percentComplete;
}
double modified = remaining * 10;
for (GraphCutEdge e : edges) {
e.setEmphasis(modified);
}
}
}

View File

@@ -0,0 +1,192 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/job/FcgExpandingVertexCollection.java
*/
package graphcut;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import edu.uci.ics.jung.algorithms.layout.Layout;
import functioncalls.graph.FcgDirection;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.map.LazyMap;
import ghidra.graph.viewer.GraphViewer;
import util.CollectionUtils;
public class GraphCutExpandingVertexCollection {
private Comparator<GraphCutVertex> vertexComparator =
(v1,v2) -> v1.getID() > v2.getID() ? +1 : v1.getID() < v2.getID() ? -1 : 0;
private Comparator<GraphCutVertex> sourceVertexComparator = this::compareVerticesByLayoutPosition;
private Map<GraphCutVertex, TreeSet<GraphCutVertex>> newVerticesBySource = LazyMap.lazyMap(
new TreeMap<>(sourceVertexComparator), () -> new TreeSet<>(vertexComparator));
private GraphViewer<GraphCutVertex, GraphCutEdge> viewer;
private GraphCutLevel parentLevel;
private GraphCutLevel expandingLevel;
private Set<GraphCutVertex> newVertices;
private Set<GraphCutEdge> newEdges;
private Set<GraphCutEdge> indirectEdges = Collections.emptySet();
private boolean isIncoming;
private Iterable<GraphCutVertex> sources;
public GraphCutExpandingVertexCollection(Iterable<GraphCutVertex> sources, GraphCutLevel parentLevel, GraphCutLevel expandingLevel,
Set<GraphCutVertex> newVertices, Set<GraphCutEdge> newEdges, Set<GraphCutEdge> allParentLevelEdges, boolean isIncoming,
GraphViewer<GraphCutVertex, GraphCutEdge> viewer) {
this.sources = sources;
this.parentLevel = parentLevel;
this.newVertices = newVertices;
this.newEdges = newEdges;
this.isIncoming = isIncoming;
this.viewer = viewer;
this.expandingLevel = expandingLevel;
for(GraphCutEdge e: allParentLevelEdges) {
GraphCutVertex start = e.getStart();
GraphCutVertex end = e.getEnd();
GraphCutLevel startLevel = start.getLevel();
GraphCutLevel endLevel = end.getLevel();
if (expandingLevel.equals(startLevel)) {
if(expandingLevel.equals(endLevel)) {
newVerticesBySource.get(start).add(end);
newVerticesBySource.get(end).add(start);
}
else {
newVerticesBySource.get(end).add(start);
}
}
else {
newVerticesBySource.get(start).add(end);
}
}
}
private int compareVerticesByLayoutPosition(GraphCutVertex v1, GraphCutVertex v2) {
Layout<GraphCutVertex, GraphCutEdge> layout = viewer.getGraphLayout();
GraphCutLevel l1 = v1.getLevel();
GraphCutLevel l2 = v2.getLevel();
int result = l1.compareTo(l2);
if (result != 0) {
if(l1.equals(parentLevel)) {
return -1;
}
if(l2.equals(parentLevel)) {
return 1;
}
return result;
}
Point2D p1 = layout.apply(v1);
Point2D p2 = layout.apply(v2);
return (int) (p1.getX() - p2.getX());
}
public List<GraphCutVertex> getVerticesByLevel(GraphCutLevel level){
Set<GraphCutVertex> existingVertices = newVerticesBySource.keySet();
List<GraphCutVertex> verticesAtLevel = existingVertices.stream().filter(v -> v.getLevel().equals(level)).collect(Collectors.toList());
return verticesAtLevel;
}
public List<GraphCutVertex> getAllVerticesAtNewLevel(){
Set<GraphCutVertex> existingVertices = newVerticesBySource.keySet();
LinkedHashSet<GraphCutVertex> sortedVertices = existingVertices
.stream()
.map(v -> newVerticesBySource.get(v))
.flatMap(set -> set.stream())
.filter(v -> v.getLevel().equals(expandingLevel))
.collect(Collectors.toCollection(LinkedHashSet::new));
return new ArrayList<>(sortedVertices);
}
public Set<GraphCutVertex> getNewVertices(){
return newVertices;
}
public Set<GraphCutEdge> getIndirectEdges() {
return indirectEdges;
}
public Iterable<GraphCutEdge> getNewEdges(){
return IterableUtils.chainedIterable(newEdges, indirectEdges);
}
public int getNewEdgeCount() {
return newEdges.size() + indirectEdges.size();
}
public void setIndirectEdges(Set<GraphCutEdge> indirectEdges) {
this.indirectEdges = CollectionUtils.asSet(indirectEdges);
}
public GraphCutLevel getExpandingLevel() {
return expandingLevel;
}
public FcgDirection getExpandingDirection() {
return expandingLevel.getDirection();
}
public Iterable<GraphCutVertex> getSources(){
return sources;
}
public boolean isIncoming() {
return isIncoming;
}
}

View File

@@ -0,0 +1,50 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
package graphcut;
public interface GraphCutExpansionListener {
/**
* Show or hide those vertices that are on incoming edges to v
*
* @param v the vertex
*/
public void toggleIncomingVertices(GraphCutVertex v);
/**
* Show or hide those vertices that are on outgoing edges to v
*
* @param v the vertex
*/
public void toggleOutgoingVertices(GraphCutVertex v);
}

View File

@@ -0,0 +1,177 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/FunctionCallGraph.java
*/
package graphcut;
import java.util.*;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.map.LazyMap;
import functioncalls.graph.FcgDirection;
import ghidra.graph.graphs.FilteringVisualGraph;
import ghidra.graph.viewer.layout.VisualGraphLayout;
import ghidra.program.model.symbol.Namespace;
/*
* A graph for the GraphCut Plugin
*/
public class GraphCutGraph extends FilteringVisualGraph<GraphCutVertex, GraphCutEdge> {
private VisualGraphLayout<GraphCutVertex, GraphCutEdge> layout;
private GraphCutVertex source;
private Map<Namespace, GraphCutVertex> verticesByNamespace = new HashMap<>();
private Comparator<GraphCutVertex> vertexComparator =
(v1,v2) -> v1.getID() > v2.getID() ? +1 : v1.getID() < v2.getID() ? -1 : 0;
private Map<GraphCutLevel, Set<GraphCutVertex>> verticesByLevel =
LazyMap.lazyMap(new HashMap<>(), () -> new TreeSet<>(vertexComparator));
/**
* Sets the source vertex from which the graph originates
* @param the source vertex
*/
public void setSource(GraphCutVertex source) {
if (this.source != null) {
throw new IllegalStateException("Cannot change graph source once it has been created");
}
this.source = source;
addVertex(source);
}
/**
* Returns the vertex from which the graph is created
* @return source vertex
*/
public GraphCutVertex getSource() {
return source;
}
/**
* returns whether there is a vertex for the given namespace
* @param ns Namespace
* @return True if graph contains a vertex for the namespace
*/
public boolean containsNamespace(Namespace ns) {
return verticesByNamespace.containsKey(ns);
}
/**
* Returns the vertex for the given namespace
* @param ns Namespace
* @return the vertex for the given namespace
*/
public GraphCutVertex getVertex(Namespace ns) {
return verticesByNamespace.get(ns);
}
/**
* Return all vertices in the given level
* @param level the level to retrieve
* @return all vertices in the given level
*/
public Iterable<GraphCutVertex> getVerticesByLevel(GraphCutLevel level){
return IterableUtils.unmodifiableIterable(verticesByLevel.get(level));
}
/**
* Returns the level furthest from the source node in the given direction
* @param direction the direction to search
* @return the furthest level
*/
public GraphCutLevel getLargestLevel(FcgDirection direction) {
GraphCutLevel greatest = new GraphCutLevel(1, direction);
Set<GraphCutLevel> keys = verticesByLevel.keySet();
for (GraphCutLevel level : keys) {
if (level.getDirection() != direction) {
continue;
}
if (level.getRow() > greatest.getRow()) {
greatest = level;
}
}
return greatest;
}
@Override
public VisualGraphLayout<GraphCutVertex, GraphCutEdge> getLayout(){
return layout;
}
@Override
public GraphCutGraph copy() {
GraphCutGraph newGraph = new GraphCutGraph();
for (GraphCutVertex v : vertices.keySet()) {
newGraph.addVertex(v);
}
for (GraphCutEdge e : edges.keySet()) {
newGraph.addEdge(e);
}
return newGraph;
}
public void setLayout(VisualGraphLayout<GraphCutVertex, GraphCutEdge> layout) {
this.layout = layout;
}
@Override
protected void verticesAdded(Collection<GraphCutVertex> added) {
for (GraphCutVertex v : added) {
Namespace ns = v.getNamespace();
verticesByNamespace.put(ns, v);
verticesByLevel.get(v.getLevel()).add(v);
}
super.verticesAdded(added);
}
@Override
protected void verticesRemoved(Collection<GraphCutVertex> removed) {
for (GraphCutVertex v : removed) {
Namespace ns = v.getNamespace();
verticesByNamespace.remove(ns);
verticesByLevel.get(v.getLevel()).remove(v);
}
}
}

View File

@@ -0,0 +1,137 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows heavily from Ghidra File /Features Graph FunctionCalls/src/main/java/functioncalls/graph/layout/BowTieLayout.java
*/
package graphcut;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import ghidra.graph.VisualGraph;
import ghidra.graph.viewer.layout.AbstractVisualGraphLayout;
import ghidra.graph.viewer.layout.Column;
import ghidra.graph.viewer.layout.GridLocationMap;
import ghidra.graph.viewer.layout.LayoutPositions;
import ghidra.graph.viewer.layout.Row;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class GraphCutLayout extends AbstractVisualGraphLayout<GraphCutVertex, GraphCutEdge> {
protected GraphCutLayout(GraphCutGraph graph, String name){
super(graph, name);
}
public AbstractVisualGraphLayout<GraphCutVertex, GraphCutEdge> createClonedLayout(VisualGraph<GraphCutVertex, GraphCutEdge> newGraph){
if (!(newGraph instanceof GraphCutGraph)) {
throw new IllegalArgumentException("Must pass a GraphCut Graph to clone");
}
GraphCutLayout newLayout = new GraphCutLayout((GraphCutGraph) newGraph, getLayoutName());
return newLayout;
}
@Override
protected Point2D getVertexLocation(GraphCutVertex v, Column col, Row<GraphCutVertex> row, Rectangle bounds) {
return getCenteredVertexLocation(v, col, row, bounds);
}
@Override
public boolean isCondensedLayout() {
return false;
}
@Override
public GraphCutGraph getVisualGraph() {
return (GraphCutGraph) getGraph();
}
@Override
protected GridLocationMap<GraphCutVertex, GraphCutEdge> performInitialGridLayout(VisualGraph<GraphCutVertex,GraphCutEdge> g) throws CancelledException {
if (!(g instanceof GraphCutGraph)) {
throw new IllegalArgumentException("This layout can only be used with the GraphCutGraph");
}
return layoutGraphCutGraph((GraphCutGraph)g);
}
@Override
public LayoutPositions<GraphCutVertex, GraphCutEdge> calculateLocations(VisualGraph<GraphCutVertex, GraphCutEdge> g, TaskMonitor taskMonitor){
LayoutPositions<GraphCutVertex, GraphCutEdge> locs = super.calculateLocations(g, taskMonitor);
return locs;
}
private GridLocationMap<GraphCutVertex, GraphCutEdge> layoutGraphCutGraph(GraphCutGraph g){
GridLocationMap<GraphCutVertex, GraphCutEdge> grid = new GridLocationMap<>();
GraphCutVertex source = Objects.requireNonNull(g.getSource());
// Incoming nodes on top
// Sorted by id
List<GraphCutEdge> inEdges = new ArrayList<>(g.getInEdges(source));
List<GraphCutVertex> inVertices = inEdges.stream().map(e -> e.getStart()).collect(Collectors.toList());
inVertices.sort((v1,v2) -> v1.getID() > v2.getID() ? +1 : v1.getID() < v2.getID() ? -1 : 0);
//first row -> incoming nodes
int row = 0;
for(int col = 0; col < inVertices.size(); col++) {
GraphCutVertex v = inVertices.get(col);
grid.set(v, row, col);
}
//middle row -> source
row = 1;
grid.set(source, row, 0);
//bottom row -> outgoing nodes
List<GraphCutEdge> outEdges = new ArrayList<>(g.getOutEdges(source));
List<GraphCutVertex> outVertices = outEdges.stream().map(e -> e.getEnd()).collect(Collectors.toList());
outVertices.removeAll(inVertices); //prevent cycles
outVertices.sort((v1,v2) -> v1.getID() > v2.getID() ? +1 : v1.getID() < v2.getID() ? -1 : 0);
row = 2;
for (int col = 0; col < outVertices.size(); col++) {
GraphCutVertex v = outVertices.get(col);
grid.set(v, row, col);
}
grid.centerRows();
return grid;
}
}

View File

@@ -0,0 +1,393 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/job/BowTieExpandVerticesJob.java
*/
package graphcut;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Function;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.RenderContext;
import ghidra.graph.job.AbstractGraphTransitionJob;
import ghidra.graph.viewer.GraphViewer;
import ghidra.graph.viewer.GraphViewerUtils;
import ghidra.util.Msg;
// bowTieExpandVerticesJob
public class GraphCutLayoutExpandVerticesJob extends AbstractGraphTransitionJob<GraphCutVertex, GraphCutEdge> {
private boolean incoming;
private GraphCutLevel expandingLevel;
private GraphCutExpandingVertexCollection newVertexCollection;
public GraphCutLayoutExpandVerticesJob(GraphViewer<GraphCutVertex, GraphCutEdge> viewer,
GraphCutExpandingVertexCollection newVertexCollection, boolean useAnimation) {
super(viewer, useAnimation);
this.newVertexCollection = newVertexCollection;
this.incoming = newVertexCollection.isIncoming();
this.expandingLevel = newVertexCollection.getExpandingLevel();
if(!(graphLayout instanceof GraphCutLayout)) {
throw new IllegalArgumentException("The current graph layout must be the GraphCut Layout to use this job");
}
Msg.trace(this, "\n Layout Expand Job - new vertices: " + newVertexCollection.getNewVertices());
}
@Override
protected boolean isTooBigToAnimate() {
return graph.getVertexCount() > 1000;
}
@Override
protected void updateOpacity(double percentComplete) {
double x = percentComplete;
double x2 = x*x;
double remaining = 1-percentComplete;
double y = x2 - remaining;
Set<GraphCutVertex> newVertices = newVertexCollection.getNewVertices();
double vertexAlpha = x;
double edgeAlpha = Math.max(y, 0);
for(GraphCutVertex v : newVertices) {
v.setAlpha(vertexAlpha);
}
Iterable<GraphCutEdge> newEdges = newVertexCollection.getNewEdges();
for(GraphCutEdge e : newEdges) {
e.setAlpha(edgeAlpha);
}
}
@Override
public boolean canShortcut() {
return true;
}
@Override
public void shortcut() {
isShortcut = true;
if(vertexLocations.isEmpty()) {
initializeVertexLocations();
}
stop();
}
@Override
protected void initializeVertexLocations() {
Map<GraphCutVertex, TransitionPoints> destinationLocations = createDestinationLocation();
vertexLocations.putAll(destinationLocations);
}
private Map<GraphCutVertex, TransitionPoints> createDestinationLocation(){
Map<GraphCutVertex, Point2D> finalDestinations = arrangeNewVertices();
Map<GraphCutVertex, TransitionPoints> transitions = new HashMap<>();
GraphCutLevel parentLevel = expandingLevel.parent();
Iterable<GraphCutEdge> newEdges = newVertexCollection.getNewEdges();
Set<GraphCutVertex> newVertices = newVertexCollection.getNewVertices();
for(GraphCutEdge e : newEdges) {
GraphCutVertex newVertex = incoming ? e.getStart() : e.getEnd();
if(!finalDestinations.containsKey(newVertex)) {
continue;
}
if(!newVertices.contains(newVertex)) {
continue;
}
GraphCutVertex existingVertex = incoming ? e.getEnd() : e.getStart();
GraphCutLevel existingLevel = existingVertex.getLevel();
if (!existingLevel.equals(parentLevel)) {
continue;
}
Point2D start = (Point2D) toLocation(existingVertex).clone();
Point2D end = finalDestinations.get(newVertex);
TransitionPoints trans = new TransitionPoints(start, end);
transitions.put(newVertex, trans);
}
return transitions;
}
private Map<GraphCutVertex, Point2D> arrangeNewVertices(){
GraphCutLayout bowTie = (GraphCutLayout) graphLayout;
boolean isCondensed = bowTie.isCondensedLayout();
int widthPadding = isCondensed ? GraphViewerUtils.EXTRA_LAYOUT_COLUMN_SPACING_CONDENSED :
GraphViewerUtils.EXTRA_LAYOUT_COLUMN_SPACING;
widthPadding *= expandingLevel.getDistance();
int heightPadding = calculateHeightPadding(isCondensed);
GraphCutLevel parentLevel = expandingLevel.parent();
List<GraphCutVertex> parentLevelVertices = newVertexCollection.getVerticesByLevel(parentLevel);
if(parentLevelVertices.isEmpty()) {
return Collections.emptyMap();
}
Rectangle existingRowBounds = getBounds(parentLevelVertices);
Msg.trace(this, "existing row bounds " + existingRowBounds);
double existingY = existingRowBounds.y;
double existingCenterX = existingRowBounds.x + (existingRowBounds.width/2);
List<GraphCutVertex> allLevelVertices = newVertexCollection.getAllVerticesAtNewLevel();
double newRowWidth = getWidth(allLevelVertices, widthPadding);
double newRowHeight = getHeight(allLevelVertices);
double newRowX = existingCenterX - (newRowWidth/2);
double newRowY = 0;
if(newVertexCollection.isIncoming()) {
newRowY = existingY - newRowHeight - heightPadding;
}
else {
newRowY = existingY +existingRowBounds.height + heightPadding;
}
Msg.trace(this, "new row bounds " + new Rectangle2D.Double(newRowX, newRowY, newRowWidth, newRowHeight));
Map<GraphCutVertex, Point2D> locations = getExistingLocations(allLevelVertices);
if(!locations.isEmpty()) {
return locations;
}
RenderContext<GraphCutVertex, GraphCutEdge> renderContext = viewer.getRenderContext();
Function<? super GraphCutVertex, Shape> shaper = renderContext.getVertexShapeTransformer();
double x = newRowX;
double y = newRowY;
int n = allLevelVertices.size();
//Dynamic Layout
Set<GraphCutVertex> placed = new HashSet<>();
Set<GraphCutVertex> newVertices = newVertexCollection.getNewVertices();
//Make shallow copy
List<GraphCutVertex> allLevelVerticesOrig = new ArrayList<>();
for(GraphCutVertex v : allLevelVertices) {
allLevelVerticesOrig.add(v);
}
// 1. Place visible vertices
for(GraphCutVertex v: allLevelVerticesOrig) {
if(v.visible) {
GraphCutVertex tmpVertex = allLevelVertices.get(v.layoutIndex);
int tmpIndex = allLevelVertices.indexOf(v);
allLevelVertices.set(v.layoutIndex, v);
allLevelVertices.set(tmpIndex, tmpVertex);
placed.add(v);
}
}
// 2. Place newVertices
for(GraphCutVertex v: newVertices) {
if(placed.contains(v)) {
continue;
}
placed.add(v);
int index = findEmptyLayoutIndex(allLevelVertices);
v.layoutIndex = index;
v.visible = true;
GraphCutVertex tmpVertex = allLevelVertices.get(index);
int tmpIndex = allLevelVertices.indexOf(v);
allLevelVertices.set(tmpIndex, tmpVertex);
allLevelVertices.set(v.layoutIndex, v);
}
// 3. Place all invisible vertices randomly
int curr = 0;
for(GraphCutVertex v: allLevelVerticesOrig) {
if(placed.contains(v)) {
continue;
}
//find empty spot
while(allLevelVertices.get(curr).visible) {
curr++;
}
allLevelVertices.set(curr, v);
}
for(int i = 0; i < n; i++) {
GraphCutVertex v = allLevelVertices.get(i);
Rectangle myBounds = shaper.apply(v).getBounds();
double myHalf = myBounds.width / 2;
double nextHalf = 0;
boolean isLast = i == n-1;
if(!isLast) {
GraphCutVertex nextV = allLevelVertices.get(i+1);
Rectangle nextBounds = shaper.apply(nextV).getBounds();
nextHalf = nextBounds.width/2;
}
Point2D p = new Point2D.Double(x,y);
locations.put(v, p);
double vWidth = myHalf + widthPadding + nextHalf;
Msg.trace(this, v + " at x,width: "+x+","+vWidth);
x+=vWidth;
}
return locations;
}
private int findEmptyLayoutIndex(List<GraphCutVertex> vertices) {
//Check middle
int mid = vertices.size()/2;
if(!vertices.get(mid).visible) {
return mid;
}
// start checking both sides
int left = mid-1;
int right = mid+1;
while(left >= 0 || right <=vertices.size()-1) {
if(left>=0 && !vertices.get(left).visible) {
return left;
}
left--;
if(right <= vertices.size()-1 && !vertices.get(right).visible) {
return right;
}
right++;
}
return -1;
}
private int calculateHeightPadding(boolean isCondensed) {
int basePadding = isCondensed ? GraphViewerUtils.EXTRA_LAYOUT_ROW_SPACING_CONDENSED
: GraphViewerUtils.EXTRA_LAYOUT_ROW_SPACING;
double separationFactor = expandingLevel.getDistance();
List<GraphCutVertex> allLevelVertices = newVertexCollection.getAllVerticesAtNewLevel();
int count = allLevelVertices.size();
double to = 1.25;
double power = Math.pow(separationFactor, to);
int maxPadding = (int) (basePadding * power);
int delta = maxPadding - basePadding;
double percent = Math.min(count / 20f, 1);
int padding = basePadding + (int) (delta * percent);
return padding;
}
private Map<GraphCutVertex, Point2D> getExistingLocations(List<GraphCutVertex> vertices){
Map<GraphCutVertex, Point2D> locations = new HashMap<>();
for(GraphCutVertex v: vertices) {
Point2D p = toLocation(v);
if (p.getX() == 0 && p.getY() == 0) {
return new HashMap<>();
}
locations.put(v, (Point2D) p.clone());
}
return locations;
}
private Rectangle getBounds(List<GraphCutVertex> vertices) {
RenderContext<GraphCutVertex, GraphCutEdge> renderContext = viewer.getRenderContext();
Function<? super GraphCutVertex, Shape> shaper = renderContext.getVertexShapeTransformer();
Layout<GraphCutVertex, GraphCutEdge> layout = viewer.getGraphLayout();
Rectangle area = null;
for (GraphCutVertex v : vertices) {
Rectangle bounds = shaper.apply(v).getBounds();
Point2D loc = layout.apply(v);
int x = (int) loc.getX();
int y = (int) loc.getY();
bounds.setLocation(x,y);
if(area == null) {
area = bounds;
}
area.add(bounds);
}
return area;
}
private int getWidth(List<GraphCutVertex> vertices, int widthPadding) {
RenderContext<GraphCutVertex, GraphCutEdge> renderContext = viewer.getRenderContext();
Function<? super GraphCutVertex, Shape> shaper = renderContext.getVertexShapeTransformer();
int width = 0;
for (GraphCutVertex v : vertices) {
width += shaper.apply(v).getBounds().width + widthPadding;
}
return width;
}
private int getHeight(List<GraphCutVertex> vertices) {
RenderContext<GraphCutVertex, GraphCutEdge> renderContext = viewer.getRenderContext();
Function<? super GraphCutVertex, Shape> shaper = renderContext.getVertexShapeTransformer();
int height = 0;
for (GraphCutVertex v: vertices) {
height = Math.max(height, shaper.apply(v).getBounds().height);
}
return height;
}
}

View File

@@ -0,0 +1,68 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/layout/BowTieLayoutProvider.java
*/
package graphcut;
import javax.swing.Icon;
import generic.theme.GIcon;
import ghidra.graph.viewer.layout.AbstractLayoutProvider;
import ghidra.graph.viewer.layout.VisualGraphLayout;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* A layout provider for GraphCut
*/
public class GraphCutLayoutProvider extends AbstractLayoutProvider<GraphCutVertex, GraphCutEdge, GraphCutGraph> {
public static final String NAME = "GraphCut Layout";
private static final Icon DEFAULT_ICON = new GIcon("icon.plugin.fcg.layout.bow.tie");
@Override
public VisualGraphLayout<GraphCutVertex, GraphCutEdge> getLayout(GraphCutGraph graph, TaskMonitor taskMonitor) throws CancelledException{
GraphCutLayout layout = new GraphCutLayout(graph, NAME);
initVertexLocations(graph, layout);
return layout;
}
@Override
public String getLayoutName() {
return NAME;
}
@Override
public Icon getActionIcon() {
return DEFAULT_ICON;
}
}

View File

@@ -0,0 +1,193 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrowed from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/FcgEdge.java
*/
package graphcut;
import static functioncalls.graph.FcgDirection.*;
import functioncalls.graph.FcgDirection;
// A container class that represents a GraphCut row.
public class GraphCutLevel implements Comparable<GraphCutLevel> {
private int row;
private FcgDirection direction;
public static GraphCutLevel sourceLevel() {
return new GraphCutLevel(0, IN_AND_OUT);
}
public GraphCutLevel(int distance, FcgDirection direction) {
this.row = toRow(distance);
this.direction = direction;
if (row == 0) {
throw new IllegalArgumentException("Graph Cut uses a 1-based row system");
}
if (row == 1 && direction != IN_AND_OUT) {
throw new IllegalArgumentException("Row 1 must be FcgDirection.IN_AND_OUT");
}
}
private int toRow(int distance) {
int oneBased = distance + 1;
return (direction == OUT) ? -oneBased : oneBased;
}
public int getRow() {
return row;
}
public int getDistance() {
return Math.abs(row) - 1;
}
public FcgDirection getDirection() {
return direction;
}
/**
* Returns true if this level is level 1
* @return true if this level represents the source level
*/
public boolean isSource() {
return direction.isSource();
}
public GraphCutLevel parent() {
if (direction == IN_AND_OUT) {
// undefined--we are the parent of all
throw new IllegalArgumentException(
"To get the parent of the source level you must use the constructor directly");
}
int newDistance = getDistance() - 1;
FcgDirection newDirection = direction;
if (newDistance == 0) {
newDirection = IN_AND_OUT;
}
return new GraphCutLevel(newDistance, newDirection);
}
public GraphCutLevel child() {
if (direction == IN_AND_OUT) {
// undefined--this node goes in both directions
throw new IllegalArgumentException(
"To get the child of the source level you " + "must use the constructor directly");
}
return child(direction);
}
public boolean isParentOf(GraphCutLevel other) {
if (isSource()) {
return other.getDistance() == 1;
}
if (direction != other.direction) {
return false;
}
// e.g., row 2 - row 1 = 1
return other.getDistance() - getDistance() == 1;
}
public boolean isChildOf(GraphCutLevel other) {
return other.isParentOf(this);
}
public GraphCutLevel child(FcgDirection newDirection) {
if (newDirection == IN_AND_OUT) {
// undefined--IN_AND_OUT goes in both directions
throw new IllegalArgumentException("Direction cannot be IN_AND_OUT");
}
int newDistance = getDistance() + 1;
return new GraphCutLevel(newDistance, newDirection);
}
@Override
public String toString() {
return direction + " - row " + Integer.toString(getRelativeRow());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((direction == null) ? 0 : direction.hashCode());
result = prime * result + row;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GraphCutLevel other = (GraphCutLevel) obj;
if (direction != other.direction) {
return false;
}
if (row != other.row) {
return false;
}
return true;
}
private int getRelativeRow() {
return direction == OUT ? -row : row;
}
@Override
public int compareTo(GraphCutLevel l2) {
int result = getDirection().compareTo(l2.getDirection());
if (result != 0) {
return result;
}
return -(getRelativeRow() - l2.getRelativeRow());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/renderer/FcgTooltipProvider.java
*/
package graphcut;
import java.awt.Component;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.*;
import ghidra.graph.viewer.event.mouse.VertexTooltipProvider;
public class GraphCutTooltipProvider implements VertexTooltipProvider<GraphCutVertex, GraphCutEdge> {
@Override
public JComponent getTooltip(GraphCutVertex v) {
JToolTip tip = new JToolTip();
tip.setTipText(v.getName());
return tip;
}
@Override
public JComponent getTooltip(GraphCutVertex v, GraphCutEdge e) {
return null;
}
@Override
public String getTooltipText(GraphCutVertex v, MouseEvent e) {
Component child = e.getComponent();
if(child instanceof JButton) {
return ((JButton) child).getToolTipText();
}
return v.getName();
}
}

View File

@@ -0,0 +1,664 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrowed from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/FcgVertex.java
*/
package graphcut;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Ellipse2D.Double;
import java.awt.image.BufferedImage;
import java.util.Objects;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import ghidra.program.model.symbol.Namespace;
import docking.widgets.EmptyBorderButton;
import docking.widgets.label.GDLabel;
import functioncalls.graph.FcgDirection;
import functioncalls.graph.FcgVertex;
import generic.theme.GColor;
import generic.theme.GThemeDefaults.Colors.Palette;
import generic.theme.Gui;
import ghidra.graph.viewer.vertex.AbstractVisualVertex;
import ghidra.graph.viewer.vertex.VertexShapeProvider;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.util.StringUtilities;
import resources.Icons;
import resources.ResourceManager;
// A GraphCutVertex
public class GraphCutVertex extends AbstractVisualVertex implements VertexShapeProvider {
public static final Color DEFAULT_VERTEX_SHAPE_COLOR = new GColor("color.bg.plugin.fcg.vertex.default");
private static final Color TOO_BIG_VERTEX_SHAPE_COLOR = new GColor("color.bg.plugin.fcg.vertex.toobig");
//@formatter:on
public static final Icon NOT_ALLOWED_ICON = Icons.ERROR_ICON;
private static final Icon EXPAND_ICON =
ResourceManager.getScaledIcon(Icons.EXPAND_ALL_ICON, 10, 10);
private static final Icon COLLAPSE_ICON =
ResourceManager.getScaledIcon(Icons.COLLAPSE_ALL_ICON, 10, 10);
// higher numbered layers go on top
private static final Integer VERTEX_SHAPE_LAYER = 100;
private static final Integer TOGGLE_BUTTON_LAYER = 200;
private static final Integer LABEL_LAYER = 300;
private static final int GAP = 2;
private static final int VERTEX_SHAPE_SIZE = 50;
private static final int MAX_NAME_LENGTH = 30;
private Namespace namespace;
private JLayeredPane layeredPane;
private JButton toggleInsButton = new EmptyBorderButton(EXPAND_ICON);
private JButton toggleOutsButton = new EmptyBorderButton(EXPAND_ICON);
private JLabel nameLabel = new GDLabel();
private JLabel vertexImageLabel = new GDLabel();
private Double vertexShape;
private Double compactShape;
private Shape fullShape;
// these values are set after construction from external sources
private boolean hasIncomingReferences;
private boolean hasOutgoingReferences;
private boolean tooManyIncomingReferences;
private boolean tooManyOutgoingReferences;
private boolean incomingExpanded;
private boolean outgoingExpanded;
// set this to true to see borders around the components of this vertex
private boolean useDebugBorders = false;
private Paint inPaint;
private Paint outPaint;
private GraphCutLevel level;
// Dynamic Layout Variables
public int layoutIndex;
public boolean visible;
/**
* Constructor
* @param namespace Namespace represented by this vertex
* @param level The level of this vertex
* @param expansionListener listener for expanding connections to this vertex
*/
public GraphCutVertex(Namespace namespace, GraphCutLevel level, GraphCutExpansionListener expansionListener) {
this.namespace = namespace;
this.level = level;
Objects.requireNonNull(expansionListener);
toggleInsButton.addActionListener(e -> {
if (tooManyIncomingReferences) {
return;
}
expansionListener.toggleIncomingVertices(GraphCutVertex.this);
});
toggleOutsButton.addActionListener(e -> {
if (tooManyOutgoingReferences) {
return;
}
expansionListener.toggleOutgoingVertices(GraphCutVertex.this);
});
buildUi();
setTogglesVisible(false);
}
private void createPaints() {
Color vertexShapeColor = getVertexShapeColor();
Color lightColor = vertexShapeColor;
Color darkColor = Gui.darker(vertexShapeColor);
Color darkestColor = Gui.darker(darkColor);
int offset = 5 * level.getDistance();
int half = VERTEX_SHAPE_SIZE / 2;
int start = 0;
int end = half + offset;
// paint top-down: dark to light for incoming; light to dark for outgoing
inPaint = new LinearGradientPaint(new Point(0, start), new Point(0, end),
new float[] { .0f, .2f, 1f }, new Color[] { darkestColor, darkColor, lightColor });
start = half - offset; // (offset + 10);
end = VERTEX_SHAPE_SIZE;
outPaint = new LinearGradientPaint(new Point(0, start), new Point(0, end),
new float[] { .0f, .8f, 1f }, new Color[] { lightColor, darkColor, darkestColor });
}
private void buildUi() {
createPaints();
String truncated = StringUtilities.trimMiddle(getName(), MAX_NAME_LENGTH);
nameLabel.setText(truncated);
buildVertexShape();
// calculate the needed size
layeredPane = new JLayeredPane();
Border border = createDebugBorder(new LineBorder(Palette.GOLD, 1));
layeredPane.setBorder(border);
updateLayeredPaneSize();
// layout the components
addVertexShape();
addToggleButtons();
addNameLabel();
buildFullShape();
}
private Border createDebugBorder(Border border) {
if (useDebugBorders) {
return border;
}
return BorderFactory.createEmptyBorder();
}
private void buildFullShape() {
// Note: this method assumes all bounds have been set
Area parent = new Area();
Area v = new Area(vertexShape);
Area name = new Area(nameLabel.getBounds());
parent.add(v);
parent.add(name);
// for now, the buttons only appear on hover, but if we want to avoid clipping when
// painting, we need to account for them in the shape's overall bounds
Area in = new Area(toggleInsButton.getBounds());
Area out = new Area(toggleOutsButton.getBounds());
parent.add(in);
parent.add(out);
fullShape = parent;
}
private void updateLayeredPaneSize() {
//
// The overall component size is the total width and height of all components, with any
// spacing between them.
//
Dimension shapeSize = vertexImageLabel.getPreferredSize();
Dimension nameLabelSize = nameLabel.getPreferredSize();
int height = shapeSize.height + GAP + nameLabelSize.height;
Dimension insSize = toggleInsButton.getPreferredSize();
Dimension outsSize = toggleOutsButton.getPreferredSize();
int buttonWidth = Math.max(insSize.width, outsSize.width);
int offset = buttonWidth / 3; // overlap the vertex shape
int width = offset + shapeSize.width;
width = Math.max(width, nameLabelSize.width);
layeredPane.setPreferredSize(new Dimension(width, height));
}
private void buildVertexShape() {
int w = VERTEX_SHAPE_SIZE;
int h = VERTEX_SHAPE_SIZE;
Double circle = new Ellipse2D.Double(0, 0, w, h);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FcgDirection direction = level.getDirection();
if (direction.isSource()) {
g2.setColor(getVertexShapeColor());
}
else if (direction.isIn()) {
g2.setPaint(inPaint);
}
else {
g2.setPaint(outPaint);
}
g2.fill(circle);
g2.dispose();
vertexShape = circle;
compactShape = (Double) vertexShape.clone();
vertexImageLabel.setIcon(new ImageIcon(image));
Border border = createDebugBorder(new LineBorder(Palette.PINK, 1));
vertexImageLabel.setBorder(border);
}
private Color getVertexShapeColor() {
if (isInDirection() && tooManyIncomingReferences) {
return TOO_BIG_VERTEX_SHAPE_COLOR;
}
if (isOutDirection() && tooManyOutgoingReferences) {
return TOO_BIG_VERTEX_SHAPE_COLOR;
}
return DEFAULT_VERTEX_SHAPE_COLOR;
}
private boolean isInDirection() {
FcgDirection direction = level.getDirection();
boolean isIn = direction.isIn() || direction.isSource();
return isIn;
}
private boolean isOutDirection() {
FcgDirection direction = level.getDirection();
boolean isOut = direction.isOut() || direction.isSource();
return isOut;
}
private void addVertexShape() {
Dimension parentSize = layeredPane.getPreferredSize();
Dimension size = vertexImageLabel.getPreferredSize();
// centered
int x = (parentSize.width / 2) - (size.width / 2);
int y = 0;
vertexImageLabel.setBounds(x, y, size.width, size.height);
Dimension shapeSize = vertexShape.getBounds().getSize();
// setFrame() will make sure the shape's x,y values are where they need to be
// for the later 'full shape' creation
vertexShape.setFrame(x, y, shapeSize.width, shapeSize.height);
layeredPane.add(vertexImageLabel, VERTEX_SHAPE_LAYER);
}
private void addNameLabel() {
Border border = createDebugBorder(new LineBorder(Palette.GREEN, 1));
nameLabel.setBorder(border);
Rectangle parentBounds = vertexImageLabel.getBounds();
Dimension size = nameLabel.getPreferredSize();
int x = (parentBounds.x + (parentBounds.width / 2)) - (size.width / 2);
int y = parentBounds.y + parentBounds.height + GAP;
nameLabel.setBounds(x, y, size.width, size.height);
layeredPane.add(nameLabel, LABEL_LAYER);
}
private void addToggleButtons() {
// hide the button background
toggleInsButton.setBackground(Palette.NO_COLOR);
toggleOutsButton.setBackground(Palette.NO_COLOR);
// This is needed for Flat Dark theme to work correctly, due to the fact that it wants to
// paint its parent background when the button is opaque. The parent background will get
// painted over any items that lie between the button and the parent.
toggleInsButton.setOpaque(false);
toggleOutsButton.setOpaque(false);
Rectangle parentBounds = vertexImageLabel.getBounds();
Dimension size = toggleInsButton.getPreferredSize();
// upper toggle; upper-left
int x = parentBounds.x - (size.width / 3);
int y = 0;
toggleInsButton.setBounds(x, y, size.width, size.height);
layeredPane.add(toggleInsButton, TOGGLE_BUTTON_LAYER);
// lower toggle; lower-left, lined-up with the vertex shape
size = toggleOutsButton.getPreferredSize();
Dimension vertexSize = parentBounds.getSize();
y = vertexSize.height - size.height;
toggleOutsButton.setBounds(x, y, size.width, size.height);
layeredPane.add(toggleOutsButton, TOGGLE_BUTTON_LAYER);
}
public String getName() {
return namespace.getName();
}
public Namespace getNamespace() {
return namespace;
}
public GraphCutLevel getLevel() {
return level;
}
public int getDegree() {
return level.getRow();
}
public FcgDirection getDirection() {
return level.getDirection();
}
public JButton getIncomingToggleButton() {
return toggleInsButton;
}
public JButton getOutgoingToggleButton() {
return toggleOutsButton;
}
public void setIncomingExpanded(boolean setExpanded) {
validateIncomingExpandedState(setExpanded);
this.incomingExpanded = setExpanded;
toggleInsButton.setIcon(setExpanded ? COLLAPSE_ICON : EXPAND_ICON);
String hideShow = setExpanded ? "hide" : "show";
toggleInsButton.setToolTipText("Click to " + hideShow + " incoming edges");
}
private void validateOutgoingExpandedState(boolean isExpanding) {
if (isExpanding) {
if (!canExpandOutgoingReferences()) {
throw new IllegalStateException("Vertex cannot be expanded: " + this);
}
return;
}
// collapsing
if (!isOutgoingExpanded()) {
throw new IllegalStateException("Vertex cannot be collapsed: " + this);
}
}
private void validateIncomingExpandedState(boolean expanding) {
if (expanding) {
if (!canExpandIncomingReferences()) {
throw new IllegalStateException("Vertex cannot be expanded: " + this);
}
return;
}
// collapsing
if (!isIncomingExpanded()) {
throw new IllegalStateException("Vertex cannot be collapsed: " + this);
}
}
/**
* Returns true if this vertex is showing all edges in the incoming direction
*
* @return true if this vertex is showing all edges in the incoming direction
*/
public boolean isIncomingExpanded() {
return incomingExpanded;
}
/**
* Sets to true if this vertex is showing all edges in the outgoing direction
*
* @param setExpanded true if this vertex is showing all edges in the outgoing direction
*/
public void setOutgoingExpanded(boolean setExpanded) {
validateOutgoingExpandedState(setExpanded);
this.outgoingExpanded = setExpanded;
toggleOutsButton.setIcon(setExpanded ? COLLAPSE_ICON : EXPAND_ICON);
String hideShow = setExpanded ? "hide" : "show";
toggleInsButton.setToolTipText("Click to " + hideShow + " outgoing edges");
}
/**
* Returns true if this vertex is showing all edges in the outgoing direction
*
* @return true if this vertex is showing all edges in the outgoing direction
*/
public boolean isOutgoingExpanded() {
return outgoingExpanded;
}
/**
* Returns whether this vertex is fully expanded in its current direction
*
* @return whether this vertex is fully expanded in its current direction
*/
public boolean isExpanded() {
FcgDirection direction = level.getDirection();
if (direction.isSource()) {
return isIncomingExpanded() && isOutgoingExpanded();
}
if (direction.isIn()) {
return isIncomingExpanded();
}
return isOutgoingExpanded();
}
/**
* Sets whether this vertex has too many incoming references, where too many is subjectively
* defined by this class. Too many nodes in the display would ruin rendering and general
* usability.
*
* @param tooMany if there are too many references
*/
public void setTooManyIncomingReferences(boolean tooMany) {
this.tooManyIncomingReferences = tooMany;
toggleInsButton.setIcon(NOT_ALLOWED_ICON);
toggleInsButton.setToolTipText("Too many incoming references to show");
buildUi();
}
/**
* Sets whether this vertex has too many outgoing references, where too many is subjectively
* defined by this class. Too many nodes in the display would ruin rendering and general
* usability.
*
* @param tooMany if there are too many references
*/
public void setTooManyOutgoingReferences(boolean tooMany) {
this.tooManyOutgoingReferences = tooMany;
toggleOutsButton.setIcon(NOT_ALLOWED_ICON);
toggleOutsButton.setToolTipText("Too many outgoing references to show");
buildUi();
}
/**
* Returns whether this vertex has too many incoming references, where too many is subjectively
* defined by this class. Too many nodes in the display would ruin rendering and general
* usability.
*
* @return true if there are too many references
*/
public boolean hasTooManyIncomingReferences() {
return tooManyIncomingReferences;
}
/**
* Returns whether this vertex has too many outgoing references, where too many is subjectively
* defined by this class. Too many nodes in the display would ruin rendering and general
* usability.
*
* @return true if there are too many references
*/
public boolean hasTooManyOutgoingReferences() {
return tooManyOutgoingReferences;
}
/**
* Returns true if this vertex can expand itself in its current direction, or in either
* direction if this is a source vertex
*
* @return true if this vertex can be expanded
*/
public boolean canExpand() {
FcgDirection direction = level.getDirection();
if (direction.isSource()) {
return canExpandIncomingReferences() || canExpandOutgoingReferences();
}
if (direction.isIn()) {
return canExpandIncomingReferences();
}
return canExpandOutgoingReferences();
}
public boolean canExpandIncomingReferences() {
return hasIncomingReferences && !tooManyIncomingReferences && !incomingExpanded;
}
public boolean canExpandOutgoingReferences() {
return hasOutgoingReferences && !tooManyOutgoingReferences && !outgoingExpanded;
}
/**
* Sets whether this vertex has any incoming references
*
* @param hasIncoming true if this vertex has any incoming references
*/
public void setHasIncomingReferences(boolean hasIncoming) {
this.hasIncomingReferences = hasIncoming;
}
/**
* Sets whether this vertex has any outgoing references
*
* @param hasOutgoing true if this vertex has any outgoing references
*/
public void setHasOutgoingReferences(boolean hasOutgoing) {
this.hasOutgoingReferences = hasOutgoing;
}
@Override
public void setHovered(boolean hovered) {
super.setHovered(hovered);
setTogglesVisible(hovered);
}
private void setTogglesVisible(boolean visible) {
boolean isIn = isInDirection();
boolean turnOn = isIn && hasIncomingReferences && visible;
toggleInsButton.setVisible(turnOn);
boolean isOut = isOutDirection();
turnOn = isOut && hasOutgoingReferences && visible;
toggleOutsButton.setVisible(turnOn);
}
@Override
public JComponent getComponent() {
return layeredPane;
}
@Override
public Shape getCompactShape() {
return compactShape;
}
@Override
public Shape getFullShape() {
return fullShape;
}
@Override
public String toString() {
return getName();// + " @ " + level; // + " (" + System.identityHashCode(this) + ')';
}
@Override
public int hashCode() {
return Objects.hash(namespace);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
GraphCutVertex other = (GraphCutVertex) obj;
return Objects.equals(namespace, other.namespace);
}
@Override
public void dispose() {
// nothing to do
}
public long getID() {
return getNamespace().getID();
}
}

View File

@@ -0,0 +1,56 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/renderer/FcgVertexPaintTransformer.java
*/
package graphcut;
import com.google.common.base.Function;
import java.awt.Color;
import java.awt.Paint;
import ghidra.util.ColorUtils;
/**
* A class that takes a GraphCutVertex and determines which color to paint it with
*/
public class GraphCutVertexPaintTransformer implements Function<GraphCutVertex, Paint> {
private Color color;
public GraphCutVertexPaintTransformer(Color color) {
this.color = color;
}
@Override
public Paint apply(GraphCutVertex v) {
return color;
}
}

View File

@@ -0,0 +1,63 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Heavily Borrows from Ghidra file /Features Graph FunctionCalls/src/main/java/functioncalls/graph/view/FcgView.java
*/
package graphcut;
import ghidra.graph.viewer.VisualGraphView;
import ghidra.graph.viewer.options.VisualGraphOptions;
public class GraphCutView extends VisualGraphView<GraphCutVertex, GraphCutEdge, GraphCutGraph> {
private VisualGraphOptions options;
public GraphCutView(VisualGraphOptions options) {
this.options = options;
}
@Override
protected void installGraphViewer() {
GraphCutComponent component = createGraphComponent();
component.setGraphOptions(options);
setGraphComponent(component);
}
private GraphCutComponent createGraphComponent() {
GraphCutComponent component = new GraphCutComponent(getVisualGraph());
return component;
}
@Override
public GraphCutComponent getGraphComponent() {
return (GraphCutComponent) super.getGraphComponent();
}
}

View File

@@ -0,0 +1,92 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
package graphcut;
import java.util.Objects;
import ghidra.program.model.symbol.Namespace;
public class NamespaceEdge {
private Namespace start;
private Namespace end;
NamespaceEdge(Namespace start, Namespace end){
this.start = start;
this.end = end;
}
Namespace getStart() {
return start;
}
Namespace getEnd() {
return end;
}
@Override
public String toString() {
return "[" + start + ", " + end + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime*result + ((end == null) ? 0 : end.hashCode());
result = prime * result + ((start == null) ? 0 : start.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
NamespaceEdge other = (NamespaceEdge) obj;
if(!Objects.equals(end, other.end)) {
return false;
}
if(!Objects.equals(start, other.start)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,65 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
package graphcut;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.map.LazyMap;
import ghidra.program.model.symbol.Namespace;
/**
* A class to cache known namespace edges
*/
public class NamespaceEdgeCache {
//contains all known edges, even those not shown in the graph
private Map<Namespace, Set<NamespaceEdge>> allEdgesByNamespace =
LazyMap.lazyMap(new HashMap<>(), () -> new HashSet<>());
// track processed namespaces
private Set<Namespace> tracked = new HashSet<>();
public Set<NamespaceEdge> get(Namespace ns){
return allEdgesByNamespace.get(ns);
}
public boolean isTracked(Namespace ns) {
return tracked.contains(ns);
}
public void setTracked(Namespace ns) {
tracked.add(ns);
}
}

View File

@@ -0,0 +1,101 @@
/* ###
* © 2021 The Johns Hopkins University Applied Physics Laboratory LLC (JHU/APL).
* All Rights Reserved.
*
* This material may be only be used, modified, or reproduced by or for the U.S.
* Government pursuant to the license rights granted under the clauses at
* DFARS 252.227-7013/7014 or FAR 52.227-14. For any other permission, please
* contact the Office of Technology Transfer at JHU/APL.
*
* NO WARRANTY, NO LIABILITY. THIS MATERIAL IS PROVIDED “AS IS.” JHU/APL MAKES
* NO REPRESENTATION OR WARRANTY WITH RESPECT TO THE PERFORMANCE OF THE MATERIALS,
* INCLUDING THEIR SAFETY, EFFECTIVENESS, OR COMMERCIAL VIABILITY, AND DISCLAIMS
* ALL WARRANTIES IN THE MATERIAL, WHETHER EXPRESS OR IMPLIED, INCLUDING
* (BUT NOT LIMITED TO) ANY AND ALL IMPLIED WARRANTIES OF PERFORMANCE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF
* INTELLECTUAL PROPERTY OR OTHER THIRD PARTY RIGHTS. ANY USER OF THE MATERIAL
* ASSUMES THE ENTIRE RISK AND LIABILITY FOR USING THE MATERIAL. IN NO EVENT SHALL
* JHU/APL BE LIABLE TO ANY USER OF THE MATERIAL FOR ANY ACTUAL, INDIRECT,
* CONSEQUENTIAL, SPECIAL OR OTHER DAMAGES ARISING FROM THE USE OF, OR INABILITY TO
* USE, THE MATERIAL, INCLUDING, BUT NOT LIMITED TO, ANY DAMAGES FOR LOST PROFITS.
*
* HAVE A NICE DAY.
*/
/* This material is based upon work supported by the Defense Advanced Research
* Projects Agency (DARPA) and Naval Information Warfare Center Pacific (NIWC Pacific)
* under Contract Number N66001-20-C-4024.
*/
/*
* Borrows from /Features Graph FunctionCalls/src/main/java/functioncalls/plugin/ValidFcgData.java
*/
package graphcut;
import java.util.Objects;
import ghidra.graph.viewer.GraphPerspectiveInfo;
import ghidra.program.model.symbol.Namespace;
public class ValidGraphCutData implements GraphCutData {
private Namespace namespace;
private GraphCutGraph graph;
private GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> perspectiveInfo;
private NamespaceEdgeCache allEdgesByNamespace = new NamespaceEdgeCache();
ValidGraphCutData(Namespace namespace, GraphCutGraph graph){
this.namespace = Objects.requireNonNull(namespace);
this.graph = Objects.requireNonNull(graph);
}
@Override
public Namespace getNamespace() {
return namespace;
}
@Override
public boolean isNamespace(Namespace ns) {
return namespace.equals(ns);
}
@Override
public GraphCutGraph getGraph() {
return graph;
}
@Override
public NamespaceEdgeCache getNamespaceEdgeCache() {
return allEdgesByNamespace;
}
@Override
public boolean hasResults() {
return true;
}
@Override
public boolean isInitialized() {
return !graph.isEmpty();
}
@Override
public void dispose() {
graph.dispose();
}
@Override
public GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> getGraphPerspective(){
return perspectiveInfo;
}
@Override
public void setGraphPerspective( GraphPerspectiveInfo<GraphCutVertex, GraphCutEdge> info) {
this.perspectiveInfo = info;
}
}

View File

@@ -54,13 +54,13 @@ class Net(torch.nn.Module):
ReLU(),
Linear(dim, dim), ReLU(), Linear(dim, dim))
self.e_bn1 = torch.nn.LayerNorm(dim)
self.gin1 = nn.GINEConv(mlp1, train_eps=True).jittable()
self.gin1 = nn.GINEConv(mlp1, train_eps=True)
self.bn1 = nn.PairNorm() # nn.LayerNorm(dim) #torch.nn.BatchNorm1d(dim)
mlp2 = Sequential(Linear(dim, dim), ReLU(), Linear(dim, dim),
ReLU(),
Linear(dim, dim))
self.gin2 = nn.GINEConv(mlp2, train_eps=True).jittable()
self.gin2 = nn.GINEConv(mlp2, train_eps=True)
self.bn2 = nn.PairNorm() # nn.LayerNorm(dim) #torch.nn.BatchNorm1d(dim)
self.e_mlp2 = Sequential(Linear(3*dim, dim),
ReLU(),
@@ -74,7 +74,7 @@ class Net(torch.nn.Module):
Linear(dim, dim),
ReLU(),
Linear(dim, dim))
self.gin3 = nn.GINEConv(mlp3, train_eps=True).jittable()
self.gin3 = nn.GINEConv(mlp3, train_eps=True)
self.bn3 = nn.PairNorm() # nn.LayerNorm(dim)
self.e_mlp3 = Sequential(Linear(3*dim, dim),
ReLU(),
@@ -142,7 +142,8 @@ def load_gnn(model_file):
)
loaded_weights = torch.load(model_file,
map_location=torch.device('cpu'))
map_location=torch.device('cpu'),
weights_only=True)
model.load_state_dict(loaded_weights)
return model

View File

@@ -56,7 +56,7 @@ public class DeepCutAnalyzer extends AbstractAnalyzer {
private final static String OPTION_NAME_PYTHON_EXEC = "Python Executable";
private final static String OPTION_DESCRIPTION_PYTHON_EXEC = "";
private final static String OPTION_DEFAULT_PYTHON_EXEC = "/projects/venv/bin/python3";
private final static String OPTION_DEFAULT_PYTHON_EXEC = "/usr/bin/python3";
private String pythonExec = OPTION_DEFAULT_PYTHON_EXEC;
public DeepCutAnalyzer() {

View File

@@ -57,7 +57,7 @@ public class DeepCutPython {
process = runtime.exec(exec);
// Yes this is confusing. stdin is a Java OutputStream, stdin is an InputStream
// Yes this is confusing. stdin is a Java OutputStream, stdout is an InputStream
stdin = process.getOutputStream();
stdout = process.getInputStream();
stderr = process.getErrorStream();

BIN
img/graph-pic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB