Skip to content

Instantly share code, notes, and snippets.

@nstarke
Created August 18, 2025 19:57
Show Gist options
  • Save nstarke/f9b35a04bc09de1d3f8c6214d2662b52 to your computer and use it in GitHub Desktop.
Save nstarke/f9b35a04bc09de1d3f8c6214d2662b52 to your computer and use it in GitHub Desktop.
Ghidra Script - Display GraphML Call Graph
# 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