Skip to content

Commit

Permalink
ghidra: refactor
Browse files Browse the repository at this point in the history
Added a plugins system, launcher generator, eclipse plugin package.
  • Loading branch information
deliciouslytyped committed May 13, 2019
1 parent 3da8111 commit a1f6aac
Show file tree
Hide file tree
Showing 22 changed files with 507 additions and 48 deletions.
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.

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
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
'';

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

0 comments on commit a1f6aac

Please sign in to comment.