diff --git a/pkgs/tools/security/ghidra/0002-Load-nix-extensions.patch b/pkgs/tools/security/ghidra/0002-Load-nix-extensions.patch new file mode 100644 index 000000000000..0e87aa71a407 --- /dev/null +++ b/pkgs/tools/security/ghidra/0002-Load-nix-extensions.patch @@ -0,0 +1,15 @@ +diff --git a/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java b/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java +index ea12a661f0..da7779b07f 100644 +--- a/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java ++++ b/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java +@@ -36,6 +36,10 @@ public class ApplicationUtilities { + */ + public static Collection findDefaultApplicationRootDirs() { + Collection applicationRootDirs = new ArrayList<>(); ++ String nixGhidraHome = System.getenv("NIX_GHIDRAHOME"); ++ if (nixGhidraHome != null) { ++ applicationRootDirs.add(new ResourceFile(nixGhidraHome)); ++ }; + ResourceFile applicationRootDir = findPrimaryApplicationRootDir(); + if (applicationRootDir != null) { + applicationRootDirs.add(applicationRootDir); diff --git a/pkgs/tools/security/ghidra/0003-Remove-build-datestamp.patch b/pkgs/tools/security/ghidra/0003-Remove-build-datestamp.patch new file mode 100644 index 000000000000..0a8948701502 --- /dev/null +++ b/pkgs/tools/security/ghidra/0003-Remove-build-datestamp.patch @@ -0,0 +1,26 @@ +diff --git a/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle b/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle +index bc194f219..94b00fabd 100644 +--- a/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle ++++ b/Ghidra/RuntimeScripts/Common/support/buildExtension.gradle +@@ -82,7 +82,7 @@ dependencies { + helpPath fileTree(dir: ghidraDir + '/Features/Base', include: "**/Base.jar") + } + +-def ZIP_NAME_PREFIX = "${DISTRO_PREFIX}_${RELEASE_NAME}_${getCurrentDate()}" ++def ZIP_NAME_PREFIX = "${DISTRO_PREFIX}_${RELEASE_NAME}" + def DISTRIBUTION_DIR = file("dist") + + def pathInZip = "${project.name}" +diff --git a/gradle/root/distribution.gradle b/gradle/root/distribution.gradle +index f44c8267b..f6231c417 100644 +--- a/gradle/root/distribution.gradle ++++ b/gradle/root/distribution.gradle +@@ -32,7 +32,7 @@ apply from: "$rootProject.projectDir/gradle/support/sbom.gradle" + def currentPlatform = getCurrentPlatformName() + def PROJECT_DIR = file (rootProject.projectDir.absolutePath) + ext.DISTRIBUTION_DIR = file("$buildDir/dist") +-ext.ZIP_NAME_PREFIX = "${rootProject.DISTRO_PREFIX}_${rootProject.BUILD_DATE_SHORT}" ++ext.ZIP_NAME_PREFIX = "${rootProject.DISTRO_PREFIX}" + ext.ZIP_DIR_PREFIX = "${rootProject.DISTRO_PREFIX}" + ext.ALL_REPOS = [rootProject.file('.').getName()] + diff --git a/pkgs/tools/security/ghidra/build-extension.nix b/pkgs/tools/security/ghidra/build-extension.nix new file mode 100644 index 000000000000..dfec17d8834a --- /dev/null +++ b/pkgs/tools/security/ghidra/build-extension.nix @@ -0,0 +1,78 @@ +{ lib +, stdenv +, unzip +, jdk +, gradle +, ghidra +}: + +let + metaCommon = oldMeta: + oldMeta // (with lib; { + maintainers = (oldMeta.maintainers or []) ++ (with maintainers; [ emilytrau ]); + platforms = oldMeta.platforms or ghidra.meta.platforms; + }); + + buildGhidraExtension = { + pname, nativeBuildInputs ? [], meta ? { }, ... + }@args: + stdenv.mkDerivation (args // { + nativeBuildInputs = nativeBuildInputs ++ [ + unzip + jdk + gradle + ]; + + buildPhase = args.buildPhase or '' + runHook preBuild + + # Set project name, otherwise defaults to directory name + echo -e '\nrootProject.name = "${pname}"' >> settings.gradle + + export GRADLE_USER_HOME=$(mktemp -d) + gradle \ + --offline \ + --no-daemon \ + -PGHIDRA_INSTALL_DIR=${ghidra}/lib/ghidra + + runHook postBuild + ''; + + installPhase = args.installPhase or '' + runHook preInstall + + mkdir -p $out/lib/ghidra/Ghidra/Extensions + unzip -d $out/lib/ghidra/Ghidra/Extensions dist/*.zip + + runHook postInstall + ''; + + meta = metaCommon meta; + }); + + buildGhidraScripts = { pname, meta ? { }, ... }@args: + stdenv.mkDerivation (args // { + installPhase = '' + runHook preInstall + + GHIDRA_HOME=$out/lib/ghidra/Ghidra/Extensions/${pname} + mkdir -p $GHIDRA_HOME + cp -r . $GHIDRA_HOME/ghidra_scripts + + touch $GHIDRA_HOME/Module.manifest + cat <<'EOF' > extension.properties + name=${pname} + description=${meta.description or ""} + author= + createdOn= + version=${lib.getVersion ghidra} + + EOF + + runHook postInstall + ''; + + meta = metaCommon meta; + }); +in + { inherit buildGhidraExtension buildGhidraScripts; } diff --git a/pkgs/tools/security/ghidra/build.nix b/pkgs/tools/security/ghidra/build.nix index f8f0fb3ae5a8..fe4d37c6a9f2 100644 --- a/pkgs/tools/security/ghidra/build.nix +++ b/pkgs/tools/security/ghidra/build.nix @@ -1,6 +1,7 @@ { stdenv , fetchFromGitHub , lib +, callPackage , gradle_7 , perl , makeWrapper @@ -10,6 +11,7 @@ , icoutils , xcbuild , protobuf +, ghidra-extensions }: let @@ -17,15 +19,40 @@ let pname = "ghidra"; version = "11.0.3"; + releaseName = "NIX"; + distroPrefix = "ghidra_${version}_${releaseName}"; src = fetchFromGitHub { owner = "NationalSecurityAgency"; repo = "Ghidra"; rev = "Ghidra_${version}_build"; - hash = "sha256-Id595aKYHP1R3Zw9sV1oL32nAUAr7D/K4wn6Zs7q3Jo="; + hash = "sha256-IiLxaJvfJcK275FDZEsUCGp7haJjp8O2fUIoM4F9H30="; + # populate values that require us to use git. By doing this in postFetch we + # can delete .git afterwards and maintain better reproducibility of the src. + leaveDotGit = true; + postFetch = '' + cd "$out" + git rev-parse HEAD > $out/COMMIT + # 1970-Jan-01 + date -u -d "@$(git log -1 --pretty=%ct)" "+%Y-%b-%d" > $out/SOURCE_DATE_EPOCH + # 19700101 + date -u -d "@$(git log -1 --pretty=%ct)" "+%Y%m%d" > $out/SOURCE_DATE_EPOCH_SHORT + find "$out" -name .git -print0 | xargs -0 rm -rf + ''; }; gradle = gradle_7; + patches = [ + # Use our own protoc binary instead of the prebuilt one + ./0001-Use-protobuf-gradle-plugin.patch + + # Override installation directory to allow loading extensions + ./0002-Load-nix-extensions.patch + + # Remove build dates from output filenames for easier reference + ./0003-Remove-build-datestamp.patch + ]; + desktopItem = makeDesktopItem { name = "ghidra"; exec = "ghidra"; @@ -35,7 +62,25 @@ let categories = [ "Development" ]; }; - # postPatch scripts. + postPatch = '' + # Set name of release (eg. PUBLIC, DEV, etc.) + sed -i -e 's/application\.release\.name=.*/application.release.name=${releaseName}/' Ghidra/application.properties + + # Set build date and git revision + echo "application.build.date=$(cat SOURCE_DATE_EPOCH)" >> Ghidra/application.properties + echo "application.build.date.short=$(cat SOURCE_DATE_EPOCH_SHORT)" >> Ghidra/application.properties + echo "application.revision.ghidra=$(cat COMMIT)" >> Ghidra/application.properties + + # Tells ghidra to use our own protoc binary instead of the prebuilt one. + cat >>Ghidra/Debug/Debugger-gadp/build.gradle <>build.gradle < /nix/store/3yn0rbnz5mbrxf0x70jbjq73wgkszr5c-ghidra-with-extensions-10.2.2 + */ + withExtensions = f: (symlinkJoin { + name = "${ghidra.pname}-with-extensions-${lib.getVersion ghidra}"; + paths = (f allExtensions); + nativeBuildInputs = [ makeBinaryWrapper ]; + postBuild = '' + makeWrapper '${ghidra}/bin/ghidra' "$out/bin/ghidra" \ + --set NIX_GHIDRAHOME "$out/lib/ghidra/Ghidra" + ''; + inherit (ghidra) meta; + }); +in + withExtensions diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 160b65391fea..c34cc3d2ba9f 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -5384,6 +5384,8 @@ with pkgs; protobuf = protobuf_21; }; + ghidra-extensions = recurseIntoAttrs (callPackage ../tools/security/ghidra/extensions.nix { }); + ghidra-bin = callPackage ../tools/security/ghidra { }; gh2md = callPackage ../tools/backup/gh2md { };