-
Copy all four Java files to your Ghidra scripts directory:
- macOS/Linux:
~/.ghidra/.ghidra_<version>/scripts/
- macOS/Linux:
-
Run
TreeTableGhidraScript.javafrom the Script Manager
This extension provides a hierarchical tree table view for displaying CAPA analysis results in Ghidra.
// Display hierarchical data using JTreeTable component
// @author
// @category Data.Structures
// @keybinding
// @menupath Tools.Show TreeTable
// @toolbar
import ghidra.app.script.GhidraScript;
import javax.swing.*;
public class TreeTableGhidraScript extends GhidraScript {
@Override
protected void run() throws Exception {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Ghidra TreeTable Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(500, 400);
CapaTreeModel model = new CapaTreeModel();
JTreeTable treeTable = new JTreeTable(model);
frame.add(new JScrollPane(treeTable));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}import javax.swing.tree.DefaultMutableTreeNode;
public class CapaTreeModel extends AbstractTreeTableModel {
private String[] columnNames = {"Rule Information", "Address", "Details"};
private Class[] columnTypes = {TreeTableModel.class, String.class, String.class};
public CapaTreeModel() {
super(createData());
}
private static Object createData() {
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new CapaNodeData("get Program Files directory (2 matches)", "", "anti-analysis/anti-forensic/self-deletion"));
DefaultMutableTreeNode mammals = new DefaultMutableTreeNode(new CapaNodeData("function (FUN_100000368)", "0x100000358", ""));
DefaultMutableTreeNode and = new DefaultMutableTreeNode(new CapaNodeData("and", "", ""));
and.add(new DefaultMutableTreeNode(new CapaNodeData("api(shell32.SHGetFolderPath)", "0x100000368", "call shell32.SHGetFolderPath")));
mammals.add(and);
root.add(mammals);
DefaultMutableTreeNode basicBlock = new DefaultMutableTreeNode(new CapaNodeData("basic block @ 0x14003755D", "", ""));
DefaultMutableTreeNode and2 = new DefaultMutableTreeNode(new CapaNodeData("and", "", ""));
DefaultMutableTreeNode or1 = new DefaultMutableTreeNode(new CapaNodeData("or", "", ""));
or1.add(new DefaultMutableTreeNode(new CapaNodeData("number: 0x26 = CSIDL_PROGRAM_FILES", "0x140037560", "mov eax, 0x26")));
and2.add(or1);
basicBlock.add(and2);
DefaultMutableTreeNode or2 = new DefaultMutableTreeNode(new CapaNodeData("or", "", ""));
or2.add(new DefaultMutableTreeNode(new CapaNodeData("api: SHGetSpecialFolderPath", "0x14003756C", "call SHGetSpecialFolderPath")));
basicBlock.add(or2);
root.add(basicBlock);
return root;
}
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int column) {
return columnNames[column];
}
public Class getColumnClass(int column) {
return columnTypes[column];
}
public Object getValueAt(Object node, int column) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
CapaNodeData data = (CapaNodeData) treeNode.getUserObject();
switch(column) {
case 0: return data;
case 1: return data.getType();
case 2: return data.getDetails();
default: return null;
}
}
public Object getChild(Object parent, int index) {
return ((DefaultMutableTreeNode) parent).getChildAt(index);
}
public int getChildCount(Object parent) {
return ((DefaultMutableTreeNode) parent).getChildCount();
}
}public class CapaNodeData {
private String name;
private String type;
private String details;
public CapaNodeData(String name, String type, String details) {
this.name = name;
this.type = type;
this.details = details;
}
public String getName() { return name; }
public String getType() { return type; }
public String getDetails() { return details; }
@Override
public String toString() {
return name;
}
}import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.Graphics;
public class JTreeTable extends JTable {
protected TreeTableCellRenderer tree;
public JTreeTable(TreeTableModel treeTableModel) {
super();
tree = new TreeTableCellRenderer(treeTableModel);
super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
tree.setSelectionModel(new DefaultTreeSelectionModel() {
{ setSelectionModel(listSelectionModel); }
});
tree.setRowHeight(getRowHeight());
setDefaultRenderer(TreeTableModel.class, tree);
setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
setShowGrid(false);
setIntercellSpacing(new Dimension(0, 0));
DefaultTableCellRenderer headerRenderer = new DefaultTableCellRenderer();
headerRenderer.setHorizontalAlignment(JLabel.LEFT);
getTableHeader().setDefaultRenderer(headerRenderer);
}
public int getEditingRow() {
return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 : editingRow;
}
public class TreeTableCellRenderer extends JTree implements TableCellRenderer {
protected int visibleRow;
public TreeTableCellRenderer(TreeModel model) {
super(model);
}
public void setBounds(int x, int y, int w, int h) {
super.setBounds(x, 0, w, JTreeTable.this.getHeight());
}
public void paint(Graphics g) {
g.translate(0, -visibleRow * getRowHeight());
super.paint(g);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(isSelected) setBackground(table.getSelectionBackground());
else setBackground(table.getBackground());
visibleRow = row;
return this;
}
}
public class TreeTableCellEditor extends AbstractCellEditor implements TableCellEditor {
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int r, int c) {
return tree;
}
}
}