# MCGhidra Docker Image # Ghidra + MCGhidra Plugin pre-installed for headless binary analysis # # Build: docker build -t mcghidra:latest -f docker/Dockerfile . # Run: docker run -p 8192:8192 -v /path/to/binaries:/binaries mcghidra:latest ARG GHIDRA_VERSION=11.4.2 ARG GHIDRA_DATE=20250826 # ============================================================================= # Stage 1: Build the MCGhidra plugin # ============================================================================= FROM eclipse-temurin:21-jdk-jammy AS builder ARG GHIDRA_VERSION ARG GHIDRA_DATE # Install build dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ unzip \ maven \ git \ && rm -rf /var/lib/apt/lists/* # Download and extract Ghidra WORKDIR /opt # Download with retries and resume support for unreliable connections RUN for i in 1 2 3 4 5; do \ curl -fSL --http1.1 -C - \ "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_${GHIDRA_VERSION}_build/ghidra_${GHIDRA_VERSION}_PUBLIC_${GHIDRA_DATE}.zip" \ -o ghidra.zip && break || sleep 30; \ done \ && unzip -q ghidra.zip \ && rm ghidra.zip \ && mv ghidra_${GHIDRA_VERSION}_PUBLIC ghidra ENV GHIDRA_HOME=/opt/ghidra # Copy MCGhidra source and build WORKDIR /build # Copy pom.xml first and download dependencies (cached until pom.xml changes) COPY pom.xml . RUN mvn dependency:resolve -P plugin-only -q \ -Dghidra.generic.jar=${GHIDRA_HOME}/Ghidra/Framework/Generic/lib/Generic.jar \ -Dghidra.softwaremodeling.jar=${GHIDRA_HOME}/Ghidra/Framework/SoftwareModeling/lib/SoftwareModeling.jar \ -Dghidra.project.jar=${GHIDRA_HOME}/Ghidra/Framework/Project/lib/Project.jar \ -Dghidra.docking.jar=${GHIDRA_HOME}/Ghidra/Framework/Docking/lib/Docking.jar \ -Dghidra.decompiler.jar=${GHIDRA_HOME}/Ghidra/Features/Decompiler/lib/Decompiler.jar \ -Dghidra.utility.jar=${GHIDRA_HOME}/Ghidra/Framework/Utility/lib/Utility.jar \ -Dghidra.base.jar=${GHIDRA_HOME}/Ghidra/Features/Base/lib/Base.jar \ || true # Now copy source - only this layer rebuilds on code changes COPY src ./src # Build the plugin (skip git-commit-id plugin since .git isn't in Docker context) RUN mvn package -P plugin-only -DskipTests \ -Dmaven.gitcommitid.skip=true \ -Dghidra.generic.jar=${GHIDRA_HOME}/Ghidra/Framework/Generic/lib/Generic.jar \ -Dghidra.softwaremodeling.jar=${GHIDRA_HOME}/Ghidra/Framework/SoftwareModeling/lib/SoftwareModeling.jar \ -Dghidra.project.jar=${GHIDRA_HOME}/Ghidra/Framework/Project/lib/Project.jar \ -Dghidra.docking.jar=${GHIDRA_HOME}/Ghidra/Framework/Docking/lib/Docking.jar \ -Dghidra.decompiler.jar=${GHIDRA_HOME}/Ghidra/Features/Decompiler/lib/Decompiler.jar \ -Dghidra.utility.jar=${GHIDRA_HOME}/Ghidra/Framework/Utility/lib/Utility.jar \ -Dghidra.base.jar=${GHIDRA_HOME}/Ghidra/Features/Base/lib/Base.jar # ============================================================================= # Stage 2: Runtime image with Ghidra + MCGhidra # ============================================================================= # NOTE: Ghidra requires JDK (not JRE) - it checks for javac in LaunchSupport FROM eclipse-temurin:21-jdk-jammy AS runtime ARG GHIDRA_VERSION ARG GHIDRA_DATE LABEL org.opencontainers.image.title="mcghidra" \ org.opencontainers.image.description="Ghidra + MCGhidra Plugin for AI-assisted reverse engineering" \ org.opencontainers.image.source="https://github.com/starsong-consulting/MCGhidra" \ org.opencontainers.image.licenses="Apache-2.0" # Install runtime dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ unzip \ fontconfig \ libfreetype6 \ && rm -rf /var/lib/apt/lists/* # Create non-root user RUN groupadd -g 1001 ghidra && useradd -u 1001 -g ghidra -m -s /bin/bash ghidra # Download and extract Ghidra (in runtime stage for cleaner image) WORKDIR /opt # Download with retries and resume support for unreliable connections RUN for i in 1 2 3 4 5; do \ curl -fSL --http1.1 -C - \ "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_${GHIDRA_VERSION}_build/ghidra_${GHIDRA_VERSION}_PUBLIC_${GHIDRA_DATE}.zip" \ -o ghidra.zip && break || sleep 30; \ done \ && unzip -q ghidra.zip \ && rm ghidra.zip \ && mv ghidra_${GHIDRA_VERSION}_PUBLIC ghidra \ && chown -R ghidra:ghidra /opt/ghidra ENV GHIDRA_HOME=/opt/ghidra ENV PATH="${GHIDRA_HOME}:${PATH}" # Install the MCGhidra plugin COPY --from=builder /build/target/MCGhidra-*.zip /tmp/ RUN mkdir -p /opt/ghidra/Ghidra/Extensions \ && unzip -q /tmp/MCGhidra-*.zip -d /opt/ghidra/Ghidra/Extensions/ \ && rm /tmp/MCGhidra-*.zip \ && chown -R ghidra:ghidra /opt/ghidra/Ghidra/Extensions/ # Create directories for projects and binaries RUN mkdir -p /projects /binaries /home/ghidra/.ghidra \ && chown -R ghidra:ghidra /projects /binaries /home/ghidra # Copy MCGhidra Python scripts to user scripts directory # Python/Jython scripts don't require OSGi bundle registration - they work without issue RUN mkdir -p /home/ghidra/ghidra_scripts COPY docker/MCGhidraServer.py /home/ghidra/ghidra_scripts/ COPY docker/ImportRawARM.java /home/ghidra/ghidra_scripts/ # Set proper ownership and permissions RUN chown -R ghidra:ghidra /home/ghidra/ghidra_scripts \ && chmod 755 /home/ghidra/ghidra_scripts/*.py 2>/dev/null || true \ && chmod 755 /home/ghidra/ghidra_scripts/*.java 2>/dev/null || true # Copy entrypoint script (755 so ghidra user can read and execute) COPY docker/entrypoint.sh /entrypoint.sh RUN chmod 755 /entrypoint.sh # Switch to non-root user USER ghidra WORKDIR /home/ghidra # Expose the MCGhidra HTTP API port (and additional ports for multiple instances) EXPOSE 8192 8193 8194 8195 # Default environment ENV MCGHIDRA_MODE=headless ENV MCGHIDRA_PORT=8192 ENV MCGHIDRA_MAXMEM=2G # Healthcheck HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD curl -f http://localhost:${MCGHIDRA_PORT}/ || exit 1 ENTRYPOINT ["/entrypoint.sh"]