diff --git a/README.md b/README.md index e898a66..e0eb582 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,10 @@ the service user and group a variant of ghidra built with a headless openjdk, intended to reduce closure size for server operation +### [`ghidra_xenia`](./pkgs/ghidra-xenia/default.nix) + +preview version of ghidra with my nix patches + ### [`ocamlPackages.ppx_unicode`](./pkgs/ocaml/ppx_unicode) opinionated ppx for string literals: diff --git a/default.nix b/default.nix index 8c09479..c62d8c4 100644 --- a/default.nix +++ b/default.nix @@ -15,6 +15,12 @@ openjdk17 = prev.openjdk17_headless; }; + ghidra = final.callPackage ./pkgs/ghidra-xenia/build.nix { + protobuf = final.protobuf_21; + }; + ghidra-extensions = final.lib.recurseIntoAttrs (final.callPackage ./pkgs/ghidra-xenia/extensions.nix { }); + ghidra-bin = final.callPackage ./pkgs/ghidra-xenia { }; + ocamlPackages = prev.ocamlPackages.overrideScope (ofinal: oprev: { ppx_unicode = ofinal.callPackage ./pkgs/ocaml/ppx_unicode {}; xlog = ofinal.callPackage ./pkgs/ocaml/xlog {}; diff --git a/pkgs/ghidra-xenia/0001-Use-protobuf-gradle-plugin.patch b/pkgs/ghidra-xenia/0001-Use-protobuf-gradle-plugin.patch new file mode 100644 index 0000000..7378878 --- /dev/null +++ b/pkgs/ghidra-xenia/0001-Use-protobuf-gradle-plugin.patch @@ -0,0 +1,214 @@ +From ffb6777d58f068db7e14372415154cd93f77766e Mon Sep 17 00:00:00 2001 +From: roblabla +Date: Wed, 31 Jan 2024 13:19:55 +0100 +Subject: [PATCH] Use com.google.protobuf:protobuf-gradle-plugin + +--- + Ghidra/Debug/Debugger-gadp/build.gradle | 7 +- + Ghidra/Debug/Debugger-isf/build.gradle | 8 +- + Ghidra/Debug/Debugger-rmi-trace/build.gradle | 14 +-- + build.gradle | 6 ++ + gradle/debugger/hasProtobuf.gradle | 94 -------------------- + 5 files changed, 26 insertions(+), 103 deletions(-) + +diff --git a/Ghidra/Debug/Debugger-gadp/build.gradle b/Ghidra/Debug/Debugger-gadp/build.gradle +index 9e1c57faf..3a3242eb5 100644 +--- a/Ghidra/Debug/Debugger-gadp/build.gradle ++++ b/Ghidra/Debug/Debugger-gadp/build.gradle +@@ -18,11 +18,16 @@ apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" + apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" + apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" + apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" +-apply from: "${rootProject.projectDir}/gradle/debugger/hasProtobuf.gradle" ++apply plugin: 'com.google.protobuf' + + apply plugin: 'eclipse' + eclipse.project.name = 'Debug Debugger-gadp' + ++buildscript { ++ dependencies { ++ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18' ++ } ++} + dependencies { + api project(':Framework-AsyncComm') + api project(':Framework-Debugging') +diff --git a/Ghidra/Debug/Debugger-isf/build.gradle b/Ghidra/Debug/Debugger-isf/build.gradle +index d135294a0..785681ca2 100644 +--- a/Ghidra/Debug/Debugger-isf/build.gradle ++++ b/Ghidra/Debug/Debugger-isf/build.gradle +@@ -18,11 +18,15 @@ apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" + apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" + apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" + apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" +-apply from: "${rootProject.projectDir}/gradle/debugger/hasProtobuf.gradle" +- ++apply plugin: 'com.google.protobuf' + apply plugin: 'eclipse' + eclipse.project.name = 'Debug Debugger-isf' + ++buildscript { ++ dependencies { ++ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18' ++ } ++} + dependencies { + api project(':Framework-AsyncComm') + api project(':Framework-Debugging') +diff --git a/Ghidra/Debug/Debugger-rmi-trace/build.gradle b/Ghidra/Debug/Debugger-rmi-trace/build.gradle +index 40fbc17ab..7517ffe6e 100644 +--- a/Ghidra/Debug/Debugger-rmi-trace/build.gradle ++++ b/Ghidra/Debug/Debugger-rmi-trace/build.gradle +@@ -18,12 +18,17 @@ apply from: "${rootProject.projectDir}/gradle/javaProject.gradle" + apply from: "${rootProject.projectDir}/gradle/jacocoProject.gradle" + apply from: "${rootProject.projectDir}/gradle/javaTestProject.gradle" + apply from: "${rootProject.projectDir}/gradle/distributableGhidraModule.gradle" +-apply from: "${rootProject.projectDir}/gradle/debugger/hasProtobuf.gradle" ++apply plugin: 'com.google.protobuf' + apply from: "${rootProject.projectDir}/gradle/debugger/hasPythonPackage.gradle" + + apply plugin: 'eclipse' + eclipse.project.name = 'Debug Debugger-rmi-trace' + ++buildscript { ++ dependencies { ++ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18' ++ } ++} + dependencies { + api project(':Pty') + api project(':Debugger') +@@ -44,12 +49,9 @@ task generateProtoPy { + ext.outdir = file("build/generated/source/proto/main/py") + outputs.dir(outdir) + inputs.files(src) +- dependsOn(configurations.protocArtifact) ++ dependsOn(protobuf.generateProtoTasks.all()) + doLast { +- def exe = configurations.protocArtifact.first() +- if (!isCurrentWindows()) { +- exe.setExecutable(true) +- } ++ def exe = protobuf.tools.protoc.path + exec { + commandLine exe, "--python_out=$outdir", "-I$srcdir" + args src +diff --git a/build.gradle b/build.gradle +index b0c717fb1..5f56506a5 100644 +--- a/build.gradle ++++ b/build.gradle +@@ -74,6 +74,12 @@ if (flatRepo.isDirectory()) { + jcenter() + flatDir name: "flat", dirs:["$flatRepo"] + } ++ buildscript { ++ repositories { ++ mavenLocal() ++ mavenCentral() ++ } ++ } + } + } + else { +diff --git a/gradle/debugger/hasProtobuf.gradle b/gradle/debugger/hasProtobuf.gradle +index 23b4ce74b..e69de29bb 100644 +--- a/gradle/debugger/hasProtobuf.gradle ++++ b/gradle/debugger/hasProtobuf.gradle +@@ -1,94 +0,0 @@ +-/* ### +- * 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. +- */ +-/*plugins { +- id 'com.google.protobuf' version '0.8.10' +-}*/ +- +-configurations { +- allProtocArtifacts +- protocArtifact +-} +- +-def platform = getCurrentPlatformName() +- +- +-dependencies { +- allProtocArtifacts 'com.google.protobuf:protoc:3.21.8:windows-x86_64@exe' +- allProtocArtifacts 'com.google.protobuf:protoc:3.21.8:linux-x86_64@exe' +- allProtocArtifacts 'com.google.protobuf:protoc:3.21.8:linux-aarch_64@exe' +- allProtocArtifacts 'com.google.protobuf:protoc:3.21.8:osx-x86_64@exe' +- allProtocArtifacts 'com.google.protobuf:protoc:3.21.8:osx-aarch_64@exe' +- +- if (isCurrentWindows()) { +- protocArtifact 'com.google.protobuf:protoc:3.21.8:windows-x86_64@exe' +- } +- if (isCurrentLinux()) { +- if (platform.endsWith("x86_64")) { +- protocArtifact 'com.google.protobuf:protoc:3.21.8:linux-x86_64@exe' +- } +- else { +- protocArtifact 'com.google.protobuf:protoc:3.21.8:linux-aarch_64@exe' +- } +- } +- if (isCurrentMac()) { +- if (platform.endsWith("x86_64")) { +- protocArtifact 'com.google.protobuf:protoc:3.21.8:osx-x86_64@exe' +- } +- else { +- protocArtifact 'com.google.protobuf:protoc:3.21.8:osx-aarch_64@exe' +- } +- } +-} +- +-/*protobuf { +- protoc { +- artifact = 'com.google.protobuf:protoc:3.21.8' +- } +-}*/ +- +-task generateProto { +- ext.srcdir = file("src/main/proto") +- ext.src = fileTree(srcdir) { +- include "**/*.proto" +- } +- ext.outdir = file("build/generated/source/proto/main/java") +- outputs.dir(outdir) +- inputs.files(src) +- dependsOn(configurations.protocArtifact) +- doLast { +- def exe = configurations.protocArtifact.first() +- if (!isCurrentWindows()) { +- exe.setExecutable(true) +- } +- exec { +- commandLine exe, "--java_out=$outdir", "-I$srcdir" +- args src +- } +- } +-} +- +-tasks.compileJava.dependsOn(tasks.generateProto) +-tasks.eclipse.dependsOn(tasks.generateProto) +-rootProject.tasks.prepDev.dependsOn(tasks.generateProto) +- +-sourceSets { +- main { +- java { +- srcDir tasks.generateProto.outdir +- } +- } +-} +-zipSourceSubproject.dependsOn generateProto +-- +2.42.0 + diff --git a/pkgs/ghidra-xenia/0002-Load-nix-extensions.patch b/pkgs/ghidra-xenia/0002-Load-nix-extensions.patch new file mode 100644 index 0000000..0e87aa7 --- /dev/null +++ b/pkgs/ghidra-xenia/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/ghidra-xenia/0003-Remove-build-datestamp.patch b/pkgs/ghidra-xenia/0003-Remove-build-datestamp.patch new file mode 100644 index 0000000..0a89487 --- /dev/null +++ b/pkgs/ghidra-xenia/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/ghidra-xenia/build-extension.nix b/pkgs/ghidra-xenia/build-extension.nix new file mode 100644 index 0000000..373f357 --- /dev/null +++ b/pkgs/ghidra-xenia/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; [ vringar ]); + 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/ghidra-xenia/build.nix b/pkgs/ghidra-xenia/build.nix new file mode 100644 index 0000000..3c73960 --- /dev/null +++ b/pkgs/ghidra-xenia/build.nix @@ -0,0 +1,266 @@ +{ + stdenv, + fetchFromGitHub, + lib, + callPackage, + gradle_7, + perl, + makeBinaryWrapper, + openjdk17, + unzip, + makeDesktopItem, + copyDesktopItems, + desktopToDarwinBundle, + xcbuild, + protobuf, + ghidra-extensions, + python3, + python3Packages, +}: + +let + pkg_path = "$out/lib/ghidra"; + pname = "ghidra"; + version = "11.1.1"; + + releaseName = "NIX"; + distroPrefix = "ghidra_${version}_${releaseName}"; + src = fetchFromGitHub { + owner = "NationalSecurityAgency"; + repo = "Ghidra"; + rev = "Ghidra_${version}_build"; + hash = "sha256-t96FcAK3JwO66dOf4OhpOfU8CQfAczfF61Cg7m+B3fA="; + # 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 + ]; + + 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 < + subProject.buildscript.configurations.each { configuration -> + resolveConfiguration(subProject, configuration, "buildscript config \''${configuration.name}") + } + subProject.configurations.each { configuration -> + resolveConfiguration(subProject, configuration, "config \''${configuration.name}") + } + } + } + } + void resolveConfiguration(subProject, configuration, name) { + if (configuration.canBeResolved) { + logger.info("Resolving project {} {}", subProject.name, name) + configuration.resolve() + } + } + HERE + ''; + + # fake build to pre-download deps into fixed-output derivation + # Taken from mindustry derivation. + deps = stdenv.mkDerivation { + pname = "${pname}-deps"; + inherit version src patches; + + postPatch = addResolveStep; + + nativeBuildInputs = [ + gradle + perl + ] ++ lib.optional stdenv.isDarwin xcbuild; + buildPhase = '' + runHook preBuild + export HOME="$NIX_BUILD_TOP/home" + mkdir -p "$HOME" + export JAVA_TOOL_OPTIONS="-Duser.home='$HOME'" + export GRADLE_USER_HOME="$HOME/.gradle" + + # First, fetch the static dependencies. + gradle --no-daemon --info -Dorg.gradle.java.home=${openjdk17} -I gradle/support/fetchDependencies.gradle init + + # Then, fetch the maven dependencies. + gradle --no-daemon --info -Dorg.gradle.java.home=${openjdk17} resolveDependencies + runHook postBuild + ''; + # perl code mavenizes pathes (com.squareup.okio/okio/1.13.0/a9283170b7305c8d92d25aff02a6ab7e45d06cbe/okio-1.13.0.jar -> com/squareup/okio/okio/1.13.0/okio-1.13.0.jar) + installPhase = '' + runHook preInstall + find $GRADLE_USER_HOME/caches/modules-2 -type f -regex '.*\.\(jar\|pom\)' \ + | perl -pe 's#(.*/([^/]+)/([^/]+)/([^/]+)/[0-9a-f]{30,40}/([^/\s]+))$# ($x = $2) =~ tr|\.|/|; "install -Dm444 $1 \$out/maven/$x/$3/$4/$5" #e' \ + | sh + cp -r dependencies $out/dependencies + runHook postInstall + ''; + outputHashAlgo = "sha256"; + outputHashMode = "recursive"; + outputHash = "sha256-66gL4UFlBUo2JIEOXoF6tFvXtBdEX4b2MeSrV1b6Vg4="; + }; +in +stdenv.mkDerivation (finalAttrs: { + inherit + pname + version + src + patches + postPatch + ; + + # Don't create .orig files if the patch isn't an exact match. + patchFlags = [ + "--no-backup-if-mismatch" + "-p1" + ]; + + desktopItems = [ + (makeDesktopItem { + name = "ghidra"; + exec = "ghidra"; + icon = "ghidra"; + desktopName = "Ghidra"; + genericName = "Ghidra Software Reverse Engineering Suite"; + categories = [ "Development" ]; + terminal = false; + startupWMClass = "ghidra-Ghidra"; + }) + ]; + + nativeBuildInputs = + [ + gradle + unzip + makeBinaryWrapper + copyDesktopItems + protobuf + python3 + python3Packages.pip + ] + ++ lib.optionals stdenv.isDarwin [ + xcbuild + desktopToDarwinBundle + ]; + + dontStrip = true; + + __darwinAllowLocalNetworking = true; + + buildPhase = '' + runHook preBuild + export HOME="$NIX_BUILD_TOP/home" + mkdir -p "$HOME" + export JAVA_TOOL_OPTIONS="-Duser.home='$HOME'" + + ln -s ${deps}/dependencies dependencies + + sed -i "s#mavenLocal()#mavenLocal(); maven { url '${deps}/maven' }#g" build.gradle + + gradle --offline --no-daemon --info -Dorg.gradle.java.home=${openjdk17} buildGhidra + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p "${pkg_path}" "$out/share/applications" + + ZIP=build/dist/$(ls build/dist) + echo $ZIP + unzip $ZIP -d ${pkg_path} + f=("${pkg_path}"/*) + mv "${pkg_path}"/*/* "${pkg_path}" + rmdir "''${f[@]}" + + for f in Ghidra/Framework/Gui/src/main/resources/images/GhidraIcon*.png; do + res=$(basename "$f" ".png" | cut -d"_" -f3 | cut -c11-) + install -Dm444 "$f" "$out/share/icons/hicolor/''${res}x''${res}/apps/ghidra.png" + done; + # improved macOS icon support + install -Dm444 Ghidra/Framework/Gui/src/main/resources/images/GhidraIcon64.png $out/share/icons/hicolor/32x32@2/apps/ghidra.png + + runHook postInstall + ''; + + postFixup = '' + mkdir -p "$out/bin" + ln -s "${pkg_path}/ghidraRun" "$out/bin/ghidra" + wrapProgram "${pkg_path}/support/launch.sh" \ + --set-default NIX_GHIDRAHOME "${pkg_path}/Ghidra" \ + --prefix PATH : ${lib.makeBinPath [ openjdk17 ]} + ''; + + passthru = { + inherit releaseName distroPrefix; + inherit (ghidra-extensions.override { ghidra = finalAttrs.finalPackage; }) + buildGhidraExtension + buildGhidraScripts + ; + + withExtensions = callPackage ./with-extensions.nix { ghidra = finalAttrs.finalPackage; }; + }; + + meta = with lib; { + changelog = "https://htmlpreview.github.io/?https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_${finalAttrs.version}_build/Ghidra/Configurations/Public_Release/src/global/docs/ChangeHistory.html"; + description = "Software reverse engineering (SRE) suite of tools"; + mainProgram = "ghidra"; + homepage = "https://ghidra-sre.org/"; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + sourceProvenance = with sourceTypes; [ + fromSource + binaryBytecode # deps + ]; + license = licenses.asl20; + maintainers = with maintainers; [ + roblabla + vringar + ]; + broken = stdenv.isDarwin && stdenv.isx86_64; + }; +}) diff --git a/pkgs/ghidra-xenia/default.nix b/pkgs/ghidra-xenia/default.nix new file mode 100644 index 0000000..fc9b4d2 --- /dev/null +++ b/pkgs/ghidra-xenia/default.nix @@ -0,0 +1,83 @@ +{ stdenv +, fetchzip +, lib +, makeWrapper +, autoPatchelfHook +, openjdk17 +, pam +, makeDesktopItem +, icoutils +}: + +let + + pkg_path = "$out/lib/ghidra"; + + desktopItem = makeDesktopItem { + name = "ghidra"; + exec = "ghidra"; + icon = "ghidra"; + desktopName = "Ghidra"; + genericName = "Ghidra Software Reverse Engineering Suite"; + categories = [ "Development" ]; + terminal = false; + startupWMClass = "ghidra-Ghidra"; + }; + +in stdenv.mkDerivation rec { + pname = "ghidra"; + version = "10.4"; + versiondate = "20230928"; + + src = fetchzip { + url = "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_${version}_build/ghidra_${version}_PUBLIC_${versiondate}.zip"; + hash = "sha256-IiAQ9OKmr8ZgqmGftuW0ITdG06fb9Lr30n2H9GArctk="; + }; + + nativeBuildInputs = [ + makeWrapper + icoutils + ] + ++ lib.optionals stdenv.isLinux [ autoPatchelfHook ]; + + buildInputs = [ + stdenv.cc.cc.lib + pam + ]; + + dontStrip = true; + + installPhase = '' + mkdir -p "${pkg_path}" + mkdir -p "${pkg_path}" "$out/share/applications" + cp -a * "${pkg_path}" + ln -s ${desktopItem}/share/applications/* $out/share/applications + + icotool -x "${pkg_path}/support/ghidra.ico" + rm ghidra_4_40x40x32.png + for f in ghidra_*.png; do + res=$(basename "$f" ".png" | cut -d"_" -f3 | cut -d"x" -f1-2) + mkdir -pv "$out/share/icons/hicolor/$res/apps" + mv "$f" "$out/share/icons/hicolor/$res/apps/ghidra.png" + done; + ''; + + postFixup = '' + mkdir -p "$out/bin" + ln -s "${pkg_path}/ghidraRun" "$out/bin/ghidra" + + wrapProgram "${pkg_path}/support/launch.sh" \ + --prefix PATH : ${lib.makeBinPath [ openjdk17 ]} + ''; + + meta = with lib; { + description = "Software reverse engineering (SRE) suite of tools developed by NSA's Research Directorate in support of the Cybersecurity mission"; + mainProgram = "ghidra"; + homepage = "https://github.com/NationalSecurityAgency/ghidra"; + platforms = [ "x86_64-linux" "x86_64-darwin" ]; + sourceProvenance = with sourceTypes; [ binaryBytecode ]; + license = licenses.asl20; + maintainers = with maintainers; [ ck3d govanify mic92 ]; + }; + +} diff --git a/pkgs/ghidra-xenia/extensions.nix b/pkgs/ghidra-xenia/extensions.nix new file mode 100644 index 0000000..3f30dd8 --- /dev/null +++ b/pkgs/ghidra-xenia/extensions.nix @@ -0,0 +1,14 @@ +{ lib, newScope, callPackage, ghidra }: + +lib.makeScope newScope (self: { + inherit (callPackage ./build-extension.nix { inherit ghidra; }) buildGhidraExtension buildGhidraScripts; + + ghidraninja-ghidra-scripts = self.callPackage ./extensions/ghidraninja-ghidra-scripts { }; + + gnudisassembler = self.callPackage ./extensions/gnudisassembler { inherit ghidra; }; + + machinelearning = self.callPackage ./extensions/machinelearning { inherit ghidra; }; + + sleighdevtools = self.callPackage ./extensions/sleighdevtools { inherit ghidra; }; + +}) diff --git a/pkgs/ghidra-xenia/extensions/ghidraninja-ghidra-scripts/default.nix b/pkgs/ghidra-xenia/extensions/ghidraninja-ghidra-scripts/default.nix new file mode 100644 index 0000000..6c5e2ec --- /dev/null +++ b/pkgs/ghidra-xenia/extensions/ghidraninja-ghidra-scripts/default.nix @@ -0,0 +1,36 @@ +{ lib +, fetchFromGitHub +, buildGhidraScripts +, binwalk +, swift +, yara +}: + +buildGhidraScripts { + pname = "ghidraninja-ghidra-scripts"; + version = "unstable-2020-10-07"; + + src = fetchFromGitHub { + owner = "ghidraninja"; + repo = "ghidra_scripts"; + rev = "99f2a8644a29479618f51e2d4e28f10ba5e9ac48"; + sha256 = "aElx0mp66/OHQRfXwTkqdLL0gT2T/yL00bOobYleME8="; + }; + + postPatch = '' + # Replace subprocesses with store versions + substituteInPlace binwalk.py --replace-fail 'subprocess.call(["binwalk"' 'subprocess.call(["${binwalk}/bin/binwalk"' + substituteInPlace swift_demangler.py --replace-fail '"swift"' '"${swift}/bin/swift"' + substituteInPlace yara.py --replace-fail 'subprocess.check_output(["yara"' 'subprocess.check_output(["${yara}/bin/yara"' + substituteInPlace YaraSearch.py --replace-fail '"yara "' '"${yara}/bin/yara "' + ''; + + meta = with lib; { + description = "Scripts for the Ghidra software reverse engineering suite"; + homepage = "https://github.com/ghidraninja/ghidra_scripts"; + license = with licenses; [ + gpl3Only + gpl2Only + ]; + }; +} diff --git a/pkgs/ghidra-xenia/extensions/gnudisassembler/default.nix b/pkgs/ghidra-xenia/extensions/gnudisassembler/default.nix new file mode 100644 index 0000000..7ca4b05 --- /dev/null +++ b/pkgs/ghidra-xenia/extensions/gnudisassembler/default.nix @@ -0,0 +1,71 @@ +{ lib +, stdenv +, fetchurl +, buildGhidraExtension +, ghidra +, flex +, bison +, texinfo +, perl +, zlib +, xcbuild +}: + +let + # Incorporates source from binutils + # https://github.com/NationalSecurityAgency/ghidra/blob/7ab9bf6abffb6938d61d072040fc34ad3331332b/GPL/GnuDisassembler/build.gradle#L34-L35 + binutils-version = "2.41"; + binutils-src = fetchurl { + url = "mirror://gnu/binutils/binutils-${binutils-version}.tar.bz2"; + sha256 = "sha256-pMS+wFL3uDcAJOYDieGUN38/SLVmGEGOpRBn9nqqsws="; + }; +in +buildGhidraExtension { + pname = "gnudisassembler"; + version = lib.getVersion ghidra; + + src = "${ghidra}/lib/ghidra/Extensions/Ghidra/${ghidra.distroPrefix}_GnuDisassembler.zip"; + + postPatch = '' + ln -s ${binutils-src} binutils-${binutils-version}.tar.bz2 + ''; + + # Don't modify ELF stub resources + dontPatchELF = true; + dontStrip = true; + + __darwinAllowLocalNetworking = true; + + nativeBuildInputs = [ + flex + bison + texinfo + perl + ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ + xcbuild + ]; + + buildInputs = [ + zlib + ]; + + installPhase = '' + runHook preInstall + + EXTENSIONS_ROOT=$out/lib/ghidra/Ghidra/Extensions + mkdir -p $EXTENSIONS_ROOT + unzip -d $EXTENSIONS_ROOT $src + + mkdir -p $EXTENSIONS_ROOT/GnuDisassembler/build + cp -r build/os $EXTENSIONS_ROOT/GnuDisassembler/build/ + + runHook postInstall + ''; + + meta = with lib; { + description = "Leverage the binutils disassembler capabilities for various processors"; + homepage = "https://ghidra-sre.org/"; + downloadPage = "https://github.com/NationalSecurityAgency/ghidra/tree/master/GPL/GnuDisassembler"; + license = licenses.gpl2Only; + }; +} diff --git a/pkgs/ghidra-xenia/extensions/machinelearning/default.nix b/pkgs/ghidra-xenia/extensions/machinelearning/default.nix new file mode 100644 index 0000000..ba1e315 --- /dev/null +++ b/pkgs/ghidra-xenia/extensions/machinelearning/default.nix @@ -0,0 +1,34 @@ +{ lib +, buildGhidraExtension +, ghidra +}: + +buildGhidraExtension { + pname = "machinelearning"; + version = lib.getVersion ghidra; + + src = "${ghidra}/lib/ghidra/Extensions/Ghidra/${ghidra.distroPrefix}_MachineLearning.zip"; + dontUnpack = true; + + # Built as part ghidra + dontBuild = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out/lib/ghidra/Ghidra/Extensions + unzip -d $out/lib/ghidra/Ghidra/Extensions $src + + runHook postInstall + ''; + + meta = with lib; { + inherit (ghidra.meta) homepage license; + description = "Finds functions using ML"; + downloadPage = "https://github.com/NationalSecurityAgency/ghidra/tree/master/Ghidra/Extensions/MachineLearning"; + sourceProvenance = with sourceTypes; [ + fromSource + binaryBytecode # deps + ]; + }; +} diff --git a/pkgs/ghidra-xenia/extensions/sleighdevtools/default.nix b/pkgs/ghidra-xenia/extensions/sleighdevtools/default.nix new file mode 100644 index 0000000..d8fd018 --- /dev/null +++ b/pkgs/ghidra-xenia/extensions/sleighdevtools/default.nix @@ -0,0 +1,40 @@ +{ lib +, buildGhidraExtension +, ghidra +, python3 +}: + +buildGhidraExtension { + pname = "sleighdevtools"; + version = lib.getVersion ghidra; + + src = "${ghidra}/lib/ghidra/Extensions/Ghidra/${ghidra.distroPrefix}_SleighDevTools.zip"; + dontUnpack = true; + + # Built as part ghidra + dontBuild = true; + buildInputs = [ python3 ]; + + installPhase = '' + runHook preInstall + + mkdir -p $out/lib/ghidra/Ghidra/Extensions + unzip -d $out/lib/ghidra/Ghidra/Extensions $src + + runHook postInstall + ''; + + meta = with lib; { + inherit (ghidra.meta) homepage license; + description = "Sleigh language development tools including external disassembler capabilities"; + longDescription = '' + Sleigh language development tools including external disassembler capabilities. + The GnuDisassembler extension may be also be required as a disassembly provider. + ''; + downloadPage = "https://github.com/NationalSecurityAgency/ghidra/tree/master/Ghidra/Extensions/SleighDevTools"; + sourceProvenance = with sourceTypes; [ + fromSource + binaryBytecode # deps + ]; + }; +} diff --git a/pkgs/ghidra-xenia/with-extensions.nix b/pkgs/ghidra-xenia/with-extensions.nix new file mode 100644 index 0000000..38165ed --- /dev/null +++ b/pkgs/ghidra-xenia/with-extensions.nix @@ -0,0 +1,36 @@ +{ lib +, stdenv +, callPackage +, symlinkJoin +, makeBinaryWrapper +, desktopToDarwinBundle +, ghidra +}: + +let + ghidra-extensions = callPackage ./extensions.nix { inherit ghidra; }; + allExtensions = lib.filterAttrs (n: pkg: lib.isDerivation pkg) ghidra-extensions; + + /* Make Ghidra with additional extensions + Example: + pkgs.ghidra.withExtensions (p: with p; [ + ghostrings + ]); + => /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 ] + ++ lib.optional stdenv.hostPlatform.isDarwin desktopToDarwinBundle; + postBuild = '' + makeWrapper '${ghidra}/bin/ghidra' "$out/bin/ghidra" \ + --set NIX_GHIDRAHOME "$out/lib/ghidra/Ghidra" + ln -s ${ghidra}/share $out/share + '' + lib.optionalString stdenv.hostPlatform.isDarwin '' + convertDesktopFiles $prefix + ''; + inherit (ghidra) meta; + }); +in + withExtensions