diff --git a/.gitignore b/.gitignore index 0856468d1..2c66224f8 100644 --- a/.gitignore +++ b/.gitignore @@ -262,3 +262,6 @@ ghidra/p_code_extractor/lib/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# nix build +/result* diff --git a/README.md b/README.md index 2d00fa9b2..fffce0000 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,11 @@ The following dependencies must be installed in order to build and install the * Run `make all GHIDRA_PATH=/path/to/ghidra_folder` (with the correct path to the local Ghidra installation inserted) to compile and install the cwe_checker. +### Nix package manager +```bash +nix build 'github:fkie-cad/cwe_checker' +``` + ## Usage ## The *cwe_checker* takes a binary as input, @@ -63,6 +68,10 @@ If you installed the *cwe_checker* locally, run ```bash cwe_checker BINARY ``` +If you are using Nix package manager, run +```bash +nix run 'github:fkie-cad/cwe_checker' BINARY +``` You can adjust the behavior of most checks via a configuration file located at `src/config.json`. If you modify it, add the command line flag `--config=src/config.json` to tell the *cwe_checker* to use the modified file. For information about other available command line flags you can pass the `--help` flag to the *cwe_checker*. diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..c798be622 --- /dev/null +++ b/flake.lock @@ -0,0 +1,119 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1632882339, + "narHash": "sha256-IUDkmxEK7b9zskl3ITQy5MY+bJwgetHq+enm7VIjFIg=", + "owner": "nix-community", + "repo": "fenix", + "rev": "ba9a36ef175efed4ef3a3de485131da921439301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1631561581, + "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nix-filter": { + "locked": { + "lastModified": 1620202920, + "narHash": "sha256-BOkm3eKT45Dk4NNxJT0xL9NnyYeZcF+t79zPnJkggac=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "3c9e33ed627e009428197b07216613206f06ed80", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, + "nix-utils": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1633220764, + "narHash": "sha256-RILBQQd16gwFvtoVrrwZZBnbydjyX/qczz65S+OFF+Q=", + "ref": "master", + "rev": "e5052f293115012f3cb46fd9d7dfe012f1f618c1", + "revCount": 11, + "type": "git", + "url": "https://git.sr.ht/~ilkecan/nix-utils" + }, + "original": { + "type": "git", + "url": "https://git.sr.ht/~ilkecan/nix-utils" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1633121392, + "narHash": "sha256-3rRIMS4ELS+yBEQHLzL7eineYZ47eyPEmdT40WiWYyA=", + "owner": "ilkecan", + "repo": "nixpkgs", + "rev": "99a37dc3b9616dc758e9a4ba5765ddbddd8a7334", + "type": "github" + }, + "original": { + "owner": "ilkecan", + "ref": "nixos-21.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "nix-filter": "nix-filter", + "nix-utils": "nix-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1632765608, + "narHash": "sha256-92S3eyleED/o/qdrGS5lObktrJXZ0HEut5Y+QIuJqhI=", + "owner": "rust-analyzer", + "repo": "rust-analyzer", + "rev": "533ca584c31a251bc47f978b55df9b69058dabba", + "type": "github" + }, + "original": { + "owner": "rust-analyzer", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..2021f170c --- /dev/null +++ b/flake.nix @@ -0,0 +1,107 @@ +{ + description = "cwe_checker finds vulnerable patterns in binary executables"; + + inputs = { + # use upstream once https://github.com/NixOS/nixpkgs/pull/140208 is accepted + # nixpkgs.url = "nixpkgs/nixos-21.05"; + nixpkgs.url = "github:ilkecan/nixpkgs/nixos-21.05"; + flake-utils.url = "github:numtide/flake-utils"; + nix-filter.url = "github:numtide/nix-filter"; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + nix-utils = { + url = "git+https://git.sr.ht/~ilkecan/nix-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, flake-utils, ... }@inputs: + let + inherit (builtins) + attrNames + attrValues + ; + inherit (nixpkgs.lib) + getAttrs + intersectLists + ; + inherit (flake-utils.lib) + defaultSystems + eachSystem + ; + nix-filter = inputs.nix-filter.lib; + nix-utils = inputs.nix-utils.lib; + inherit (nix-utils) + createOverlays + importCargoLock + ; + + # ghidra-bin.meta.platforms + ghidraPlatforms = [ "x86_64-linux" "x86_64-darwin" ]; + supportedSystems = intersectLists defaultSystems ghidraPlatforms; + commonArgs = { + version = (importCargoLock ./.).cwe_checker.version; + homepage = "https://github.com/fkie-cad/cwe_checker"; + downloadPage = "https://github.com/fkie-cad/cwe_checker/releases"; + changelog = "https://raw.githubusercontent.com/fkie-cad/cwe_checker/master/CHANGES.md"; + maintainers = [ + { + email = "ilkecan@protonmail.com"; + github = "ilkecan"; + githubId = 40234257; + name = "ilkecan bozdogan"; + } + ]; + platforms = supportedSystems; + }; + + derivations = { + cwe_checker = import ./nix/cwe_checker.nix commonArgs; + cwe_checker_to_ida = import ./nix/cwe_checker_to_ida.nix commonArgs; + }; + in + { + overlays = createOverlays derivations { + inherit + nix-filter + nix-utils + ; + }; + overlay = self.overlays.cwe_checker; + } // eachSystem supportedSystems (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = attrValues self.overlays ++ [ + inputs.fenix.overlay + ]; + }; + + packageNames = attrNames derivations; + in + rec { + checks = packages; + + packages = getAttrs packageNames pkgs; + defaultPackage = packages.cwe_checker; + + hydraJobs = { + build = packages; + }; + + devShell = + let + packageList = attrValues packages; + in + pkgs.mkShell { + packages = packageList ++ [ + defaultPackage.rustToolchain.defaultToolchain + ]; + inputsFrom = packageList; + }; + }); +} diff --git a/nix/cwe_checker.nix b/nix/cwe_checker.nix new file mode 100644 index 000000000..cafd3f449 --- /dev/null +++ b/nix/cwe_checker.nix @@ -0,0 +1,105 @@ +{ version +, changelog +, downloadPage +, homepage +, maintainers +, platforms +}: +{ lib +, fenix +, ghidra-bin +, makeRustPlatform +, makeWrapper +, nix-filter +, writeShellScript +, ... +}: + +let + inherit (nix-filter) inDirectory; + + rustToolchain = fenix.stable; + rustPlatform = makeRustPlatform { + inherit (rustToolchain) cargo rustc; + }; + + pname = "cwe_checker"; + root = ./..; + # Reading the files in the filtered directory is not possible right now. + # Follow up on how https://github.com/NixOS/nix/pull/5163 will be resolved. + mainProgram = pname; + + src = nix-filter { + inherit root; + name = pname; + include = [ + "Cargo.lock" + "Cargo.toml" + (inDirectory "src") + (inDirectory "test") + ]; + }; + + preRunScript = writeShellScript "preRunScript" '' + config_dir="$HOME/.config/cwe_checker" + config_json="$config_dir/config.json" + if [[ ! -f $config_json ]]; then + install --mode=644 -D ${toString src}/src/config.json $config_json + fi + + ghidra_json="$config_dir/ghidra.json" + if [[ ! -f $ghidra_json ]]; then + echo '{ "ghidra_path": "${ghidra-bin}/lib/ghidra" }' > $ghidra_json + fi + ''; +in +rustPlatform.buildRustPackage { + inherit pname version src; + + cargoHash = "sha256-igAygYTIkV+gfBWVHGVspheTC19TilU3A3/MqSwhd90="; + + buildInputs = [ + ghidra-bin.out + ]; + + nativeBuildInputs = [ + makeWrapper.out + ]; + + patches = [ + ./patches/0001-use-env-variable-for-ghidra-plugin-path.patch + ]; + + postInstall = '' + wrapProgram "$out/bin/${mainProgram}" \ + --set CWE_CHECKER_GHIDRA_PLUGIN_PATH "${toString src}/src/ghidra" \ + --run ${preRunScript} + ''; + + doInstallCheck = true; + installCheckPhase = '' + tmp=$(mktemp) + HOME=$(mktemp -d) # because of the preRunScript + $out/bin/${mainProgram} --version &> $tmp || (cat $tmp; exit 1) + echo "OK" + ''; + + passthru = { + inherit rustToolchain; + ghidra_plugin = "${toString root}/ghidra_plugin/cwe_checker_ghidra_plugin.py"; + }; + + meta = { + description = "cwe_checker finds vulnerable patterns in binary executables"; + longDescription = + "cwe_checker is a suite of checks to detect common bug classes such as" + + "use of dangerous functions and simple integer overflows. These bug" + + "classes are formally known as Common Weakness Enumerations (CWEs). Its" + + "main goal is to aid analysts to quickly find vulnerable code paths."; + + inherit homepage downloadPage changelog; + + license = lib.licenses.lgpl3Plus; + inherit maintainers mainProgram platforms; + }; +} diff --git a/nix/cwe_checker_to_ida.nix b/nix/cwe_checker_to_ida.nix new file mode 100644 index 000000000..adc549bb9 --- /dev/null +++ b/nix/cwe_checker_to_ida.nix @@ -0,0 +1,74 @@ +{ version +, changelog +, downloadPage +, homepage +, maintainers +, platforms +}: +{ lib +, python3 +, stdenvNoCC +, ... +}: + +let + python = python3; + mainProgram = "cwe_checker_to_ida.py"; +in +stdenvNoCC.mkDerivation { + pname = "cwe_checker_to_ida"; + inherit version; + + src = ./../cwe_checker_to_ida; + + outputs = [ "bin" "out" ]; + propagatedBuildOutputs = [ ]; + + strictDeps = true; + + buildInputs = [ + python + ]; + + nativeBuildInputs = [ + python + ]; + + patches = [ + ./patches/0002-add-shebang-to-cwe-checher-to-ida.patch + ]; + + dontConfigure = true; + dontBuild = true; + + doCheck = true; + checkPhase = '' + python -m unittest CweCheckerParser_test.py + ''; + + installPhase = '' + mkdir -p $bin/bin + cp -r . $bin/lib + chmod +x $bin/lib/cwe_checker_to_ida.py + ln -s $bin/lib/${mainProgram} $bin/bin/${mainProgram} + + mkdir $out + ''; + + doInstallCheck = true; + installCheckPhase = '' + tmp=$(mktemp) + $bin/bin/${mainProgram} -h &> $tmp || (cat $tmp; exit 1) + echo "OK" + ''; + + meta = { + description = + "Generates an anotation script for IDA Pro based on CweChecker results"; + + inherit homepage downloadPage changelog; + + license = lib.licenses.lgpl3Plus; + inherit maintainers mainProgram platforms; + }; +} diff --git a/nix/patches/0001-use-env-variable-for-ghidra-plugin-path.patch b/nix/patches/0001-use-env-variable-for-ghidra-plugin-path.patch new file mode 100644 index 000000000..7f9ddb3ce --- /dev/null +++ b/nix/patches/0001-use-env-variable-for-ghidra-plugin-path.patch @@ -0,0 +1,17 @@ +diff --git a/src/cwe_checker_lib/src/utils/mod.rs b/src/cwe_checker_lib/src/utils/mod.rs +index f9b0e9c..de41b95 100644 +--- a/src/cwe_checker_lib/src/utils/mod.rs ++++ b/src/cwe_checker_lib/src/utils/mod.rs +@@ -21,10 +21,8 @@ pub fn read_config_file(filename: &str) -> serde_json::Value { + + /// Get the folder path to a Ghidra plugin bundled with the cwe_checker. + pub fn get_ghidra_plugin_path(plugin_name: &str) -> std::path::PathBuf { +- let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker") +- .expect("Could not discern location of data directory."); +- let data_dir = project_dirs.data_dir(); +- data_dir.join("ghidra").join(plugin_name) ++ let plugin_path = std::env::var("CWE_CHECKER_GHIDRA_PLUGIN_PATH").unwrap(); ++ std::path::Path::new(&plugin_path).join(plugin_name) + } + + /// Get the base address for the image of a binary when loaded into memory. diff --git a/nix/patches/0002-add-shebang-to-cwe-checher-to-ida.patch b/nix/patches/0002-add-shebang-to-cwe-checher-to-ida.patch new file mode 100644 index 000000000..b6c32af30 --- /dev/null +++ b/nix/patches/0002-add-shebang-to-cwe-checher-to-ida.patch @@ -0,0 +1,10 @@ +diff --git a/cwe_checker_to_ida.py b/cwe_checker_to_ida.py +index 50035f3..e3f4d3a 100644 +--- a/cwe_checker_to_ida.py ++++ b/cwe_checker_to_ida.py +@@ -1,3 +1,5 @@ ++#!/usr/bin/env python ++ + import os + import json + import argparse