Add unblob so REA can extract firmware
unblob 26.6.4 (the version REA is verified with) is installed from its PyPI wheel into /opt/unblob, with dependencies pinned to its uv.lock at the release commit. A wrapper puts that venv first on PATH, and REA_UNBLOB_COMMAND points REA's extract_firmware at it. Its extractors follow upstream's install-deps.sh: Ubuntu packages, sasquatch v4.5.1-6 (also used by binwalk) and onekey-sec's e2fsprogs fork, whose debugfs rdump options the ext2/3/4 handler needs. The build checks that unblob finds every extractor. The uv.lock converter moves to scripts/ and skips the locked project itself generically, since ghidra-mcp and unblob both use it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ The goal is that `docker compose up` gives you a working setup where Claude Desk
|
||||
|
||||
## Status
|
||||
|
||||
Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with Claude Desktop, Ghidra (plus GhidraMCP and an ESP32 loader), binwalk, esptool and REA, started by `run.sh` as two containers (Claude Desktop + Ghidra, and the REA MCP server); `README.md` covers usage; Claude Desktop's MCP config is `claude-desktop/claude_desktop_config.json`; Ghidra config files are in `ghidra/`. No compose setup yet. The layout below is the **intended** structure. Update this file when the real structure differs.
|
||||
Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with Claude Desktop, Ghidra (plus GhidraMCP and an ESP32 loader), binwalk, unblob, esptool and REA, started by `run.sh` as two containers (Claude Desktop + Ghidra, and the REA MCP server); `README.md` covers usage; Claude Desktop's MCP config is `claude-desktop/claude_desktop_config.json`; Ghidra config files are in `ghidra/`. No compose setup yet. The layout below is the **intended** structure. Update this file when the real structure differs.
|
||||
|
||||
## Intended layout
|
||||
|
||||
@@ -42,13 +42,13 @@ Early stage. What exists: a single image (`Dockerfile`, `launch-apps.sh`) with C
|
||||
- **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.** LaurieWired's GhidraMCP (https://github.com/LaurieWired/GhidraMCP), release 2.0, installed from the official release zip (SHA-256 checked). It has two parts:
|
||||
- a Ghidra extension, unpacked into `/opt/ghidra/Ghidra/Extensions/GhidraMCP`. Its `GhidraMCPPlugin` is a RELEASED plugin in the Developer package, so the CodeBrowser config loads it. While a CodeBrowser is open it serves an HTTP backend on `127.0.0.1:8080` (port: *Edit → Tool Options → GhidraMCP HTTP Server*).
|
||||
- the `ghidra-mcp` Python MCP server (2.0 replaced `bridge_mcp_ghidra.py`), installed from the release wheel into the venv `/opt/ghidra-mcp`. Its dependencies are pinned to GhidraMCP's `uv.lock` at the release commit via `ghidra/uvlock-to-constraints.py`. CLI: `ghidra-mcp --ghidra-server http://127.0.0.1:8080/` (stdio by default; also `--transport streamable-http`, `--headless`).
|
||||
- the `ghidra-mcp` Python MCP server (2.0 replaced `bridge_mcp_ghidra.py`), installed from the release wheel into the venv `/opt/ghidra-mcp`. Its dependencies are pinned to GhidraMCP's `uv.lock` at the release commit via `scripts/uvlock-to-constraints.py`. CLI: `ghidra-mcp --ghidra-server http://127.0.0.1:8080/` (stdio by default; also `--transport streamable-http`, `--headless`).
|
||||
|
||||
A program must be open in the CodeBrowser for the tools to return data. Claude Desktop only starts MCP servers after sign-in.
|
||||
- **ESP32 Flash Loader** (https://github.com/dynacylabs/ghidra-esp32-flash-loader): a Ghidra loader ("ESP32 Flash Image" in *File → Import File*) for ESP32 app images and flash dumps. It reads the partition table at `0x8000`, maps segments to their addresses, adds peripherals from Espressif's SVD files and loads the chip's ROM ELF for symbols. A flash dump is only recognized when it **starts with the bootloader** (ESP32-S/C layouts). Original-ESP32 dumps (bootloader at `0x1000`) are rejected, and trimming the first `0x1000` bytes doesn't help: the loader then finds no partition table at `0x8000`, loads only the bootloader and throws a NullPointerException while still reporting success. Workaround: cut the app partition out of the dump (offsets from the partition table) and import it as an app image. Its tags ship no binaries and the older forks (tslater2006, Ebiroll, saibotk v1.1.0) don't compile against Ghidra 12, so the `esp32-loader-build` stage builds dynacylabs' master at a pinned commit, with its `data/svd` and `ghidra_scripts` submodules fetched over HTTPS at pinned commits and Gradle checked against gradle.org's SHA-256. It is unpacked into `/opt/ghidra/Ghidra/Extensions/esp32`; loaders need no tool config.
|
||||
- **REA ("Reverse Engineer Anything")**: https://github.com/morluto/rea (MIT, TypeScript), the `rea-agents` npm package, pinned to **6.8.0** with the tarball checked against the registry's integrity hash; its dependencies are locked by the `npm-shrinkwrap.json` it ships. It needs Node.js ≥24.22.1 (official tarball, SHA-256 checked, in `/opt/node`) and procps' `ps`. It uses Ghidra 12.1.x headless through `GHIDRA_INSTALL_DIR` (its own temporary projects, separate from the GUI's) and binwalk 3.1.x through `REA_BINWALK_COMMAND`; Hopper, IDA, Kuna, JADX, unblob, tshark, pwntools and a browser are not installed, and frida (optional, runtime instrumentation) stays unbuilt because npm blocks its install script. Check with `rea doctor --provider ghidra --json`. REA's bundled skill (`reverse-engineer-anything`) is copied from the package into `~/.claude/skills` in the image, for Claude Code sessions in Claude Desktop's Code tab (they load user skills from there). Claude Desktop's chat doesn't read that folder: its skills come from the account (Settings → Capabilities → Skills) and are cached by the app.
|
||||
- **REA ("Reverse Engineer Anything")**: https://github.com/morluto/rea (MIT, TypeScript), the `rea-agents` npm package, pinned to **6.8.0** with the tarball checked against the registry's integrity hash; its dependencies are locked by the `npm-shrinkwrap.json` it ships. It needs Node.js ≥24.22.1 (official tarball, SHA-256 checked, in `/opt/node`) and procps' `ps`. It uses Ghidra 12.1.x headless through `GHIDRA_INSTALL_DIR` (its own temporary projects, separate from the GUI's) binwalk 3.1.x through `REA_BINWALK_COMMAND` (`inspect_firmware_regions`) and unblob 26.6.x through `REA_UNBLOB_COMMAND` (`extract_firmware`); Hopper, IDA, Kuna, JADX, tshark, pwntools and a browser are not installed, and frida (optional, runtime instrumentation) stays unbuilt because npm blocks its install script. Check with `rea doctor --provider ghidra --json`. REA's bundled skill (`reverse-engineer-anything`) is copied from the package into `~/.claude/skills` in the image, for Claude Code sessions in Claude Desktop's Code tab (they load user skills from there). Claude Desktop's chat doesn't read that folder: its skills come from the account (Settings → Capabilities → Skills) and are cached by the app.
|
||||
Some REA tools run the target (process capture, runtime/Electron observation), so it does **not** run in the Claude Desktop container. `run.sh` starts a second container from the same image (`claude-re-mcp`): `rea mcp --http --host 0.0.0.0 --port 8765`, only the workspace mounted, no display, `--cap-drop ALL`, `no-new-privileges`, and only on the `--internal` network `claude-re-setup-internal` (no route out) under the alias `rea`. The Claude Desktop container is created on the default network plus that internal one, so it can sign in and reach `http://rea:8765/mcp`. REA's HTTP endpoint has no authentication and serves one client session at a time; the internal network is what limits who can reach it. A detached `docker wait` started by `run.sh` stops REA when Claude Desktop's container ends.
|
||||
- **binwalk and esptool** are CLI tools in the same image (no MCP server yet). The `Dockerfile` has a `binwalk-build` stage adapted from binwalk's own Dockerfile (https://github.com/ReFirmLabs/binwalk) to Ubuntu 24.04, with every source pinned: binwalk at a master commit (3.1.1; the last tag, v3.1.0, is from 2024), Rust toolchain, `dumpifs`/`lzfse`/`dmg2img` commits, and the Python extractors (`jefferson`, `ubi-reader`, `uefi_firmware`, `vmlinux-to-elf`, ...) in `/opt/binwalk-py`. esptool is in `/opt/esptool`. The venvs' entry points are symlinked into `/usr/local/bin` (not their `python`/`pip`). The final stage adds upstream's runtime extractors from apt, sasquatch from onekey-sec's `.deb`, and Kaleido for `binwalk -E -p` (PNG entropy graphs), found via `KALEIDO_PATH`. Upstream publishes no checksums for sasquatch or Kaleido; their SHA-256s were recorded when pinned, amd64 only. `BINWALK_RM_EXTRACTION_SYMLINK=1` avoids dangling symlinks to container paths in extraction output. esptool needs the serial device passed into the container (`--device`) to talk to hardware; that isn't set up.
|
||||
- **binwalk, unblob and esptool** are CLI tools in the same image (no MCP server of their own; REA wraps binwalk and unblob). The `Dockerfile` has a `binwalk-build` stage adapted from binwalk's own Dockerfile (https://github.com/ReFirmLabs/binwalk) to Ubuntu 24.04, with every source pinned: binwalk at a master commit (3.1.1; the last tag, v3.1.0, is from 2024), Rust toolchain, `dumpifs`/`lzfse`/`dmg2img` commits, and the Python extractors (`jefferson`, `ubi-reader`, `uefi_firmware`, `vmlinux-to-elf`, ...) in `/opt/binwalk-py`. esptool is in `/opt/esptool`. unblob (https://github.com/onekey-sec/unblob) 26.6.4 is installed from its PyPI wheel (SHA-256 checked) into `/opt/unblob`, with dependencies pinned to its `uv.lock` at the release commit via `scripts/uvlock-to-constraints.py`; `/usr/local/bin/unblob` is a wrapper that puts that venv first on `PATH`, so unblob uses its own locked `jefferson`/`ubireader` rather than binwalk's. Its extractors follow upstream's `install-deps.sh`: Ubuntu packages, sasquatch v4.5.1-6 (shared with binwalk) and onekey-sec's e2fsprogs fork (v1.47.0-3.ok2, replacing Ubuntu's 1.47.0), whose `debugfs rdump` options unblob's ext2/3/4 handler needs. The build fails unless `unblob --show-external-dependencies` finds every extractor. unblob's Landlock sandbox works in the locked-down REA container (`status=FullyEnforced`). The venvs' entry points are symlinked into `/usr/local/bin` (not their `python`/`pip`). The final stage adds upstream's runtime extractors from apt, sasquatch from onekey-sec's `.deb`, and Kaleido for `binwalk -E -p` (PNG entropy graphs), found via `KALEIDO_PATH`. Upstream publishes no checksums for sasquatch, onekey's e2fsprogs or Kaleido; their SHA-256s were recorded when pinned, amd64 only. `BINWALK_RM_EXTRACTION_SYMLINK=1` avoids dangling symlinks to container paths in extraction output. esptool needs the serial device passed into the container (`--device`) to talk to hardware; that isn't set up.
|
||||
- **MCP transport.** Claude Desktop starts local MCP servers as stdio subprocesses. GhidraMCP runs in the same container, so `ghidra-mcp` is a plain stdio entry in `claude_desktop_config.json` that reaches Ghidra on `127.0.0.1`. Servers in other containers (so far REA) serve streamable HTTP and are bridged into the stdio config with `mcp-remote` (`--allow-http --transport http-only`), installed in `/opt/mcp-bridge` from the committed `claude-desktop/mcp-bridge/package-lock.json` and started with `/opt/node/bin/node` by absolute path. Use the same pattern for future servers. Avoid mounting the Docker socket into the Claude Desktop container just to `docker exec` MCP servers.
|
||||
|
||||
## Conventions
|
||||
|
||||
+62
-11
@@ -121,6 +121,28 @@ RUN python3 -m venv /opt/binwalk-py \
|
||||
&& /opt/esptool/bin/pip install --no-cache-dir --disable-pip-version-check \
|
||||
"esptool==${ESPTOOL_VERSION}"
|
||||
|
||||
# unblob (https://github.com/onekey-sec/unblob), the firmware extractor REA
|
||||
# uses for extract_firmware; REA is verified with 26.6.4. Installed from the
|
||||
# PyPI wheel (SHA-256 from PyPI) into its own venv, with every dependency
|
||||
# pinned to unblob's uv.lock at the release tag's commit, like ghidra-mcp.
|
||||
ARG UNBLOB_VERSION=26.6.4
|
||||
ARG UNBLOB_COMMIT=1fcc7a0a584a70a96c31f5a276c20944d199a089
|
||||
ARG UNBLOB_WHEEL_SHA256=0fc16e9c7dc92fcf86b8b61475700c9dd16e1dddacf34467574ea965a5d3584e
|
||||
ARG UNBLOB_UVLOCK_SHA256=0a88ed397d60e0624bdef34c7a0b7329a6d278dfdedb86f940c6ee25401c0317
|
||||
COPY scripts/uvlock-to-constraints.py /tmp/uvlock-to-constraints.py
|
||||
RUN set -e \
|
||||
&& whl="unblob-${UNBLOB_VERSION}-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" \
|
||||
&& curl -fsSLo "/tmp/${whl}" "https://files.pythonhosted.org/packages/cp39/u/unblob/${whl}" \
|
||||
&& echo "${UNBLOB_WHEEL_SHA256} /tmp/${whl}" | sha256sum -c - \
|
||||
&& curl -fsSLo /tmp/uv.lock "https://raw.githubusercontent.com/onekey-sec/unblob/${UNBLOB_COMMIT}/uv.lock" \
|
||||
&& echo "${UNBLOB_UVLOCK_SHA256} /tmp/uv.lock" | sha256sum -c - \
|
||||
&& python3 -I /tmp/uvlock-to-constraints.py /tmp/uv.lock > /tmp/constraints.txt \
|
||||
&& python3 -m venv /opt/unblob \
|
||||
&& /opt/unblob/bin/pip install --no-cache-dir --disable-pip-version-check \
|
||||
--constraint /tmp/constraints.txt "/tmp/${whl}"
|
||||
# (unblob needs libmagic to start, which this stage lacks; the final stage
|
||||
# runs it.)
|
||||
|
||||
|
||||
# --- Final image -------------------------------------------------------------
|
||||
FROM ubuntu:24.04
|
||||
@@ -207,7 +229,7 @@ ARG GHIDRAMCP_VERSION=2.0
|
||||
ARG GHIDRAMCP_SHA256=ebf904fd6782e429e95a60a24fe0724b20c0acb2dedb304135a5281f6b5f2e72
|
||||
ARG GHIDRAMCP_COMMIT=1e980252087b2479a21b9ca6d82d5829538651a7
|
||||
ARG GHIDRAMCP_UVLOCK_SHA256=f80ffe8df8167535f253976a1dfb702a9a7f8836d884084017dd7c04e1444837
|
||||
COPY ghidra/uvlock-to-constraints.py /tmp/uvlock-to-constraints.py
|
||||
COPY scripts/uvlock-to-constraints.py /tmp/uvlock-to-constraints.py
|
||||
RUN set -e \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends python3 python3-venv \
|
||||
@@ -230,13 +252,23 @@ RUN set -e \
|
||||
&& /opt/ghidra-mcp/bin/ghidra-mcp --help >/dev/null \
|
||||
&& rm -rf /tmp/ghidramcp /tmp/ghidramcp.zip /tmp/uv.lock /tmp/constraints.txt /tmp/uvlock-to-constraints.py
|
||||
|
||||
# binwalk (built above) with the extraction tools its Dockerfile installs.
|
||||
# sasquatch (SquashFS variants) comes as a .deb from onekey-sec; the release
|
||||
# publishes no checksums, so the SHA-256 was taken when it was pinned.
|
||||
# binwalk and unblob (built above) with the extraction tools their install
|
||||
# scripts (binwalk's Dockerfile, unblob's install-deps.sh) put in place.
|
||||
# sasquatch (SquashFS variants) comes as a .deb from onekey-sec, at the
|
||||
# release unblob 26.6.4 uses. unblob's ext2/3/4 extraction needs the rdump
|
||||
# options from onekey-sec's e2fsprogs fork, so its .debs replace Ubuntu's
|
||||
# e2fsprogs (same upstream 1.47.0). These releases publish no checksums;
|
||||
# the SHA-256s were taken when pinned.
|
||||
# The Python tools' entry points are linked into /usr/local/bin; the venvs'
|
||||
# python/pip are not, so the system python3 isn't shadowed.
|
||||
ARG SASQUATCH_RELEASE=sasquatch-v4.5.1-5
|
||||
ARG SASQUATCH_SHA256_AMD64=23191cb530bed01e9f00b9a28a74d03db057fb111e13ce5fb383356c1fdf7e6c
|
||||
# python/pip are not, so the system python3 isn't shadowed. unblob gets a
|
||||
# wrapper that puts its own venv first on PATH, so it calls its own locked
|
||||
# jefferson/ubireader rather than binwalk's.
|
||||
ARG SASQUATCH_RELEASE=sasquatch-v4.5.1-6
|
||||
ARG SASQUATCH_SHA256_AMD64=efbc92bd27594e01211e1b582f105a463a0017360a0746a93de3f283906ade95
|
||||
ARG E2FSPROGS_OK_RELEASE=v1.47.0-3.ok2
|
||||
ARG LIBEXT2FS2_OK_SHA256_AMD64=4eaf27e79a570ec89e15d8851f7df693b8b3ae78ccbb5b2a4a7d3eee845f1beb
|
||||
ARG LIBSS2_OK_SHA256_AMD64=6dfc4612915d885607f36fcea2b851a947f9c73943494208ec48a0cbe9887427
|
||||
ARG E2FSPROGS_OK_SHA256_AMD64=360adf4aa996cc80bf39002775d39900ae207b591c105d12a8ad2a833eb536f4
|
||||
# binwalk -E -p (entropy graph as PNG) renders through Kaleido. binwalk's
|
||||
# build downloads it into the build stage's /root, which the image doesn't
|
||||
# have, so -p would crash; it's fetched here instead (same release the crate
|
||||
@@ -247,12 +279,23 @@ ARG KALEIDO_SHA256_AMD64=3eaa3efd41a00db05dd71add7f54ad4e9d4259552e3f7470ac4c1a8
|
||||
COPY --from=binwalk-build /out/bin/ /usr/local/bin/
|
||||
COPY --from=binwalk-build /opt/binwalk-py /opt/binwalk-py
|
||||
COPY --from=binwalk-build /opt/esptool /opt/esptool
|
||||
COPY --from=binwalk-build /opt/unblob /opt/unblob
|
||||
RUN set -e \
|
||||
&& arch="$(dpkg --print-architecture)" \
|
||||
&& [ "$arch" = amd64 ] || { echo "sasquatch checksum only pinned for amd64" >&2; exit 1; } \
|
||||
&& curl -fsSLo /tmp/sasquatch.deb \
|
||||
"https://github.com/onekey-sec/sasquatch/releases/download/${SASQUATCH_RELEASE}/sasquatch_1.0_${arch}.deb" \
|
||||
&& echo "${SASQUATCH_SHA256_AMD64} /tmp/sasquatch.deb" | sha256sum -c - \
|
||||
&& mkdir /tmp/e2fsprogs-ok && cd /tmp/e2fsprogs-ok \
|
||||
&& v="${E2FSPROGS_OK_RELEASE#v}" \
|
||||
&& printf '%s %s\n' \
|
||||
"${LIBEXT2FS2_OK_SHA256_AMD64}" "libext2fs2_${v}_${arch}.deb" \
|
||||
"${LIBSS2_OK_SHA256_AMD64}" "libss2_${v}_${arch}.deb" \
|
||||
"${E2FSPROGS_OK_SHA256_AMD64}" "e2fsprogs_${v}_${arch}.deb" > SHA256SUMS \
|
||||
&& for f in $(awk '{print $2}' SHA256SUMS); do \
|
||||
curl -fsSLO "https://github.com/onekey-sec/e2fsprogs/releases/download/${E2FSPROGS_OK_RELEASE}/$f"; \
|
||||
done \
|
||||
&& sha256sum -c SHA256SUMS && cd / \
|
||||
&& curl -fsSLo /tmp/kaleido.zip \
|
||||
"https://github.com/plotly/Kaleido/releases/download/v${KALEIDO_VERSION}/kaleido_linux_x64.zip" \
|
||||
&& echo "${KALEIDO_SHA256_AMD64} /tmp/kaleido.zip" | sha256sum -c - \
|
||||
@@ -260,16 +303,23 @@ RUN set -e \
|
||||
&& rm /tmp/kaleido.zip \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
/tmp/sasquatch.deb \
|
||||
/tmp/sasquatch.deb /tmp/e2fsprogs-ok/*.deb \
|
||||
7zip 7zip-standalone zstd srecord tar unzip sleuthkit cabextract \
|
||||
lz4 lzop unrar unyaffs cpio device-tree-compiler \
|
||||
liblzo2-2 libucl1 libfontconfig1 \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/sasquatch.deb \
|
||||
android-sdk-libsparse-utils erofs-utils lziprecover partclone unar \
|
||||
upx-ucl libmagic1t64 xz-utils \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/sasquatch.deb /tmp/e2fsprogs-ok \
|
||||
&& for f in /opt/binwalk-py/bin/* /opt/esptool/bin/*; do \
|
||||
case "$(basename "$f")" in python*|pip*|activate*|Activate*) continue ;; esac; \
|
||||
ln -s "$f" /usr/local/bin/; \
|
||||
done \
|
||||
&& binwalk --version && esptool version
|
||||
&& printf '%s\n' '#!/bin/sh' 'PATH="/opt/unblob/bin:$PATH" exec /opt/unblob/bin/unblob "$@"' \
|
||||
> /usr/local/bin/unblob \
|
||||
&& chmod 755 /usr/local/bin/unblob \
|
||||
&& binwalk --version && esptool version && unblob --version \
|
||||
&& debugfs -V 2>&1 | grep -q 1.47.0 \
|
||||
&& unblob --show-external-dependencies
|
||||
# Binwalk leaves a symlink to the input file in its extraction directory. The
|
||||
# target is a container path, which is dangling when viewed from the host.
|
||||
ENV BINWALK_RM_EXTRACTION_SYMLINK=1 \
|
||||
@@ -312,7 +362,8 @@ RUN set -e; cd /tmp \
|
||||
&& npm install --global --omit=optional --no-audit --no-fund "./rea-agents-${REA_VERSION}.tgz" \
|
||||
&& rm -rf "rea-agents-${REA_VERSION}.tgz" /root/.npm \
|
||||
&& [ "$(rea --version)" = "${REA_VERSION}" ]
|
||||
ENV REA_BINWALK_COMMAND=/usr/local/bin/binwalk
|
||||
ENV REA_BINWALK_COMMAND=/usr/local/bin/binwalk \
|
||||
REA_UNBLOB_COMMAND=/usr/local/bin/unblob
|
||||
|
||||
# stdio-to-HTTP bridge (mcp-remote) that Claude Desktop starts to reach REA's
|
||||
# HTTP endpoint in the other container. Installed from a committed lockfile.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
A Docker-based reverse-engineering workstation for Linux. Claude Desktop is
|
||||
connected over MCP to Ghidra and to REA ("Reverse Engineer Anything"), and
|
||||
binwalk and esptool are installed alongside them. Nothing beyond Docker and
|
||||
an X server (or XWayland) is needed on the host.
|
||||
binwalk, unblob and esptool are installed alongside them. Nothing beyond
|
||||
Docker and an X server (or XWayland) is needed on the host.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -20,10 +20,10 @@ Desktop or Ghidra's project window stops everything.
|
||||
|
||||
## What runs where
|
||||
|
||||
| Container | Contents | Network |
|
||||
| ---------------- | ------------------------------------------------------------ | -------------------------- |
|
||||
| `claude-desktop` | Claude Desktop, Ghidra GUI + GhidraMCP, binwalk, esptool | default + internal |
|
||||
| `claude-re-mcp` | REA MCP server (headless Ghidra, binwalk), no display | internal only, no internet |
|
||||
| Container | Contents | Network |
|
||||
| ---------------- | --------------------------------------------------------------- | -------------------------- |
|
||||
| `claude-desktop` | Claude Desktop, Ghidra GUI + GhidraMCP, binwalk, unblob, esptool | default + internal |
|
||||
| `claude-re-mcp` | REA MCP server (headless Ghidra, binwalk, unblob), no display | internal only, no internet |
|
||||
|
||||
Both containers run from the same image. REA gets its own container because
|
||||
some of its tools run the target. Claude Desktop reaches it at
|
||||
@@ -44,6 +44,7 @@ MCP servers in Claude Desktop (`claude-desktop/claude_desktop_config.json`):
|
||||
- REA 6.8.0 (Reverse Engineer Anything): https://github.com/morluto/rea, npm `rea-agents`
|
||||
- mcp-remote 0.15.1: https://github.com/punkpeye/mcp-remote (formerly geelen/mcp-remote)
|
||||
- binwalk 3.1.1: https://github.com/ReFirmLabs/binwalk
|
||||
- unblob 26.6.4: https://github.com/onekey-sec/unblob
|
||||
- esptool 5.6.0: https://github.com/espressif/esptool
|
||||
|
||||
Versions and checksums are pinned in the `Dockerfile`. `CLAUDE.md` has the
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
"""Print pip constraints (name==version) for every package in a uv.lock.
|
||||
|
||||
Used at image build time to install the ghidra-mcp wheel with the exact
|
||||
dependency versions its upstream lock file was tested with. A package that
|
||||
uv resolved differently per Python version appears more than once; those
|
||||
Used at image build time to install a Python tool's release wheel
|
||||
(ghidra-mcp, unblob) with the exact dependency versions its upstream lock
|
||||
file was tested with. The locked project itself (source "editable"/"virtual"
|
||||
at ".") is skipped; it is installed from the wheel. A package that uv
|
||||
resolved differently per Python version appears more than once; those
|
||||
entries carry their resolution markers, so pip applies only the one that
|
||||
matches the interpreter.
|
||||
"""
|
||||
@@ -13,8 +15,8 @@ with open(sys.argv[1], "rb") as f:
|
||||
lock = tomllib.load(f)
|
||||
|
||||
for package in lock["package"]:
|
||||
# The project itself is installed from the release wheel.
|
||||
if package["name"] == "ghidra-mcp":
|
||||
source = package.get("source", {})
|
||||
if source.get("editable") == "." or source.get("virtual") == ".":
|
||||
continue
|
||||
line = f'{package["name"]}=={package["version"]}'
|
||||
markers = package.get("resolution-markers")
|
||||
Reference in New Issue
Block a user