diff --git a/Ghidra/Features/GhidraGo/src/main/java/ghidra/GhidraGo.java b/Ghidra/Features/GhidraGo/src/main/java/ghidra/GhidraGo.java index f5ece5f695..123625f230 100644 --- a/Ghidra/Features/GhidraGo/src/main/java/ghidra/GhidraGo.java +++ b/Ghidra/Features/GhidraGo/src/main/java/ghidra/GhidraGo.java @@ -149,25 +149,25 @@ public class GhidraGo implements GhidraLaunchable { * @throws IOException in the event that the execution failed */ private Process startGhidra(GhidraApplicationLayout layout) throws IOException { - ResourceFile file = layout.getApplicationInstallationDir(); + ResourceFile file = layout.getApplicationRootDirs().stream().findFirst().get(); Path ghidraRunPath; if (SystemUtilities.isInDevelopmentMode()) { if (Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS) { ghidraRunPath = Path.of(file.getAbsolutePath(), - "/ghidra/Ghidra/RuntimeScripts/Windows/ghidraRun.bat"); + "/RuntimeScripts/Windows/ghidraRun.bat"); } else { ghidraRunPath = Path.of(file.getAbsolutePath(), - "/ghidra/Ghidra/RuntimeScripts/Linux/ghidraRun"); + "/RuntimeScripts/Linux/ghidraRun"); } } else { if (Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS) { - ghidraRunPath = Path.of(file.getAbsolutePath(), "/ghidraRun.bat"); + ghidraRunPath = Path.of(file.getAbsolutePath(), "/../ghidraRun.bat"); } else { - ghidraRunPath = Path.of(file.getAbsolutePath(), "/ghidraRun"); + ghidraRunPath = Path.of(file.getAbsolutePath(), "/../ghidraRun"); } } diff --git a/docker/Dockerfile b/docker/Dockerfile index 9edff23874..533dac2d95 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,7 +5,7 @@ # a ghidra release using the command # docker build -f docker/Dockerfile -t ############################################################### -FROM alpine:3.20 as base +FROM alpine:3.20 AS base LABEL org.opencontainers.image.title="ghidra" \ org.opencontainers.image.description="Docker image for Ghidra" \ @@ -17,13 +17,10 @@ LABEL org.opencontainers.image.title="ghidra" \ RUN addgroup -g 1001 -S ghidra && adduser -u 1001 -S ghidra -G ghidra ENTRYPOINT ["/bin/bash", "/ghidra/docker/entrypoint.sh"] # Set JAVA_HOME so that we don't need to do this manually when Ghidra is first started. -ENV JAVA_HOME /usr/lib/jvm/java-21-openjdk -ENV LD_LIBRARY_PATH /usr/lib/jvm/java-21-openjdk/lib/:/usr/lib/jvm/java-21-openjdk/lib/server/ +ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk +ENV LD_LIBRARY_PATH=/usr/lib/jvm/java-21-openjdk/lib/:/usr/lib/jvm/java-21-openjdk/lib/server/ WORKDIR /ghidra -# validate build context is correct. an error will happen if this file is not present. -COPY ./ghidraRun ./ghidraRun - # update and install dependencies used to both build and run ghidra RUN apk update \ && apk add openjdk21 python3 \ @@ -32,7 +29,7 @@ RUN apk update \ linux-headers libressl-dev \ && update-ms-fonts -FROM base as build +FROM base AS build # install additional dependencies used to build ghidra RUN apk add gradle \ @@ -51,7 +48,7 @@ RUN /ghidra/Ghidra/Features/BSim/support/make-postgres.sh \ && mkdir /ghidra/repositories && mkdir /ghidra/bsim_datadir -FROM base as runtime +FROM base AS runtime # install additional dependencies needed for running ghidra RUN apk add openssl openssh-client \ diff --git a/docker/README.md b/docker/README.md index 8181147412..c621f9acc9 100644 --- a/docker/README.md +++ b/docker/README.md @@ -2,13 +2,13 @@ ## Build -From the root directory of your Ghidra release, run the following command with the correct version for your release. +From the root directory of your Ghidra release, run the following command. ``` -docker build -f docker/Dockerfile -t ghidra/ghidra[:] . +./docker/build-docker-image.sh ``` -The image tag is optional, but highly recommended. +This will build the ghidra docker image with a tag corresponding to the release version of Ghidra. ## The MODE environment variable @@ -33,7 +33,7 @@ Configuration steps vary a lot based on what MODE the container is started with. The base directory for Ghidra within the container is located at `/ghidra`. All of ghidra's default locations for files, configs, etc., are the same within that. -Ghidra is run as the user `ghidra` within the container. +Ghidra is run as the user `ghidra` within the container, with uid `1001` and guid `1001`. The `ghidra` user only has permissions to the following directories inside the container: - `/ghidra` @@ -61,19 +61,23 @@ docker run \ --volume /path/to/myproject:/home/ghidra/myproject \ --volume /path/to/mybinary:/home/ghidra/mybinary \ ghidra/ghidra: \ - /myproject programFolder -import /mybinary + /home/ghidra/myproject programFolder -import /home/ghidra/mybinary ``` Breaking this down line by line: - `docker run` is going to start a docker container using the image `ghidra/ghidra<:` - `--env MODE=headless` configures the environment variable `MODE` within the container to be the value `headless` - `--rm` removes the container after the command is complete -- `--volume /path/to/myproject:/myproject` mounts the local volume `/path/to/myproject` on the host to `/myproject` within the container -- `--volume /path/to/mybinary:/mybinary` mounts the local volume `/path/to/mybinary` on the host to `/mybinary` within the container +- `--volume /path/to/myproject:/home/ghidra/myproject` mounts the local volume + `/path/to/myproject` on the host to `/home/ghidra/myproject` within the container +- `--volume /path/to/mybinary:/home/ghidra/mybinary` mounts the local volume + `/path/to/mybinary` on the host to `/home/ghidra/mybinary` within the container - `ghidra/ghidra:` is the full reference for the docker image, where `ghidra/ghidra` is the group and name of the image, and `` is the tag. -- `/myproject programFolder -import /mybinary` are arguments being passed to Ghidra's headless analyzer's command line interface +- `/home/ghidra/myproject programFolder -import /home/ghidra/mybinary` are arguments being passed to Ghidra's headless analyzer's command line interface -Passing no arguments will result in the usage of the headless analyzer being displayed. +Passing no arguments will result in the usage of the headless analyzer being displayed. + +`/path/to/myproject` on the host must be accessible to guid `1001` with `rwx` permissions. ### Example of Gui Mode @@ -87,7 +91,7 @@ docker run \ --rm \ --net host \ --env DISPLAY \ - --volume="$HOME/.Xauthority:/home/ghidra/.Xauthority" \ + --volume "$HOME/.Xauthority:/home/ghidra/.Xauthority" \ ghidra/ghidra: ``` @@ -124,7 +128,6 @@ To stop the container, execute the command `docker stop `. ## Example of BSIM Server Mode ``` -export DATADIR_PATH=/home/ghidrausr/datadir docker run \ --env MODE=bsim-server \ --rm \ @@ -151,7 +154,7 @@ docker run \ --env MODE=bsim \ --rm \ -it \ - ghidra/ghidra:RELEASE \ + ghidra/ghidra: \ generatesigs ghidra://ghidrasvr/demo /home/ghidra \ --bsim postgresql://bsimsvr/demo \ --commit --overwrite \ @@ -201,3 +204,6 @@ Passing no arguments to the pyghidra headless analyzer will result in the help m This use case is very similar to the headless mode's example with the added benefit of being able to utilize python3 for Ghidra Scripts. +Again, in this example, appropriate permissions and group assignment for `/path/to/myproject` and `/path/to/mybinary` are necessary +in order to not run into permissions issues. + diff --git a/docker/build-docker-image.sh b/docker/build-docker-image.sh new file mode 100755 index 0000000000..bb9e6754f0 --- /dev/null +++ b/docker/build-docker-image.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +## ### +# IP: GHIDRA +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## + +#------------------------------- +# Build Docker Image +#------------------------------- + +# check if docker is installed +if which docker &> /dev/null; then + echo "Docker installation found" +else + echo "Docker installation not found. Please install docker." + exit 1 +fi + + +SCRIPT_FILE="$(readlink -f "$0" 2>/dev/null || readlink "$0" 2>/dev/null || echo "$0")" +SCRIPT_DIR="${SCRIPT_FILE%/*}" + +if [! -e $SCRIPT_DIR/../ghidraRun] then + echo "ERROR: This script must be run on a built release of Ghidra." + exit 1 +fi + +if [! -e $SCRIPT_DIR/../Ghidra/application.properties] then + echo "ERROR: $SCRIPT_DIR/../Ghidra/application.properties does not exist. Dockerized Ghidra needs this file to get tagging information." + exit 1 +fi + +# get appropriate tag +source <(sed 's/\.\|\(=.*\)/_\1/g;s/_=/=/' $SCRIPT_DIR/../Ghidra/application.properties) &> /dev/null +VERSION=${application_version} +RELEASE=${application_release_name} +TAG=${VERSION}_${RELEASE} + +# build docker image +IMAGE=ghidra/ghidra:$TAG +echo building image $IMAGE +docker build -f $SCRIPT_DIR/../docker/Dockerfile -t $IMAGE . 2>&1 | tee $SCRIPT_DIR/../docker/docker.log +if [ $? != 0 ]; then + echo "ERROR: Docker Image Build Failed! See docker/docker.log to identify build error" + exit 1 +fi +echo "Docker Image built ($IMAGE). See docker/README.md for usage instructions." +exit 0 + diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 7e16af8427..e19a7372d1 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -39,10 +39,15 @@ elif [[ $MODE == "bsim" ]] then elif [[ $MODE == "bsim-server" ]] then LAUNCH_MODE=${LAUNCH_MODE:=fg} VMARG_LIST=${VMARG_LIST:="-Djava.awt.headless=true -Xshare:off"} - mkdir -p $DATADIR_PATH - /ghidra/support/launch.sh "$LAUNCH_MODE" jdk BSimControl "$MAXMEM" "$VMARG_LIST" ghidra.features.bsim.query.BSimControlLaunchable start $@ - # need to do this since the launched process is not blocking terminal exit - while ! tail -f $DATADIR_PATH/logfile; do sleep 1 ; done + if [[ ! $# -eq 0 ]] then + /ghidra/support/launch.sh "$LAUNCH_MODE" jdk BSimControl "$MAXMEM" "$VMARG_LIST" ghidra.features.bsim.query.BSimControlLaunchable start $@ + # need to do this since the launched process is not blocking terminal exit + while ! tail -f $1/logfile; do sleep 1 ; done + else + echo "ERROR: Must pass args for bsim_ctl start command." + /ghidra/support/launch.sh "$LAUNCH_MODE" jdk BSimControl "$MAXMEM" "$VMARG_LIST" ghidra.features.bsim.query.BSimControlLaunchable start $@ + exit 1 + fi elif [[ $MODE == "pyghidra" ]] then # Add optional JVM args inside the quotes VMARG_LIST=${VMARG_LIST:=""}