Created
August 18, 2025 19:57
-
-
Save nstarke/f9b35a04bc09de1d3f8c6214d2662b52 to your computer and use it in GitHub Desktop.
Ghidra Script - Display GraphML Call Graph
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Imports a .graphml file and shows it in Ghidra's graph viewer | |
# If running headless, takes first ScriptArg as the file path. | |
# If running GUI, shows a file chooser. | |
# Author: ChatGPT | |
from ghidra.service.graph import AttributedGraph, AttributedVertex, GraphDisplayBroker | |
import xml.etree.ElementTree as ET | |
from javax.swing import JFileChooser | |
import os | |
def choose_file(): | |
"""Popup file chooser to select a .graphml file""" | |
chooser = JFileChooser() | |
chooser.setDialogTitle("Select a GraphML file to import") | |
ret = chooser.showOpenDialog(None) | |
if ret == JFileChooser.APPROVE_OPTION: | |
return chooser.getSelectedFile().getAbsolutePath() | |
return None | |
# Step 1: Get the GraphML path depending on mode | |
graphml_path = None | |
if state is not None and state.isRunningHeadless(): # Headless mode | |
if len(getScriptArgs()) > 0: | |
graphml_path = getScriptArgs()[0] | |
print("[*] Headless mode: using GraphML file from ScriptArg:", graphml_path) | |
else: | |
print("[!] No ScriptArg provided in headless mode. Aborting.") | |
exit() | |
else: # GUI mode | |
graphml_path = choose_file() | |
if graphml_path is None: | |
print("No file selected. Aborting.") | |
exit() | |
if not os.path.isfile(graphml_path): | |
print("[!] Invalid file path:", graphml_path) | |
exit() | |
print("[*] Loading GraphML:", graphml_path) | |
# Step 2: Parse the GraphML XML | |
tree = ET.parse(graphml_path) | |
root = tree.getroot() | |
ns = {"g": "http://graphml.graphdrawing.org/xmlns"} | |
# Step 3: Build Ghidra AttributedGraph | |
graph = AttributedGraph("Imported GraphML") | |
vertices = {} | |
for node in root.findall(".//g:node", ns): | |
node_id = node.attrib["id"] | |
label = node_id | |
data_elem = node.find("g:data", ns) | |
if data_elem is not None and data_elem.text: | |
label = data_elem.text.strip() | |
v = graph.addVertex(node_id) | |
v.setAttribute("Label", label) | |
vertices[node_id] = v | |
for edge in root.findall(".//g:edge", ns): | |
src = edge.attrib["source"] | |
dst = edge.attrib["target"] | |
if src in vertices and dst in vertices: | |
graph.addEdge(vertices[src], vertices[dst]) | |
print("[*] Imported %d nodes and %d edges" % (len(vertices), len(graph.getEdges()))) | |
# Step 4: Display in Ghidra’s Graph Viewer (only if GUI mode) | |
if state is not None and not state.isRunningHeadless(): | |
broker = state.getTool().getService(GraphDisplayBroker) | |
display = broker.getDefaultGraphDisplay(True, monitor) | |
display.setGraph(graph, "Imported GraphML", False, monitor) | |
print("[+] Graph displayed successfully!") | |
else: | |
print("[+] Graph built in headless mode (no GUI display).") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment