mcghidra/docker/entrypoint.sh
Ryan Malloy 0250c2df01
Some checks are pending
Build Ghidra Plugin / build (push) Waiting to run
Add language, base address, and loader support for raw firmware import
Wire GHIDRA_LANGUAGE, GHIDRA_BASE_ADDRESS, GHIDRA_LOADER through the
Docker entrypoint and MCP tools so raw binaries (e.g., ARM7TDMI firmware)
get the correct processor, memory map, and loader instead of relying on
auto-detection. Auto-sets BinaryLoader when language is specified.

Input validation at both Python and bash layers prevents malformed values
from reaching analyzeHeadless.
2026-03-06 21:46:22 -07:00

179 lines
6.4 KiB
Bash
Executable File

#!/bin/bash
# MCGhidra Docker Entrypoint
# Starts Ghidra in headless mode with HTTP API server
set -e
MCGHIDRA_MODE=${MCGHIDRA_MODE:-headless}
MCGHIDRA_PORT=${MCGHIDRA_PORT:-8192}
MCGHIDRA_MAXMEM=${MCGHIDRA_MAXMEM:-2G}
GHIDRA_HOME=${GHIDRA_HOME:-/opt/ghidra}
# User scripts directory - Python scripts don't need OSGi bundle registration
SCRIPT_DIR=${SCRIPT_DIR:-/home/ghidra/ghidra_scripts}
# Project settings
PROJECT_DIR=${PROJECT_DIR:-/projects}
PROJECT_NAME=${PROJECT_NAME:-MCGhidra}
echo "=============================================="
echo " MCGhidra Docker Container"
echo "=============================================="
echo " Mode: ${MCGHIDRA_MODE}"
echo " Port: ${MCGHIDRA_PORT}"
echo " Memory: ${MCGHIDRA_MAXMEM}"
echo " Project: ${PROJECT_DIR}/${PROJECT_NAME}"
echo "=============================================="
# Ensure directories exist
mkdir -p "${PROJECT_DIR}"
# Handle different modes
case "${MCGHIDRA_MODE}" in
headless)
# Headless mode: Import a binary and start HTTP server
if [ $# -eq 0 ]; then
echo ""
echo "Usage: docker run mcghidra:latest [binary_path] [options]"
echo ""
echo "Examples:"
echo " # Analyze a binary mounted at /binaries/sample.exe"
echo " docker run -p 8192:8192 -v ./samples:/binaries mcghidra /binaries/sample.exe"
echo ""
echo " # With custom project name"
echo " docker run -p 8192:8192 -v ./samples:/binaries -e PROJECT_NAME=malware mcghidra /binaries/sample.exe"
echo ""
echo "Environment variables:"
echo " MCGHIDRA_PORT - HTTP API port (default: 8192)"
echo " MCGHIDRA_MAXMEM - Max JVM heap (default: 2G)"
echo " PROJECT_NAME - Ghidra project name (default: MCGhidra)"
echo " PROJECT_DIR - Project directory (default: /projects)"
echo " GHIDRA_LANGUAGE - Processor language ID (e.g., ARM:LE:32:v4t)"
echo " GHIDRA_BASE_ADDRESS - Base address for raw binaries (e.g., 0x00000000)"
echo " GHIDRA_LOADER - Loader type (e.g., BinaryLoader for raw firmware)"
echo ""
echo "Starting in wait mode..."
echo "Container will stay running for debugging or manual operation."
echo "You can exec into this container to run analyzeHeadless manually."
echo ""
# Keep container alive for debugging/manual operation
tail -f /dev/null
else
BINARY_PATH="$1"
shift
if [ ! -f "${BINARY_PATH}" ]; then
echo "ERROR: Binary not found: ${BINARY_PATH}"
echo "Make sure to mount the binary directory with -v /host/path:/binaries"
exit 1
fi
BINARY_NAME=$(basename "${BINARY_PATH}")
echo "Importing and analyzing: ${BINARY_NAME}"
echo ""
# Build the analyzeHeadless command
ANALYZE_CMD="${GHIDRA_HOME}/support/analyzeHeadless"
ANALYZE_ARGS=(
"${PROJECT_DIR}"
"${PROJECT_NAME}"
-import "${BINARY_PATH}"
-max-cpu 2
-scriptPath "${SCRIPT_DIR}"
-postScript "MCGhidraServer.py" "${MCGHIDRA_PORT}"
)
# Optional: processor/language for raw binaries
if [ -n "${GHIDRA_LANGUAGE}" ]; then
if ! echo "${GHIDRA_LANGUAGE}" | grep -qE '^[A-Za-z0-9_]+:[A-Z]{2}:[0-9]+:[A-Za-z0-9._-]+$'; then
echo "ERROR: Invalid GHIDRA_LANGUAGE format: ${GHIDRA_LANGUAGE}"
echo "Expected: ARCH:ENDIAN:SIZE:VARIANT (e.g., ARM:LE:32:v4t)"
exit 1
fi
ANALYZE_ARGS+=(-processor "${GHIDRA_LANGUAGE}")
fi
# Optional: base address
if [ -n "${GHIDRA_BASE_ADDRESS}" ]; then
if ! echo "${GHIDRA_BASE_ADDRESS}" | grep -qE '^(0x)?[0-9a-fA-F]+$'; then
echo "ERROR: Invalid GHIDRA_BASE_ADDRESS format: ${GHIDRA_BASE_ADDRESS}"
echo "Expected hex: 0x00000000 or 00000000"
exit 1
fi
ANALYZE_ARGS+=(-loader-baseAddr "${GHIDRA_BASE_ADDRESS}")
fi
# Optional: explicit loader (e.g., BinaryLoader for raw firmware)
if [ -n "${GHIDRA_LOADER}" ]; then
if ! echo "${GHIDRA_LOADER}" | grep -qE '^[A-Za-z0-9_]+$'; then
echo "ERROR: Invalid GHIDRA_LOADER format: ${GHIDRA_LOADER}"
echo "Expected alphanumeric name (e.g., BinaryLoader)"
exit 1
fi
ANALYZE_ARGS+=(-loader "${GHIDRA_LOADER}")
fi
# Add any extra arguments passed
ANALYZE_ARGS+=("$@")
echo "Running: ${ANALYZE_CMD} ${ANALYZE_ARGS[*]}"
echo ""
exec "${ANALYZE_CMD}" "${ANALYZE_ARGS[@]}"
fi
;;
server)
# Server mode: Open existing project with HTTP server
echo "Starting MCGhidra server on existing project..."
if [ $# -eq 0 ]; then
echo "Usage: docker run -e MCGHIDRA_MODE=server mcghidra [program_name]"
echo ""
echo " program_name: Name of program in the project to open"
exit 1
fi
PROGRAM_NAME="$1"
shift
exec "${GHIDRA_HOME}/support/analyzeHeadless" \
"${PROJECT_DIR}" "${PROJECT_NAME}" \
-process "${PROGRAM_NAME}" \
-noanalysis \
-scriptPath "${SCRIPT_DIR}" \
-postScript "MCGhidraServer.py" "${MCGHIDRA_PORT}" \
"$@"
;;
analyze)
# Analyze mode: Import and analyze, then exit (no HTTP server)
if [ $# -eq 0 ]; then
echo "Usage: docker run -e MCGHIDRA_MODE=analyze mcghidra [binary_path]"
exit 1
fi
BINARY_PATH="$1"
shift
echo "Analyzing binary: ${BINARY_PATH}"
exec "${GHIDRA_HOME}/support/analyzeHeadless" \
"${PROJECT_DIR}" "${PROJECT_NAME}" \
-import "${BINARY_PATH}" \
-max-cpu 2 \
"$@"
;;
shell)
# Interactive shell
exec /bin/bash
;;
*)
echo "Unknown mode: ${MCGHIDRA_MODE}"
echo "Valid modes: headless, server, analyze, shell"
exit 1
;;
esac