Skip to main content
Glama
ProgramManagerService.java11.7 kB
package com.ghidramcp.services; import ghidra.app.services.ProgramManager; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFolder; import ghidra.framework.model.Project; import ghidra.framework.model.ProjectData; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.Program; import ghidra.util.Msg; import ghidra.util.task.TaskMonitor; import ghidra.app.util.importer.AutoImporter; import ghidra.app.util.importer.MessageLog; import ghidra.app.util.opinion.LoadResults; import javax.swing.SwingUtilities; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * Service for managing programs in Ghidra - listing, switching, and importing. * Used by test infrastructure for multi-binary support. */ public class ProgramManagerService { private final PluginTool tool; public ProgramManagerService(PluginTool tool) { this.tool = tool; } /** * Get the ProgramManager service */ private ProgramManager getProgramManager() { return tool.getService(ProgramManager.class); } /** * Get the current project */ private Project getProject() { return tool.getProject(); } /** * List all programs available in the current project * @return JSON array of program info */ public String listPrograms() { Project project = getProject(); if (project == null) { return "{\"error\": \"No project open\"}"; } ProjectData projectData = project.getProjectData(); DomainFolder rootFolder = projectData.getRootFolder(); List<String> programs = new ArrayList<>(); collectPrograms(rootFolder, programs, ""); ProgramManager pm = getProgramManager(); Program currentProgram = pm != null ? pm.getCurrentProgram() : null; String currentName = currentProgram != null ? currentProgram.getName() : null; StringBuilder json = new StringBuilder(); json.append("{\"programs\": ["); for (int i = 0; i < programs.size(); i++) { if (i > 0) json.append(", "); String progName = programs.get(i); boolean isCurrent = progName.equals(currentName); json.append(String.format("{\"name\": \"%s\", \"current\": %s}", escapeJson(progName), isCurrent)); } json.append("], \"current\": "); json.append(currentName != null ? "\"" + escapeJson(currentName) + "\"" : "null"); json.append("}"); return json.toString(); } /** * Recursively collect program names from folders */ private void collectPrograms(DomainFolder folder, List<String> programs, String prefix) { // Get files in this folder for (DomainFile file : folder.getFiles()) { String contentType = file.getContentType(); if ("Program".equals(contentType)) { programs.add(prefix + file.getName()); } } // Recurse into subfolders for (DomainFolder subfolder : folder.getFolders()) { collectPrograms(subfolder, programs, prefix + subfolder.getName() + "/"); } } /** * Switch to a different program by name * @param programName Name of the program to switch to * @return JSON result */ public String switchProgram(String programName) { if (programName == null || programName.isEmpty()) { return "{\"error\": \"Program name is required\"}"; } Project project = getProject(); if (project == null) { return "{\"error\": \"No project open\"}"; } ProgramManager pm = getProgramManager(); if (pm == null) { return "{\"error\": \"ProgramManager service not available\"}"; } // Check if already current Program currentProgram = pm.getCurrentProgram(); if (currentProgram != null && currentProgram.getName().equals(programName)) { return "{\"success\": true, \"message\": \"Already on program: " + escapeJson(programName) + "\"}"; } // Find the program in the project ProjectData projectData = project.getProjectData(); DomainFile domainFile = findProgramFile(projectData.getRootFolder(), programName); if (domainFile == null) { return "{\"error\": \"Program not found: " + escapeJson(programName) + "\"}"; } try { // Open the program (this must be done on Swing thread) AtomicReference<String> result = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); SwingUtilities.invokeLater(() -> { try { // Open the domain file as a program Program program = (Program) domainFile.getDomainObject( this, true, false, TaskMonitor.DUMMY); if (program != null) { pm.openProgram(program); pm.setCurrentProgram(program); result.set("{\"success\": true, \"message\": \"Switched to: " + escapeJson(programName) + "\"}"); } else { result.set("{\"error\": \"Failed to open program: " + escapeJson(programName) + "\"}"); } } catch (Exception e) { result.set("{\"error\": \"Failed to switch program: " + escapeJson(e.getMessage()) + "\"}"); } finally { latch.countDown(); } }); // Wait for the switch to complete if (!latch.await(30, TimeUnit.SECONDS)) { return "{\"error\": \"Timeout waiting for program switch\"}"; } return result.get(); } catch (Exception e) { Msg.error(this, "Failed to switch program", e); return "{\"error\": \"Failed to switch program: " + escapeJson(e.getMessage()) + "\"}"; } } /** * Find a domain file by program name */ private DomainFile findProgramFile(DomainFolder folder, String programName) { // Check files in this folder for (DomainFile file : folder.getFiles()) { if (file.getName().equals(programName) && "Program".equals(file.getContentType())) { return file; } } // Recurse into subfolders for (DomainFolder subfolder : folder.getFolders()) { DomainFile found = findProgramFile(subfolder, programName); if (found != null) { return found; } } return null; } /** * Import a binary file into the project * @param filePath Path to the binary file * @return JSON result with program name */ public String importBinary(String filePath) { if (filePath == null || filePath.isEmpty()) { return "{\"error\": \"File path is required\"}"; } File file = new File(filePath); if (!file.exists()) { return "{\"error\": \"File not found: " + escapeJson(filePath) + "\"}"; } Project project = getProject(); if (project == null) { return "{\"error\": \"No project open\"}"; } ProgramManager pm = getProgramManager(); if (pm == null) { return "{\"error\": \"ProgramManager service not available\"}"; } // Check if already imported String programName = file.getName(); ProjectData projectData = project.getProjectData(); DomainFile existingFile = findProgramFile(projectData.getRootFolder(), programName); if (existingFile != null) { return "{\"success\": true, \"message\": \"Program already imported\", \"program_name\": \"" + escapeJson(programName) + "\", \"already_exists\": true}"; } try { // Import must be done on Swing thread AtomicReference<String> result = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); SwingUtilities.invokeLater(() -> { LoadResults<Program> loadResults = null; try { MessageLog messageLog = new MessageLog(); DomainFolder rootFolder = projectData.getRootFolder(); // Use AutoImporter to import the file loadResults = AutoImporter.importByUsingBestGuess( file, project, rootFolder.getPathname(), this, messageLog, TaskMonitor.DUMMY); if (loadResults != null && loadResults.getPrimaryDomainObject() != null) { Program importedProgram = loadResults.getPrimaryDomainObject(); String importedName = importedProgram.getName(); result.set("{\"success\": true, \"message\": \"Imported successfully\", \"program_name\": \"" + escapeJson(importedName) + "\"}"); } else { String errors = messageLog.toString(); if (errors.isEmpty()) { errors = "Unknown import error"; } result.set("{\"error\": \"Import failed: " + escapeJson(errors) + "\"}"); } } catch (Exception e) { Msg.error(this, "Import failed", e); result.set("{\"error\": \"Import failed: " + escapeJson(e.getMessage()) + "\"}"); } finally { // Always release LoadResults to free resources if (loadResults != null) { loadResults.release(this); } latch.countDown(); } }); // Wait for import to complete (can take a while for large binaries) if (!latch.await(120, TimeUnit.SECONDS)) { return "{\"error\": \"Timeout waiting for import\"}"; } return result.get(); } catch (Exception e) { Msg.error(this, "Failed to import binary", e); return "{\"error\": \"Failed to import binary: " + escapeJson(e.getMessage()) + "\"}"; } } /** * Get the name of the currently active program * @return JSON with current program info */ public String getCurrentProgramInfo() { ProgramManager pm = getProgramManager(); if (pm == null) { return "{\"error\": \"ProgramManager service not available\"}"; } Program currentProgram = pm.getCurrentProgram(); if (currentProgram == null) { return "{\"program_name\": null, \"loaded\": false}"; } return "{\"program_name\": \"" + escapeJson(currentProgram.getName()) + "\", \"loaded\": true, \"path\": \"" + escapeJson(currentProgram.getExecutablePath()) + "\"}"; } /** * Escape a string for JSON */ private String escapeJson(String s) { if (s == null) return ""; return s.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } }

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/HK47196/GhidraMCP'

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