# syntax=docker/dockerfile:1 # # ChipBench — self-contained reverse-engineering environment for 8-bit # (and small embedded) chip dumps. Bundles: Ghidra 11.3.2 + GhidraMCP 1.4, # radare2, binwalk v3, binutils for several targets, sdcc, gputils, # capstone/unicorn/keystone, plus a firmware-hacker toolset (flashrom, # avrdude, openocd, srecord, simavr, unpacking tools, serial consoles). # Optional VNC/noVNC desktop so the Ghidra GUI also runs headlessly. # # Nothing runs as root; runtime user is mapped to host uid/gid so # bind-mounted artifacts keep their owner. # # This project is AGPLv3 (see LICENSE). It builds tooling from public # sources; no binaries are hosted by the project. # ---- Stage 1: build binwalk v3 (Rust) in an isolated builder ---------------- FROM rust:1-slim-bookworm AS binwalk-builder RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config libfontconfig1-dev ca-certificates \ && rm -rf /var/lib/apt/lists/* RUN cargo install --root /out --version 3.1.0 binwalk # ---- Stage 2: main image ---------------------------------------------------- ARG BUILD_UID=1001 ARG BUILD_GID=1001 FROM ubuntu:24.04 # ARGs above do not cross FROM boundaries; re-declare for this stage # (overridable at build time: --build-arg BUILD_UID=$(id -u) etc.) ARG BUILD_UID ARG BUILD_GID ENV DEBIAN_FRONTEND=noninteractive \ TZ=UTC \ LANG=C.UTF-8 \ LC_ALL=C.UTF-8 \ DISPLAY=:0 \ JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 \ GHIDRA_HOME=/opt/ghidra \ GHIDRAMCP_HOME=/opt/ghidramcp \ VENV_HOME=/opt/venv \ GHIDRA_SCRIPTS=/opt/ghidra-scripts \ PATH="${JAVA_HOME}/bin:/opt/ghidra:/opt/ghidra/support:/opt/venv/bin:/usr/local/bin:${PATH}" # ----------------------------------------------------------------------------- # 1. System packages # - GUI/VNC stack (optional desktop for the Ghidra GUI) # - core RE CLI: radare2, binutils-multiarch, binutils-avr, sdcc, gputils # - chip-programming & debug: flashrom, avrdude, openocd, stlink-tools # - EPROM/SREC/HEX wrangling: srecord # - simulators: simavr (AVR), gpsim (PIC) # - firmware-unpacking: binwalk deps (7z, cabextract, lzma, cpio, squashfs, # unar), u-boot-tools (mkimage), device-tree-compiler (dtc) # - serial: tio, picocom # - misc: esptool, z80dasm, vbindiff # NOTE: openjdk-21-jdk is UNPINNED — in-process javac for Ghidra .java # user scripts has broken before on point-release drift. The supported # script path here is Jython (.py); the .java helper scripts are not shipped. # ----------------------------------------------------------------------------- RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates wget git unzip xz-utils \ openjdk-21-jdk \ xvfb x11vnc fluxbox supervisor \ novnc websockify \ xterm dbus-x11 \ libfontconfig1 fonts-dejavu-core \ python3 python3-venv python3-pip \ file xxd hexyl binutils \ binutils-avr \ binutils-multiarch \ gputils \ sdcc \ radare2 \ net-tools iproute2 procps less vim-tiny \ flashrom avrdude openocd stlink-tools \ srecord \ simavr gpsim \ z80dasm \ squashfs-tools p7zip-full cabextract lzma cpio unar \ u-boot-tools device-tree-compiler \ tio picocom \ esptool \ vbindiff \ && rm -rf /var/lib/apt/lists/* # binwalk v3 binary (Rust) from the builder stage. COPY --from=binwalk-builder /out/bin/binwalk /usr/local/bin/binwalk # ----------------------------------------------------------------------------- # 2. Ghidra 11.3.2 (pinned: latest version the GhidraMCP plugin supports) # ----------------------------------------------------------------------------- ARG GHIDRA_VERSION=11.3.2 ARG GHIDRA_ZIP=ghidra_11.3.2_PUBLIC_20250415.zip ARG GHIDRA_URL=https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.3.2_build/${GHIDRA_ZIP} ARG GHIDRA_SHA256=99d45035bdcc3d6627e7b1232b7b379905a9fad76c772c920602e2b5d8b2dac2 RUN cd /tmp \ && wget -q -O ghidra.zip "${GHIDRA_URL}" \ && echo "${GHIDRA_SHA256} ghidra.zip" | sha256sum -c - \ && unzip -q ghidra.zip -d /opt \ && rm -f ghidra.zip \ && mv /opt/ghidra_* "${GHIDRA_HOME}" # ----------------------------------------------------------------------------- # 3. GhidraMCP 1.4 plugin (optional REST server inside the Ghidra GUI). # The GitHub release ships a NESTED zip; unpack both layers. # (Harmless "Module manifest file error" warnings at headless startup are # known noise from this plugin's manifest formatting.) # ----------------------------------------------------------------------------- ARG GHIDRAMCP_VERSION=1.4 ARG GHIDRAMCP_ZIP=GhidraMCP-release-1-4.zip ARG GHIDRAMCP_URL=https://github.com/LaurieWired/GhidraMCP/releases/download/${GHIDRAMCP_VERSION}/${GHIDRAMCP_ZIP} ARG GHIDRAMCP_SHA256=b81ca5240fdde57ea4e899170dcd9fdee6ed29246280c74263a509bfcfc7e734 RUN cd /tmp \ && wget -q -O mcp-outer.zip "${GHIDRAMCP_URL}" \ && echo "${GHIDRAMCP_SHA256} mcp-outer.zip" | sha256sum -c - \ && mkdir -p outer && unzip -q mcp-outer.zip -d outer \ && inner_zip="$(find outer -name 'GhidraMCP-*.zip' | head -n1)" \ && test -n "$inner_zip" \ && mkdir -p inner && unzip -q "$inner_zip" -d inner \ && extdir="$(dirname "$(find inner -name extension.properties | head -n1)")" \ && test -n "$extdir" \ && mkdir -p "${GHIDRA_HOME}/Ghidra/Extensions" \ && cp -r "$extdir" "${GHIDRA_HOME}/Ghidra/Extensions/GhidraMCP" \ && rm -rf /tmp/* ARG BRIDGE_URL=https://raw.githubusercontent.com/LaurieWired/GhidraMCP/1.4/bridge_mcp_ghidra.py # Non-fatal: the MCP bridge is optional (GUI-only convenience). GitHub raw # occasionally rate-limits (429); retries + a warning keep builds reproducible. RUN mkdir -p "${GHIDRAMCP_HOME}" \ && ( wget -q --tries=3 --timeout=30 -O "${GHIDRAMCP_HOME}/bridge_mcp_ghidra.py" "${BRIDGE_URL}" \ || echo "[warn] GhidraMCP bridge download failed; GUI MCP bridge disabled (headless workflow unaffected)" ) # ----------------------------------------------------------------------------- # 4. Python tooling (isolated venv; respects PEP 668) # ----------------------------------------------------------------------------- RUN python3 -m venv "${VENV_HOME}" \ && "${VENV_HOME}/bin/pip" install --no-cache-dir --upgrade pip \ && "${VENV_HOME}/bin/pip" install --no-cache-dir \ "mcp==1.5.0" "requests==2.32.3" "pyserial>=3.5" \ capstone unicorn \ && ( "${VENV_HOME}/bin/pip" install --no-cache-dir keystone-engine \ || echo "[warn] keystone-engine unavailable on this platform; skipping (non-fatal)" ) # ----------------------------------------------------------------------------- # 5. Runtime user matching host uid/gid (no root needed at runtime) # ----------------------------------------------------------------------------- RUN groupadd -g "${BUILD_GID}" chip \ && useradd -m -u "${BUILD_UID}" -g "${BUILD_GID}" -s /bin/bash chip COPY scripts/ghidra-scripts/ "${GHIDRA_SCRIPTS}/" COPY conf/supervisord.conf /opt/conf/supervisord.conf COPY conf/profile.d/chipbench.sh /etc/profile.d/chipbench.sh COPY scripts/entrypoint.sh /opt/entrypoint.sh RUN chmod +x /opt/entrypoint.sh \ && chown -R chip:chip "${GHIDRA_SCRIPTS}" /opt/entrypoint.sh /opt/conf USER chip:chip WORKDIR /data EXPOSE 5900 6080 8081 ENTRYPOINT ["/opt/entrypoint.sh"] CMD ["supervisor"]