From 51573544a4921a7693cbb89d5f2199342c409717 Mon Sep 17 00:00:00 2001 From: Linus Henke Date: Mon, 2 Nov 2026 12:58:19 +0100 Subject: [PATCH] Really enable Ghidra's Developer plugins in both tools A bare only adds the package's RELEASED plugins, so half of them (the STABLE ones) were missing and Configure showed Developer unticked. The project window wasn't configured at all. - CodeBrowser: insert ghidra/codebrowser-developer.xml, which lists the STABLE plugins explicitly, into the default CodeBrowser.tool in Public_Release.jar. - Project window: it has no default config to patch, so ship ghidra/FrontEndTool.xml in the user settings with its three Developer plugins. - Pre-creating the settings dir makes Ghidra show its user agreement; ghidra/preferences sets USER_AGREEMENT=ACCEPT, the value Ghidra writes by itself on a fresh start. Verified by closing Ghidra cleanly: it rewrites FrontEndTool.xml from the loaded plugins and kept all three Developer plugins. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 2 +- Dockerfile | 27 ++++++++++++++++++++++----- ghidra/FrontEndTool.xml | 27 +++++++++++++++++++++++++++ ghidra/codebrowser-developer.xml | 8 ++++++++ ghidra/preferences | 4 ++++ 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 ghidra/FrontEndTool.xml create mode 100644 ghidra/codebrowser-developer.xml create mode 100644 ghidra/preferences diff --git a/CLAUDE.md b/CLAUDE.md index 84da884..6599774 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with C ## Architecture -- **Claude Desktop and Ghidra share one container** (the root `Dockerfile`, started by `run.sh`), at the user's request. Ghidra is the official GitHub release (version, build date and SHA-256 are build args), runs on Ubuntu's `openjdk-25-jdk`, lives in `/opt/ghidra` and starts together with Claude Desktop. The CodeBrowser has the **Developer** plugin package enabled: the build patches `defaultTools/CodeBrowser.tool` inside `Public_Release.jar` (user settings reset every start, so patching user config wouldn't stick). Further services such as the RE MCP server may still get their own containers. +- **Claude Desktop and Ghidra share one container** (the root `Dockerfile`, started by `run.sh`), at the user's request. Ghidra is the official GitHub release (version, build date and SHA-256 are build args), runs on Ubuntu's `openjdk-25-jdk`, lives in `/opt/ghidra` and starts together with Claude Desktop. The **Developer** plugin package is enabled in both the project window and the CodeBrowser. A bare `` only adds the package's RELEASED plugins, so the STABLE ones are listed with ``. CodeBrowser: the build inserts `ghidra/codebrowser-developer.xml` into `defaultTools/CodeBrowser.tool` inside `Public_Release.jar`. Project window: it has no default to patch, so `ghidra/FrontEndTool.xml` is copied into the user settings, along with `ghidra/preferences` (`USER_AGREEMENT=ACCEPT`, because a pre-created settings dir otherwise makes Ghidra ask for its user agreement). To check a config really loaded, close Ghidra cleanly and see whether the plugins survive in the file it rewrites; Ghidra drops what it didn't load. - **Shared workspace volume.** Every container mounts the workspace at `/workspace`, so a file path means the same thing to Claude, Ghidra and the RE MCP. `run.sh` asks for the host folder on each start in the desktop's folder picker (`zenity`, or `kdialog` on KDE; terminal prompt if neither exists), opening at the last folder used, else `./workspace`. Skip it with `--workspace DIR` or `WORKSPACE=DIR`. It refuses `/` and `$HOME`. The image symlinks `~/workspace` to `/workspace` for Claude Desktop's file picker. - **GUI forwarding.** Claude Desktop and Ghidra are GUI apps. Default to X11 forwarding (`/tmp/.X11-unix` + `DISPLAY`). Support Wayland through XWayland. Keep a noVNC/VNC fallback in mind for headless hosts. Never solve display access with a blanket `xhost +`. Scope it (`xhost +local:docker` or `+SI:localuser:...`). - **GhidraMCP wiring.** GhidraMCP has two parts: diff --git a/Dockerfile b/Dockerfile index ba8bc15..8456e26 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,21 +56,38 @@ ENV GHIDRA_INSTALL_DIR=/opt/ghidra # CodeBrowser. Ghidra copies its default tools into the user's settings on # first start, and settings are reset on every container start, so the # default CodeBrowser.tool inside the jar is patched instead of user config. -# The build fails if the line could not be inserted. +# A bare only adds the package's RELEASED +# plugins; the snippet also lists the STABLE ones. The build fails if the +# snippet could not be inserted. +COPY ghidra/codebrowser-developer.xml /tmp/codebrowser-developer.xml RUN set -e; cd /tmp \ && jar_file=/opt/ghidra/Ghidra/Configurations/Public_Release/lib/Public_Release.jar \ && unzip -q "$jar_file" defaultTools/CodeBrowser.tool \ - && sed -i '0,/\n ' defaultTools/CodeBrowser.tool \ + && awk 'FNR==NR { snip = snip $0 "\n"; next } !done && / /tmp/CodeBrowser.tool \ + && mv /tmp/CodeBrowser.tool defaultTools/CodeBrowser.tool \ + && grep -q 'FidDebugPlugin' defaultTools/CodeBrowser.tool \ && jar uf "$jar_file" defaultTools/CodeBrowser.tool \ - && rm -r /tmp/defaultTools + && rm -r /tmp/defaultTools /tmp/codebrowser-developer.xml # Ubuntu 24.04 ships a default "ubuntu" user on UID 1000; replace it. RUN if id ubuntu >/dev/null 2>&1; then userdel -r ubuntu; fi \ && groupadd -g "${USER_GID}" "${USER_NAME}" \ && useradd -m -u "${USER_UID}" -g "${USER_GID}" -s /bin/bash "${USER_NAME}" \ && install -d -o "${USER_UID}" -g "${USER_GID}" /workspace \ - && ln -s /workspace "/home/${USER_NAME}/workspace" + && ln -s /workspace "/home/${USER_NAME}/workspace" \ + && install -d -o "${USER_UID}" -g "${USER_GID}" \ + "/home/${USER_NAME}/.config" \ + "/home/${USER_NAME}/.config/ghidra" \ + "/home/${USER_NAME}/.config/ghidra/ghidra_${GHIDRA_VERSION}_PUBLIC" + +# Ghidra's project window has no default config to patch; it builds one in +# code and saves it on exit. So a starting FrontEndTool.xml with the Developer +# plugins is put in the user settings. Pre-creating the settings dir makes +# Ghidra ask for its user agreement, which it otherwise accepts by itself on +# a fresh start, so preferences carries the same USER_AGREEMENT=ACCEPT. +COPY --chown=${USER_UID}:${USER_GID} ghidra/FrontEndTool.xml ghidra/preferences \ + /home/${USER_NAME}/.config/ghidra/ghidra_${GHIDRA_VERSION}_PUBLIC/ COPY --chmod=755 launch-apps.sh /usr/local/bin/launch-apps diff --git a/ghidra/FrontEndTool.xml b/ghidra/FrontEndTool.xml new file mode 100644 index 0000000..99e4f5c --- /dev/null +++ b/ghidra/FrontEndTool.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ghidra/codebrowser-developer.xml b/ghidra/codebrowser-developer.xml new file mode 100644 index 0000000..b563c45 --- /dev/null +++ b/ghidra/codebrowser-developer.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/ghidra/preferences b/ghidra/preferences new file mode 100644 index 0000000..54d432a --- /dev/null +++ b/ghidra/preferences @@ -0,0 +1,4 @@ +# Same value Ghidra writes on its own on a fresh start (no settings dir). +# Needed because the image pre-creates the settings dir for FrontEndTool.xml, +# which otherwise makes Ghidra show its user agreement dialog every start. +USER_AGREEMENT=ACCEPT