Skip to main content
Glama
GhidraMCPPlugin.java10.6 kB
package com.lauriewired; import com.lauriewired.handlers.Handler; import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.PluginTool; import ghidra.app.plugin.PluginCategoryNames; import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.util.Msg; import ghidra.framework.options.Options; import com.sun.net.httpserver.HttpServer; import org.reflections.Reflections; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.InetSocketAddress; import java.util.*; /** * GhidraMCP Plugin - Model Context Protocol Server for Ghidra * * This plugin creates an HTTP server that exposes Ghidra's analysis * capabilities * through a RESTful API, enabling AI language models to autonomously perform * reverse engineering tasks. The plugin integrates with the CodeBrowser tool * and provides comprehensive access to: * * <ul> * <li>Function decompilation and analysis</li> * <li>Symbol and variable management</li> * <li>Memory and data structure examination</li> * <li>Cross-reference analysis</li> * <li>Binary annotation and commenting</li> * </ul> * * <h3>Server Lifecycle</h3> * The HTTP server automatically starts when the plugin is enabled in * CodeBrowser * with an active program loaded. The server runs on a configurable port * (default: 8080) * and remains active while the CodeBrowser session continues. * * <h3>API Endpoints</h3> * The plugin exposes over 20 REST endpoints for comprehensive binary analysis: * <ul> * <li><code>/methods</code> - List all functions with pagination</li> * <li><code>/decompile</code> - Decompile functions by name or address</li> * <li><code>/renameFunction</code> - Rename functions and variables</li> * <li><code>/xrefs_to</code> - Analyze cross-references</li> * <li><code>/strings</code> - Extract and filter string data</li> * </ul> * * <h3>Thread Safety</h3> * All Ghidra API interactions are properly synchronized using * SwingUtilities.invokeAndWait() * to ensure thread safety with Ghidra's event dispatch thread. * * @author LaurieWired * @version 2.0 * @since Ghidra 11.3.2 * @see ghidra.framework.plugintool.Plugin * @see com.sun.net.httpserver.HttpServer */ @PluginInfo(status = PluginStatus.RELEASED, packageName = ghidra.app.DeveloperPluginPackage.NAME, category = PluginCategoryNames.ANALYSIS, shortDescription = "HTTP server plugin", description = "Starts an embedded HTTP server to expose program data. Port configurable via Tool Options.") public class GhidraMCPPlugin extends Plugin { /** The embedded HTTP server instance that handles all API requests */ private HttpServer server; /** Configuration category name for tool options */ private static final String OPTION_CATEGORY_NAME = "GhidraMCP HTTP Server"; /** Configuration option name for the server address setting */ private static final String ADDRESS_OPTION_NAME = "Server Address"; /** Default address for the HTTP server */ private static final String DEFAULT_ADDRESS = "127.0.0.1"; /** Configuration option name for the server port setting */ private static final String PORT_OPTION_NAME = "Server Port"; /** Configuration option name for the decompile timeout setting */ private static final String DECOMPILE_TIMEOUT_OPTION_NAME = "Decompile Timeout"; /** Default port number for the HTTP server (8080) */ private static final int DEFAULT_PORT = 8080; /** Default decompile timeout in seconds */ private static final int DEFAULT_DECOMPILE_TIMEOUT = 30; /** HashMap to store all registered API routes */ private static final HashMap<String, Handler> routes = new HashMap<>(); /** The timeout for decompilation requests in seconds */ private int decompileTimeout; /** * Constructs a new GhidraMCP plugin instance and initializes the HTTP server. * * This constructor: * <ol> * <li>Registers the port configuration option in Ghidra's tool options</li> * <li>Starts the embedded HTTP server on the configured port</li> * <li>Creates all REST API endpoint handlers</li> * </ol> * * The server will only function properly when: * <ul> * <li>A program is loaded in the current CodeBrowser session</li> * <li>The plugin is enabled in the Developer tools configuration</li> * </ul> * * @param tool The Ghidra PluginTool instance that hosts this plugin * @throws IllegalStateException if the HTTP server fails to start * @see #startServer() */ public GhidraMCPPlugin(PluginTool tool) { super(tool); Msg.info(this, "GhidraMCPPlugin loading..."); // Register the configuration option Options options = tool.getOptions(OPTION_CATEGORY_NAME); options.registerOption(ADDRESS_OPTION_NAME, DEFAULT_ADDRESS, null, // No help location for now "The network address the embedded HTTP server will listen on. " + "Requires Ghidra restart or plugin reload to take effect after changing."); options.registerOption(PORT_OPTION_NAME, DEFAULT_PORT, null, // No help location for now "The network port number the embedded HTTP server will listen on. " + "Requires Ghidra restart or plugin reload to take effect after changing."); options.registerOption(DECOMPILE_TIMEOUT_OPTION_NAME, DEFAULT_DECOMPILE_TIMEOUT, null, "Decompilation timeout. " + "Requires Ghidra restart or plugin reload to take effect after changing."); try { startServer(); } catch (IOException e) { Msg.error(this, "Failed to start HTTP server", e); } Msg.info(this, "GhidraMCPPlugin loaded!"); } /** * Initializes and starts the embedded HTTP server with all API endpoints. * * This method creates an HTTP server instance and registers handlers for all * supported REST API endpoints. The server supports: * * <h4>Function Analysis Endpoints:</h4> * <ul> * <li><code>GET /methods</code> - List functions with pagination</li> * <li><code>POST /decompile</code> - Decompile function by name</li> * <li><code>GET /decompile_function?address=0x...</code> - Decompile by * address</li> * <li><code>GET /disassemble_function?address=0x...</code> - Get assembly * listing</li> * </ul> * * <h4>Symbol Management Endpoints:</h4> * <ul> * <li><code>POST /renameFunction</code> - Rename functions</li> * <li><code>POST /renameVariable</code> - Rename local variables</li> * <li><code>POST /set_function_prototype</code> - Set function signatures</li> * </ul> * * <h4>Analysis and Reference Endpoints:</h4> * <ul> * <li><code>GET /xrefs_to?address=0x...</code> - Find references to * address</li> * <li><code>GET /xrefs_from?address=0x...</code> - Find references from * address</li> * <li><code>GET /strings</code> - List string data with filtering</li> * </ul> * * <h4>Commenting and Annotation:</h4> * <ul> * <li><code>POST /set_decompiler_comment</code> - Add pseudocode comments</li> * <li><code>POST /set_disassembly_comment</code> - Add assembly comments</li> * </ul> * * The server runs on a separate thread to avoid blocking Ghidra's UI thread. * All endpoints return plain text responses with UTF-8 encoding. * * @throws IOException if the server cannot bind to the configured port * @see #sendResponse(HttpExchange, String) * @see #parseQueryParams(HttpExchange) */ private void startServer() throws IOException { // Read the configured port Options options = tool.getOptions(OPTION_CATEGORY_NAME); String listenAddress = options.getString(ADDRESS_OPTION_NAME, DEFAULT_ADDRESS); int port = options.getInt(PORT_OPTION_NAME, DEFAULT_PORT); // Stop existing server if running (e.g., if plugin is reloaded) if (server != null) { Msg.info(this, "Stopping existing HTTP server before starting new one."); server.stop(0); server = null; } InetSocketAddress inetAddress = new InetSocketAddress(listenAddress, port); if (inetAddress.isUnresolved()) { Msg.error(this, "Failed to resolve listen address."); return; } server = HttpServer.create(inetAddress, 0); Reflections reflections = new Reflections("com.lauriewired.handlers"); Set<Class<? extends Handler>> subclasses = reflections.getSubTypesOf(Handler.class); for (Class<?> clazz : subclasses) { System.out.println(clazz.getName()); try { Constructor<?> constructor = clazz.getConstructor(PluginTool.class); Handler handler = (Handler) constructor.newInstance(tool); if (routes.containsKey(handler.getPath())) { Msg.error(this, "Handler class " + clazz.getName() + " already registered, skipped."); continue; } routes.put(handler.getPath(), handler); server.createContext(handler.getPath(), exchange -> { try { handler.handle(exchange); } catch (Exception e) { throw new RuntimeException(e); } }); } catch (NoSuchMethodException e) { Msg.error(this, "Handler class " + clazz.getName() + " doesn't have constructor xxx(PluginTool tool), skipped."); } catch (Exception e) { e.printStackTrace(); } } server.setExecutor(null); new Thread(() -> { try { server.start(); Msg.info(this, "GhidraMCP HTTP server started on port " + port); } catch (Exception e) { Msg.error(this, "Failed to start HTTP server on port " + port + ". Port might be in use.", e); server = null; // Ensure server isn't considered running } }, "GhidraMCP-HTTP-Server").start(); } /** * Cleanly shuts down the HTTP server and releases plugin resources. * * This method is automatically called by Ghidra when: * <ul> * <li>The plugin is disabled in the CodeBrowser configuration</li> * <li>The CodeBrowser tool is closed</li> * <li>Ghidra is shutting down</li> * <li>The plugin is being reloaded</li> * </ul> * * <b>Shutdown Process:</b> * <ol> * <li>Stops the HTTP server with a 1-second grace period for active * connections</li> * <li>Nullifies the server reference to prevent further use</li> * <li>Calls the parent dispose method to clean up plugin infrastructure</li> * </ol> * * <b>Thread Safety:</b> This method can be called from any thread and safely * handles concurrent access to the server instance. * * @see HttpServer#stop(int) * @see Plugin#dispose() */ @Override public void dispose() { if (server != null) { Msg.info(this, "Stopping GhidraMCP HTTP server..."); server.stop(1); // Stop with a small delay (e.g., 1 second) for connections to finish server = null; // Nullify the reference Msg.info(this, "GhidraMCP HTTP server stopped."); } super.dispose(); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/harry-cysic/GhidraMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server