From 4fe3c16d25befa8f0f3090dc5d33e9d274bf553a Mon Sep 17 00:00:00 2001 From: Teal Bauer Date: Tue, 8 Apr 2025 20:11:11 +0200 Subject: [PATCH] feat: Add decompiler configuration options Add toggleCCode, toggleSyntaxTree and setSimplificationStyle controls to the Java plugin. These allow controlling decompiler output format between C code (default) and raw decompiler output with syntax trees. Example usage: decompile_function_by_address(port=8192, address='0x1000', cCode=True) decompile_function_by_address(port=8192, address='0x1000', syntaxTree=True) --- CHANGELOG.md | 24 ++++++++++++++ bridge_mcp_hydra.py | 33 ++++++++++++++++--- .../eu/starsong/ghidra/GhydraMCPPlugin.java | 13 ++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e67229..8a81ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [Unreleased] + +### Added +- Added decompiler output controls to customize analysis results: + - Choose between clean C-like pseudocode (default) or raw decompiler output + - Toggle syntax tree visibility for detailed analysis + - Select different simplification styles for alternate views + - Useful for comparing different decompilation approaches or focusing on specific aspects of the code + + Example showing how to get raw decompiler output with syntax tree: + ```xml + + ghydra + decompile_function_by_address + + { + "address": "0x1000", + "cCode": false, + "syntaxTree": true + } + + + ``` + ## [1.4.0] - 2025-04-08 ### Added diff --git a/bridge_mcp_hydra.py b/bridge_mcp_hydra.py index a64fafe..89570fa 100644 --- a/bridge_mcp_hydra.py +++ b/bridge_mcp_hydra.py @@ -333,9 +333,24 @@ def list_classes(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = return safe_get(port, "classes", {"offset": offset, "limit": limit}) @mcp.tool() -def get_function(port: int = DEFAULT_GHIDRA_PORT, name: str = "") -> dict: - """Get decompiled code for a specific function""" - response = safe_get(port, f"functions/{quote(name)}", {}) +def get_function(port: int = DEFAULT_GHIDRA_PORT, name: str = "", cCode: bool = True, syntaxTree: bool = False, simplificationStyle: str = "normalize") -> dict: + """Get decompiled code for a specific function + + Args: + port: Ghidra instance port (default: 8192) + name: Name of the function to decompile + cCode: Whether to output C code (default: True) + syntaxTree: Whether to include syntax tree (default: False) + simplificationStyle: Decompiler analysis style (default: "normalize") + + Returns: + Dict containing function details including decompiled code + """ + response = safe_get(port, f"functions/{quote(name)}", { + "cCode": str(cCode).lower(), + "syntaxTree": str(syntaxTree).lower(), + "simplificationStyle": simplificationStyle + }) # Check if the response is a string (old format) or already a dict with proper structure if isinstance(response, dict) and "success" in response: @@ -524,17 +539,25 @@ def get_current_function(port: int = DEFAULT_GHIDRA_PORT) -> dict: # Return dict return safe_get(port, "get_current_function") @mcp.tool() -def decompile_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "") -> dict: +def decompile_function_by_address(port: int = DEFAULT_GHIDRA_PORT, address: str = "", cCode: bool = True, syntaxTree: bool = False, simplificationStyle: str = "normalize") -> dict: """Decompile a function at a specific memory address Args: port: Ghidra instance port (default: 8192) address: Memory address of the function (hex string) + cCode: Whether to output C code (default: True) + syntaxTree: Whether to include syntax tree (default: False) + simplificationStyle: Decompiler analysis style (default: "normalize") Returns: Dict containing the decompiled pseudocode in the 'result.decompilation' field """ - response = safe_get(port, "decompile_function", {"address": address}) + response = safe_get(port, "decompile_function", { + "address": address, + "cCode": str(cCode).lower(), + "syntaxTree": str(syntaxTree).lower(), + "simplificationStyle": simplificationStyle + }) # Check if the response is a string (old format) or already a dict with proper structure if isinstance(response, dict) and "success" in response: diff --git a/src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java b/src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java index 54c6e16..b783a40 100644 --- a/src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java +++ b/src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java @@ -438,6 +438,9 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin { if ("GET".equals(exchange.getRequestMethod())) { Map qparams = parseQueryParams(exchange); String address = qparams.get("address"); + boolean cCode = Boolean.parseBoolean(qparams.getOrDefault("cCode", "true")); + boolean syntaxTree = Boolean.parseBoolean(qparams.getOrDefault("syntaxTree", "false")); + String simplificationStyle = qparams.getOrDefault("simplificationStyle", "normalize"); if (address == null || address.isEmpty()) { sendErrorResponse(exchange, 400, "Address parameter is required"); @@ -472,6 +475,11 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin { DecompInterface decomp = new DecompInterface(); try { + // Set decompilation options from parameters + decomp.toggleCCode(cCode); + decomp.setSimplificationStyle(simplificationStyle); + decomp.toggleSyntaxTree(syntaxTree); + if (!decomp.openProgram(program)) { sendErrorResponse(exchange, 500, "Failed to initialize decompiler"); return; @@ -1105,6 +1113,11 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin { DecompInterface decomp = new DecompInterface(); try { + // Default to C code output and no syntax tree for better readability + decomp.toggleCCode(true); + decomp.setSimplificationStyle("normalize"); + decomp.toggleSyntaxTree(false); + if (!decomp.openProgram(program)) { resultObj.addProperty("decompilation_error", "Failed to initialize decompiler"); } else {