Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ghidra: refactor #60664

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 9 additions & 48 deletions pkgs/tools/security/ghidra/default.nix
Original file line number Diff line number Diff line change
@@ -1,48 +1,9 @@
{ stdenv, fetchurl, unzip, lib, makeWrapper, autoPatchelfHook
, openjdk11, pam
}: let

pkg_path = "$out/lib/ghidra";

in stdenv.mkDerivation {

name = "ghidra-9.0";

src = fetchurl {
url = https://ghidra-sre.org/ghidra_9.0_PUBLIC_20190228.zip;
sha256 = "3b65d29024b9decdbb1148b12fe87bcb7f3a6a56ff38475f5dc9dd1cfc7fd6b2";
};

nativeBuildInputs = [
makeWrapper
autoPatchelfHook
unzip
];

buildInputs = [
stdenv.cc.cc.lib
pam
];

dontStrip = true;

installPhase = ''
mkdir -p "${pkg_path}"
cp -a * "${pkg_path}"
'';

postFixup = ''
mkdir -p "$out/bin"
makeWrapper "${pkg_path}/ghidraRun" "$out/bin/ghidra" \
--prefix PATH : ${lib.makeBinPath [ openjdk11 ]}
'';

meta = with lib; {
description = "A software reverse engineering (SRE) suite of tools developed by NSA's Research Directorate in support of the Cybersecurity mission";
homepage = "https://ghidra-sre.org/";
platforms = [ "x86_64-linux" ];
license = licenses.asl20;
maintainers = [ maintainers.ck3d ];
};

}
{callPackage}:
let
packages = callPackage ./packages.nix {};
in
# For ergonomics
packages.ghidra // {
inherit (packages) withPlugins extend; # For ergonomics and overrides
pkgs = packages; # For access
}
8 changes: 8 additions & 0 deletions pkgs/tools/security/ghidra/doc/documentation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Overriding
----------
For the moment, the API documentation is the tests.
Documentation will be added in at most a few weeks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Famous last words.


Tests
-----
Tests can be run with `nix-shell testname.nix`.
23 changes: 23 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/eclipse-00.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Expected result:
# An Eclipse instance should start, with the GhidraDev
# plugin asking for consent to open ports

let pkgs = import ./nixpkgs.nix; in

let
inherit (pkgs) mkShell callPackage eclipses;

ghidra = callPackage ./ghidra.nix {};

eclipse = eclipses.eclipseWithPlugins {
eclipse = eclipses.eclipse-java;
plugins = [ ghidra.pkgs.ghidraDev ];
};
in
mkShell {
name = "ghidra";
buildInputs = [ eclipse ];
shellHook = ''
exec eclipse -data "$(mktemp -d)"
'';
}
8 changes: 8 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/eclipse-01.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Expected result:
# An Eclipse instance should start, with the GhidraDev
# plugin attached as a debugger to a headless Ghidra
# that is built with an out-of-tree plugin. The debugger
# should stop in a breakpoint in the plugin source code
# and display the appropriate source location.

#TODO not yet implemented
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say either implement this right away or do not check in this stubbed test for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Giving up on this for the moment, possible starting points:
https://testing.bredex.de/test-machine-setup.html https://www.eclipse.org/rcptt/
I have no idea how to work any of this stuff.

2 changes: 2 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/ghidra.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{callPackage}:
callPackage ../../default.nix {}
17 changes: 17 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/gui-00.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Expected result:
# A running Ghidra GUI instance

let pkgs = import ./nixpkgs.nix; in

let
inherit (pkgs) mkShell callPackage;

ghidra = callPackage ./ghidra.nix {};
in
mkShell {
name = "ghidra";
buildInputs = [ ghidra ];
shellHook = ''
exec ghidraRun
'';
}
18 changes: 18 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/gui-01.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Expected result:
# A Ghidra GUI instance should start with the scala loader plugin listed under
# File -> Install Extensions

let pkgs = import ./nixpkgs.nix; in

let
inherit (pkgs) mkShell callPackage;

ghidra = callPackage ./ghidra.nix {};
in
mkShell {
name = "ghidra";
buildInputs = [ (ghidra.withPlugins (p: with p; [ ghidra-scala-loader ])) ];
shellHook = ''
exec ghidraRun
'';
}
36 changes: 36 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/gui-02.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Expected result:
# Some trace output, the build, and then
# Listening for transport dt_socket at address: 18001

let pkgs = import ./nixpkgs.nix; in

let
inherit (pkgs) mkShell callPackage;

ghidra = callPackage ./ghidra.nix {};

newOpts = self: super: {
tracing = true;
config = super.pkgs.lib.recursiveUpdate super.config ({
defaultOpts.debug = {
enable = true;
};
});
};
newGh = self: super: {
ghidra = super.ghidra.override {
extraLaunchers = {
"myGhidraDebug" = self.lib.mkRunline { debug = { suspend = true; }; };
};
};
};
in
mkShell {
name = "ghidra";
buildInputs = [
(((ghidra.extend newOpts).extend newGh).withPlugins (p: with p; [ ghidra-scala-loader ]))
];
shellHook = ''
exec myGhidraDebug
'';
}
28 changes: 28 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/headless-00.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Expected result:
# It should dump the headless analyzer usage information

let pkgs = import ./nixpkgs.nix; in

let
inherit (pkgs) mkShell callPackage;

ghidra = callPackage ./ghidra.nix {};
newGh = self: super: {
ghidra = super.ghidra.override {
extraLaunchers = {
"myGhidra" = self.lib.mkRunline {
args = self.config.defaultOpts.args.headless;
};
};
};
};
in
mkShell {
name = "ghidra";
buildInputs = [
((ghidra.extend newGh).withPlugins (p: with p; [ ghidra-scala-loader ]))
];
shellHook = ''
exec myGhidra
'';
}
28 changes: 28 additions & 0 deletions pkgs/tools/security/ghidra/doc/tests/headless-01.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Expected result:
# INFO HelloWorldScript.scala> Hello world, I'm written in Scala! (GhidraScript)

let pkgs = import ./nixpkgs.nix; in

let
inherit (pkgs) mkShell callPackage;

ghidra = callPackage ./ghidra.nix {};
newGh = self: super: {
ghidra = super.ghidra.override {
extraLaunchers = {
"myGhidra" = self.lib.mkRunline {
args = self.config.defaultOpts.args.headless;
};
};
};
};
in
mkShell {
name = "ghidra";
buildInputs = [
((ghidra.extend newGh).withPlugins (p: with p; [ ghidra-scala-loader ]))
];
shellHook = ''
exec myGhidra $(mktemp -d) TestProj -noanalysis -preScript HelloWorldScript.scala
'';
}
1 change: 1 addition & 0 deletions pkgs/tools/security/ghidra/doc/tests/nixpkgs.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import ../../../../../.. {}
17 changes: 17 additions & 0 deletions pkgs/tools/security/ghidra/layers/0_base.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{pkgs, lib}:
self: {
# Don't pollute the namespace (for e.g. tab completion)
inherit pkgs;

# Thus we can take dependencies from both pkgs and Ghidra nevertheless.
# Packages from ghidra take precedence on collision.
callPackage = lib.callPackageWith ( self.pkgs // self );


tracing = false;
trace = str: val:
if self.tracing then
lib.traceValFn (v: "${str}\n${lib.generators.toPretty {} v}") val
else
val;
}
63 changes: 63 additions & 0 deletions pkgs/tools/security/ghidra/layers/1_util.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
self: super: {
config = {
pkg_path = "lib/ghidra";
ghidraDevPath = self.config.pkg_path + "/Extensions/Eclipse/GhidraDev/GhidraDev-2.0.0.zip";
ghidraDevVersion = "2.0.0";
launchers = [
"ghidraRun" "support/analyzeHeadless" "support/buildGhidraJar"
"support/convertStorage" "support/dumpGhidraThreads" "support/ghidraDebug"
"support/pythonRun" "support/sleigh"
];
defaultOpts = import ../lib/defaultOpts.nix;
};


lib = {
# nix-shell -p nix-prefetch-github --run "nix-prefetch-github owner repo > ./plugins/json/theplugin.json"
fetchGitHubJSON = {JSONfile, ...}@args:
self.pkgs.fetchFromGitHub ({ inherit (self.pkgs.lib.importJSON JSONfile) owner repo rev sha256; } // (
builtins.removeAttrs args [ "JSONfile" ]
));

# { args ? defaultOpts.args, debug ? defaultOpts.debug }: # The signature
mkRunline = self.callPackage ((import ../lib/mkRunline.nix) self.config.defaultOpts) {};

poorMkGradle = self.callPackage ../lib/poorMkGradle.nix {};

jdkWrapper = src: dst: ''
makeWrapper "$out/${src}" "$out/${dst}" \
--prefix PATH : '${self.pkgs.lib.makeBinPath [ self.jdk ]}' \
--run '. ${self.jdk}/nix-support/setup-hook' #set JAVA_HOME
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We not setting JAVA_HOME directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't really sure what to do about this. It makes some kind of sense to me to delegate setting whatever environment stuff a dependency implies, by relying on the dependency's scripts?

'';

writeCustomLauncher = name: content: ''
cp -- "${self.pkgs.writeShellScriptBin name content}/bin/${name}" "$out/bin/.${name}"
${self.lib.jdkWrapper "bin/.${name}" "bin/${name}" }
'';

# hash-name -> name
nameOf = i: with self.pkgs.lib;
let dropHash = s: concatStringsSep "-" (tail (splitString "-" s)); in
dropHash (removeSuffix ".zip" (builtins.baseNameOf i));

unpackPlugin = pluginZip:
self.pkgs.stdenv.mkDerivation {
name = builtins.unsafeDiscardStringContext (self.lib.nameOf pluginZip);
phases = [ "unpackPhase" "installPhase" ];
buildInputs = [ self.pkgs.unzip ];
src = pluginZip;
installPhase = ''
mkdir -p -- "$out"
cp -r -- ./* "$out"
'';
};

#TODO mk extracted plugin derivation and ln that? <--use multiple output?
installPlugin = plugin: ''
ln -s -- "${plugin}" "$out/${self.config.pkg_path}/Ghidra/Extensions/${self.lib.nameOf (builtins.baseNameOf plugin)}"
'';
};


withPlugins = f: self.ghidra.override { plugins = f self; };
}
15 changes: 15 additions & 0 deletions pkgs/tools/security/ghidra/layers/2_base_packages.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
self: super: {
jdk = self.pkgs.jdk11;
gradleGen = (self.pkgs.gradleGen.override {jdk = self.jdk;}).gradle_latest;

ghidra = self.callPackage ../lib/ghidra.nix {};
# For use with `eclipses.eclipseWithPlugins`, see tests/eclipse-00.nix
ghidraDev = self.pkgs.eclipses.plugins.buildEclipseUpdateSite rec {
name = "GhidraDev";
version = self.config.ghidraDevVersion;

sourceRoot = ".";

src = self.ghidra + "/" + self.config.ghidraDevPath;
};
}
6 changes: 6 additions & 0 deletions pkgs/tools/security/ghidra/layers/3_packages.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# NOTE to maintainers: please check whether plugins include binaries or JARs in their distributions,
# as opposed to getting them from proper sources - accepting this is highly discouraged!
# The plugin building code will check for files with .jar extensions, but not more than that.
self: super: {
ghidra-scala-loader = self.callPackage ./4_plugins/ghidra-scala-loader.nix { scala = self.pkgs.scala_2_11; };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{scala, lib}:
let
src = lib.fetchGitHubJSON { JSONfile = ./json/ghidra-scala-loader.json; };
in
(lib.poorMkGradle "ghidra-scala-loader" src).overrideAttrs (a: {
removeJars = ''
pushd lib
rm -- *
popd
'';
addJars = ''
pushd lib
unpacked=$(mktemp -d)
pushd -- $unpacked
cp -- ${scala.src} .
tar axvf "$(basename -- *)"
mv -- scala-*/* .
popd
cp -- "$unpacked/lib/scala-compiler.jar" ./scala-compiler-2.11.12.jar
cp -- "$unpacked/lib/scala-library.jar" ./scala-library-2.11.12.jar
cp -- "$unpacked/lib/scala-reflect.jar" ./scala-reflect-2.11.12.jar
popd
'';
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"owner": "edmcman",
"repo": "ghidra-scala-loader",
"rev": "392a2af0441f3c53dbbd749ec8d86539e7bcb901",
"sha256": "0drphh7fjvlkqwwk0lcr3818vk7mqs6xkm6iv3xp6c7vl3d32b1c"
}
25 changes: 25 additions & 0 deletions pkgs/tools/security/ghidra/lib/defaultOpts.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This is essentially for mkRunline, but if it becomes useful to have
# default arguments for something else, this is where they should go.
{
debug = {
enable = false;
suspend = false;
port = "18001";
# host = "127.0.0.1"; #TODO for implementation when ghidra version is updated
};

args = {
name = "Ghidra";
maxMemory = "";
vmArgs = "";
class = "ghidra.GhidraRun";
extraArgs = "";
enableUserShellArgs = true;

#Convenience, see tests/headless-00.nix for an example.
headless = {
name = "Ghidra-Headless";
class = "ghidra.app.util.headless.AnalyzeHeadless";
};
};
}
Loading