{ description = "ghidr 0.1: pinned read-only Ghidra analysis CLI"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/62e0f05ede1da0d54515d4ea8ce9c733f12d9f08"; outputs = { self, nixpkgs }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; }; inherit (pkgs) lib; jdk = pkgs.jdk21_headless; ghidra = pkgs.stdenvNoCC.mkDerivation { pname = "ghidra"; version = "12.1.2"; src = pkgs.fetchurl { url = "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_12.1.2_build/ghidra_12.1.2_PUBLIC_20260605.zip"; hash = "sha256-ti6BoDkGGEZsAZxg2ML3ls7SUJxMGupKN2RKdycs+Z0="; }; nativeBuildInputs = [ pkgs.autoPatchelfHook pkgs.makeWrapper pkgs.unzip ]; buildInputs = [ pkgs.stdenv.cc.cc.lib ]; dontConfigure = true; dontBuild = true; unpackPhase = '' runHook preUnpack unzip -q "$src" sourceRoot=ghidra_12.1.2_PUBLIC runHook postUnpack ''; installPhase = '' runHook preInstall mkdir -p "$out/lib/ghidra" "$out/bin" cp -a . "$out/lib/ghidra/" patchShebangs "$out/lib/ghidra/support" makeWrapper "$out/lib/ghidra/support/analyzeHeadless" "$out/bin/ghidra-analyzeHeadless" \ --set JAVA_HOME "${jdk}" \ --prefix PATH : "${ lib.makeBinPath [ jdk pkgs.bash pkgs.coreutils pkgs.findutils pkgs.gnugrep pkgs.gnused ] }" runHook postInstall ''; passthru = { inherit jdk; }; meta = { description = "Ghidra 12.1.2 headless analysis distribution"; homepage = "https://github.com/NationalSecurityAgency/ghidra"; license = lib.licenses.asl20; platforms = [ system ]; sourceProvenance = [ lib.sourceTypes.binaryBytecode ]; }; }; adapter = pkgs.stdenvNoCC.mkDerivation { pname = "ghidr-java-adapter"; version = "0.1.0"; src = ./java; nativeBuildInputs = [ jdk pkgs.clang-tools ]; dontConfigure = true; buildPhase = '' runHook preBuild clang-format --dry-run --Werror --style=Google GhidrAdapter.java classpath="$(find ${ghidra}/lib/ghidra -type f -name '*.jar' -print | LC_ALL=C sort | paste -sd: -)" # Ghidra's release JAR manifests reference optional JARs that are not # shipped in the distribution. Keep adapter source warnings fatal, # while excluding those upstream class-path warnings and preventing # processors discovered in Ghidra's dependency closure from running. javac -encoding UTF-8 -source 21 -target 21 -proc:none \ -Xlint:all,-path -Werror -cp "$classpath" GhidrAdapter.java runHook postBuild ''; installPhase = '' runHook preInstall mkdir -p "$out/share/ghidr/java" install -m 0444 GhidrAdapter.java dispatch.txt "$out/share/ghidr/java/" install -m 0444 ./*.class "$out/share/ghidr/java/" runHook postInstall ''; }; elfFixture = pkgs.stdenv.mkDerivation { pname = "ghidr-fixture-elf-x86-64"; version = "0.1.0"; src = ./fixtures/src/known.c; dontUnpack = true; SOURCE_DATE_EPOCH = "1"; buildPhase = '' runHook preBuild $CC -x c "$src" -Os -ffreestanding -fno-ident -fno-stack-protector \ -fno-asynchronous-unwind-tables -fno-unwind-tables -nostdlib -static \ -no-pie -Wl,--build-id=none,-e,_start -o known.elf runHook postBuild ''; installPhase = '' mkdir -p "$out/elf-x86_64" install -m 0555 known.elf "$out/elf-x86_64/known.elf" ''; }; peFixture = pkgs.pkgsCross.mingwW64.stdenv.mkDerivation { pname = "ghidr-fixture-pe32plus-x86-64"; version = "0.1.0"; src = ./fixtures/src/known.c; dontUnpack = true; SOURCE_DATE_EPOCH = "1"; buildPhase = '' runHook preBuild $CC -x c "$src" -Os -ffreestanding -fno-ident -fno-stack-protector \ -nostdlib -Wl,--no-insert-timestamp,--entry,mainCRTStartup,--subsystem,console \ -o known.exe runHook postBuild ''; installPhase = '' mkdir -p "$out/pe32plus-x86_64" install -m 0555 known.exe "$out/pe32plus-x86_64/known.exe" ''; }; fixtures = pkgs.symlinkJoin { name = "ghidr-fixtures-0.1.0"; paths = [ elfFixture peFixture ]; }; rustPackage = pkgs.rustPlatform.buildRustPackage { pname = "ghidra-cli"; version = "0.1.0"; src = lib.cleanSource self; cargoLock.lockFile = ./Cargo.lock; doCheck = true; cargoTestFlags = [ "--features=test-support" ]; nativeBuildInputs = [ pkgs.installShellFiles ]; }; ghidr = pkgs.runCommand "ghidr-0.1.0" { nativeBuildInputs = [ pkgs.makeWrapper ]; meta.mainProgram = "ghidr"; } '' mkdir -p "$out/bin" "$out/share/ghidr" ln -s ${adapter}/share/ghidr/java "$out/share/ghidr/java" makeWrapper ${rustPackage}/bin/ghidr "$out/bin/ghidr" \ --set GHIDR_GHIDRA_VERSION 12.1.2 \ --set GHIDR_JAVA_HOME ${jdk} \ --set GHIDR_ANALYZE_HEADLESS ${ghidra}/bin/ghidra-analyzeHeadless \ --set GHIDR_ADAPTER_PATH "$out/share/ghidr/java" \ --set GHIDR_BUBBLEWRAP ${pkgs.bubblewrap}/bin/bwrap \ --prefix PATH : ${ lib.makeBinPath [ ghidra jdk pkgs.bubblewrap ] } ''; realGhidraE2e = pkgs.runCommand "ghidr-real-ghidra-e2e" { nativeBuildInputs = [ ghidr pkgs.jq ]; } '' export HOME="$TMPDIR/home" mkdir -p "$HOME" "$TMPDIR/store" ${ghidr}/bin/ghidr --sandbox off --store "$TMPDIR/store" inspect \ ${fixtures}/elf-x86_64/known.elf > elf.json jq -e ' .kind == "inspection" and .provenance.ghidr_version == "0.1.0" and .provenance.ghidra_version == "12.1.2" ' elf.json ${ghidr}/bin/ghidr --sandbox off --store "$TMPDIR/store" functions \ ${fixtures}/pe32plus-x86_64/known.exe --all > pe.json jq -e '.kind == "functions" and (.data.items | length) > 0' pe.json ${ghidr}/bin/ghidr --sandbox off --store "$TMPDIR/store" decompile \ ${fixtures}/elf-x86_64/known.elf --name fixture_add > decompile.json jq -e '.data.function.name == "fixture_add"' decompile.json touch "$out" ''; adapterE2e = pkgs.runCommand "ghidr-java-adapter-e2e" { nativeBuildInputs = [ ghidra jdk pkgs.bash pkgs.jq ]; } '' export HOME="$TMPDIR/home" export GHIDR_ANALYZE_HEADLESS=${ghidra}/bin/ghidra-analyzeHeadless export GHIDR_ADAPTER_PATH=${adapter}/share/ghidr/java export GHIDR_FIXTURES=${fixtures} mkdir -p "$HOME" cp -R ${./tests} tests chmod +x tests/real_ghidra_adapter.sh patchShebangs tests/real_ghidra_adapter.sh ./tests/real_ghidra_adapter.sh touch "$out" ''; in { packages.${system} = { default = ghidr; inherit ghidr ghidra adapter fixtures ; real-ghidra-e2e = realGhidraE2e; }; apps.${system}.default = { type = "app"; program = "${ghidr}/bin/ghidr"; meta.description = "Run ghidr with its pinned Ghidra runtime"; }; checks.${system} = { rust = rustPackage; java-adapter = adapter; java-adapter-e2e = adapterE2e; inherit fixtures; real-ghidra-e2e = realGhidraE2e; cargo-deny = pkgs.stdenvNoCC.mkDerivation { pname = "ghidr-cargo-deny"; version = "0.1.0"; src = lib.cleanSource self; inherit (rustPackage) cargoDeps; nativeBuildInputs = [ pkgs.cargo pkgs.cargo-deny pkgs.rustPlatform.cargoSetupHook ]; dontConfigure = true; CARGO_NET_OFFLINE = "true"; buildPhase = '' runHook preBuild cargo deny check bans licenses sources runHook postBuild ''; installPhase = '' touch "$out" ''; }; nix-style = pkgs.runCommand "ghidr-nix-style" { nativeBuildInputs = [ pkgs.nixfmt-rfc-style pkgs.statix ]; } '' cp ${./flake.nix} flake.nix nixfmt --check flake.nix statix check flake.nix touch "$out" ''; }; devShells.${system}.default = pkgs.mkShell { packages = [ pkgs.rustc pkgs.cargo pkgs.clippy pkgs.rustfmt pkgs.cargo-deny pkgs.bubblewrap pkgs.clang-tools pkgs.nixfmt-rfc-style pkgs.statix pkgs.jq jdk ghidra ]; GHIDR_GHIDRA_VERSION = "12.1.2"; GHIDR_JAVA_HOME = "${jdk}"; GHIDR_ANALYZE_HEADLESS = "${ghidra}/bin/ghidra-analyzeHeadless"; GHIDR_ADAPTER_PATH = "${adapter}/share/ghidr/java"; }; }; }