From 4bd1a678c841ffa432c1b89d1d12ef967916db92 Mon Sep 17 00:00:00 2001 From: "Garret Wassermann (SEI)" Date: Mon, 29 Mar 2021 15:08:52 -0400 Subject: [PATCH] Public release 20210329, includes a number of bug fixes and improvements. Largely improvements to README, and a new HEADLESS_README for better documentation on using Kaiju in headless mode from a command line. New runKaiju bash script for easy headless usage. Also includes a simple new plugin for counting function cross-references within a program. --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++ HEADLESS_README.md | 73 ++++ README.md | 279 +++++++++------ extension.properties | 2 +- ghidra_scripts/exportCSVHeadless.java | 4 +- ghidra_scripts/exportXrefsToCSVHeadless.java | 51 +++ ghidra_scripts/exportYaraHeadless.java | 161 +++++++++ runKaiju | 139 ++++++++ .../kaiju/fnhash/export/HeadlessToCSV.java | 4 +- .../plugins/fnxrefs/FnXrefViewerContext.java | 72 ++++ .../plugins/fnxrefs/FnXrefViewerPlugin.java | 324 ++++++++++++++++++ .../plugins/fnxrefs/FnXrefViewerProvider.java | 308 +++++++++++++++++ .../fnxrefs/FnXrefViewerTableModel.java | 272 +++++++++++++++ .../plugins/fnxrefs/HeadlessXrefsToCSV.java | 84 +++++ 15 files changed, 1717 insertions(+), 114 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 HEADLESS_README.md create mode 100644 ghidra_scripts/exportXrefsToCSVHeadless.java create mode 100644 ghidra_scripts/exportYaraHeadless.java create mode 100644 runKaiju create mode 100644 src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerContext.java create mode 100644 src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerPlugin.java create mode 100644 src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerProvider.java create mode 100644 src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerTableModel.java create mode 100644 src/main/java/kaiju/plugins/fnxrefs/HeadlessXrefsToCSV.java diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/HEADLESS_README.md b/HEADLESS_README.md new file mode 100644 index 0000000..c906c80 --- /dev/null +++ b/HEADLESS_README.md @@ -0,0 +1,73 @@ +# Using CERT Kaiju in "Headless" Mode + +## Overview + +The following Kaiju tools are available in "headless" mode: + +- **fn2hash** = automatically run Fn2Hash on a given program +and export all the hashes to a CSV file specified +- **fn2yara** = automatically run Fn2Hash on a given program +and export all hash data as YARA signatures to the file specified +- **fnxrefs** = analyze a Program and export a list of Functions +based on entry point address that have cross-references in +data or other parts of the Program + +These tools are designed to work completely from the command line; +the user does not need to run the GUI version of Ghidra to utilize +this mode. This may be helpful in several scenarios, such as +an automated "batch" mode where a script runs Ghidra/Kaiju tools +automatically on a batch of samples, or to allow remote analysis +on a virtual machine/resources with only a remote terminal. + +*NOTE*: These tools may not completely correspond with features available +in graphical interface tools, simply due to the nature of +how a typical user utilizes GUI versus command line tools. + +## Using the Headless Analyzer + +Kaiju makes use of Ghidra's built-in "Headless Analyzer" mode, as well +as some GhidraScripts, to expose functionality and tools to the command line. +Users are recommended to familiar with the official Ghidra documentation, +for details please see the [Ghidra Headless Analyzer README](https://ghidra.re/ghidra_docs/analyzeHeadlessREADME.html). + +A brief overview, for the purposes of using CERT Kaiju in headless mode, is provided here. + +--- + +The general syntax for the Headless analyzer is: + +`analyzeHeadless PROJECT_DIRECTORY PROJECT_NAME [options...]` + +Three main steps happen when you run this command: + +1. if specified with `-preScript`, a GhidraScript is run (pre-analysis) that can be used to initialize or setup tools including Kaiju +2. one or more Ghidra analyzers, including those installed by Kaiju, are run against the provided executable in the given project environment +3. if specified with `-postScript`, a GhidraScript is then run (post-analysis) to process analyzer results (including export data to file) + +We have provided several sample scripts to perform these actions: + +- _setupScript.java_ +Ensures that the CERT Function Hashing plugin is enabled for headless analysis. (preScript) + +- _exportCSVHeadless.java_ +Extracts the function hashing artifacts and outputs them to the specified file in CSV format. (postScript) + +- _exportXrefsToCSVHeadless.java_ +Counts the number of external References to Function entry points and outputs them to the specified file in CSV format. (postScript) + +--- + +### Examples + +Analyzing a single binary with Fn2Hash and export the hashes to CSV: +`$GHIDRA_INSTALL_DIR/support/analyzeHeadless $HOME/ghidra_projects tmpProj -import exampleFile.exe -preScript setupScript.java -postScript exportCSVHeadless.java exampleFileResults.csv` + +Analyzing a directory of binaries with Fn2Hash: +`$GHIDRA_INSTALL_DIR/support/analyzeHeadless $HOME/ghidra_projects tmpProj -import path/to/binaries -preScript setupScript.java -postScript exportCSVHeadless.java exampleFileResults.csv` + + +Using `-okToDelete` and `-deleteProject` options on analyzeHeadless to remove tmpProj after import: +`$GHIDRA_INSTALL_DIR/support/analyzeHeadless $HOME/ghidra_projects tmpProj -okToDelete -deleteProject -import exampleFile.exe -preScript setupScript.java -postScript exportCSVHeadless.java exampleFileResults.csv` + +*** NOTE *** This will DELETE any Ghidra project named `tmpProj` in your user directory defined by `$HOME/ghidra_projects` (or throw an error if `$HOME` is undefined)! + diff --git a/README.md b/README.md index ffe38c3..31a6cfb 100644 --- a/README.md +++ b/README.md @@ -30,31 +30,181 @@ instruction like RET causing many more unintended collisions). As such, analytical results may vary between this plugin and Pharos fn2hash. -## Dependencies +## Quick Installation -CERT Kaiju suggests the following dependencies: +[Pre-built Kaiju packages](https://github.com/sei-eschwartz/kaiju/releases) +are available. Simply download the ZIP +file corresponding with your version of Ghidra and install. + +CERT Kaiju requires the following runtime dependencies: +- [Ghidra](https://ghidra-sre.org) 9.1+ (9.2+ recommended) +- Java 11+ (we recommend [OpenJDK 11](https://openjdk.java.net/install/)) + +**NOTE**: It is also possible to build the extension package +on your own and install it. Please see the instructions +under the "Build Kaiju Yourself" section below. + +The extension .zip file may be installed either interactively through +the graphical interface of Ghidra, or manually by unzipping the extension +into the appropriate directory in your Ghidra installation. + +### Graphical Installation + +Start Ghidra, and from the opening window, select from the menu: +`File > Install Extension`. Click the plus sign at the top of the +extensions window, navigate and select the .zip file in the file +browser and hit OK. The extension will be installed and a checkbox +will be marked next to the name of the extension in the window +to let you know it is installed and ready. + +The interface will ask you to restart Ghidra to start using +the extension. Simply restart, and then Kaiju's extra features will +be available for use interactively or in scripts. + +Some functionality may require enabling Kaiju plugins. To do this, +open the Code Browser then navigate to the menu `File > Configure`. +In the window that pops up, click the `Configure` link below +the "CERT Kaiju" category icon. A pop-up will display all available +publicly released Kaiju plugins. Check any plugins +you wish to activate, then hit OK. You will now have access to +interactive plugin features. + +If a plugin is not immediately visible once enabled, you +can find the plugin underneath the `Window` menu in the Code Browser. + +Experimental "alpha" versions of future tools may be available from +the "Experimental" category if you wish to test them. However +these plugins are definitely experimental and unsupported and not +recommended for production use. We do welcome early feedback though! + +### Manual Installation + +Ghidra extensions like Kaiju may also be installed manually +by unzipping the extension contents into the appropriate directory +of your Ghidra installation. For more information, please see +[The Ghidra Installation Guide](https://ghidra-sre.org/InstallationGuide.html#Extensions). + + +## Usage + +Kaiju's tools may be used either in an interactive graphical way, +or via a "headless" mode more suited for batch jobs. +Some tools may only be available for graphical or headless use, +by the nature of the tool. + +### Interactive Graphical Interface + +Kaiju creates an interactive graphical interface (GUI) within Ghidra +utilizing Java Swing and Ghidra's plugin architecture. + +Most of Kaiju's tools are actually Analysis plugins that run automatically +when the "Auto Analysis" option is chosen, either upon import of +a new executable to disassemble, or by directly choosing +`Analysis > Auto Analyze...` from the code browser window. You will +see several CERT Analysis plugins selected by default in the Auto Analyze +tool, but you can enable/disable any as desired. + +The Analysis tools must be run before the various GUI tools will work, +however. In some corner cases, it may even be helpful to run the +Auto Analysis twice to ensure all of the metadata is produced +to create correct partitioning and disassembly information, which +in turn can influence the hashing results. + +Analyzers include: +- **DisasmImprovements** = improves the function partitioning of the + disassembly compared to the standard Ghidra partitioning. +- **Fn2Hash** = calculates function hashes for all functions in a program. + +The GUI tools include: +- **Function Hash Viewer** = a plugin that displays an interactive list +of functions in a program, and their hashes. Analysts can use this +to export one or more functions from a program into YARA signatures. +- **OOAnalyzer JSON Importer** = a plugin that can +load, parse, and apply Pharos-generated OOAnalyzer results to object +oriented C++ executables in a Ghidra project. When launched, the +plugin will prompt the user for the JSON output file produced by +OOAnalyzer that contains information about recovered C++ +classes. After loading the JSON file, recovered C++ data types and +symbols found by OOAnalyzer are updated in the Ghidra Code +Browser. The plugin's design and implementation details are described +in our SEI blog post titled [Using OOAnalyzer to Reverse Engineer +Object Oriented Code with Ghidra][ooanalyzer-blog]. + +**NOTE**: for details on usage of these GUI tools, please see +the Kaiju help under Ghidra's built-in help system. To access +these help docs, from the Ghidra menu, go to `Help > Contents` +and then select `CERT Kaiju` from the tree navigation on the +left-hand side of the help window. + + +### Command-line "Headless" Mode + +Ghidra also supports a "headless" mode allowing tools to be run +in some circumstances without use of the interactive GUI. +These commands can therefore be utilized for scripting and +"batch mode" jobs of large numbers of files. + +The headless tools largely rely on Ghidra's GhidraScript functionality. + +Headless tools include: +- **fn2hash** = automatically run Fn2Hash on a given program +and export all the hashes to a CSV file specified +- **fn2yara** = automatically run Fn2Hash on a given program +and export all hash data as YARA signatures to the file specified +- **fnxrefs** = analyze a Program and export a list of Functions +based on entry point address that have cross-references in +data or other parts of the Program + +A simple shell launch script named `runKaiju` has been included to run +these headless commands for simple scenarios, such as outputing the +function hashes for every function in a single executable. +Assuming the `GHIDRA_INSTALL_DIR` variable is set, one might +for example run the launch script on a single executable as follows: + +``` +$GHIDRA_INSTALL_DIR/Ghidra/Extensions/kaiju/runKaiju fn2hash example.exe +``` + +This command would output the results to an automatically named file as +`example.exe.Hashes.csv`. + +Basic help for the `runKaiju` script is available by running: + +``` +$GHIDRA_INSTALL_DIR/Ghidra/Extensions/kaiju/runKaiju --help +``` + +Please see the `HEADLESS_README.md` file in the `ghidra_scripts/` +directory for more information on using this mode and +the `runKaiju` launcher script. + + +## Building Kaiju Yourself Using Gradle + +Alternately to the pre-built packages, you may compile and build +Kaiju yourself. + +### Build Dependencies + +CERT Kaiju requires the following build dependencies: - [Ghidra](https://ghidra-sre.org) 9.1+ (9.2+ recommended) - [gradle](https://gradle.org/install/) 5.6+ (gradle 6.x recommended) -- Java 11+ (we suggest [OpenJDK](https://openjdk.java.net/install/)) +- [GSON](https://github.com/google/gson) 2.8.6 +- Java 11+ (we recommend [OpenJDK 11](https://openjdk.java.net/install/)) -**NOTE**: Please ensure that gradle is building against the same +**NOTE ABOUT GRADLE**: Please ensure that gradle is building against the same JDK version in use by Ghidra on your system, or you may experience installation problems. -This framework has been tested with the following build: -- 9.2.2 (public release), gradle 6.8.3 - -### Runtime Dependencies - -The only external runtime dependency is Google's -[GSON](https://github.com/google/gson) library. In most cases, Gradle -will automatically obtain this for you. If you find that you need to -obtain it manually, you can download +**NOTE ABOUT GSON**: In most cases, Gradle will automatically obtain this for +you. If you find that you need to obtain it manually, you can download [gson-2.8.6.jar](https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar) -and place it in the -`kaiju/lib` directory. +and place it in the `kaiju/lib` directory. -## Gradle Build Instructions +This framework has been tested with the following build: +- 9.2.2 (public release), gradle 6.8.3, OpenJDK 11 + +### Build Instructions Once dependencies are installed, Kaiju may be built as a Ghidra extension by using the `gradle` build tool. It is recommended to @@ -133,38 +283,10 @@ into `lib/` directory of Kaiju if needed. You will want to run the update command whenever you pull the latest Kaiju source code, to ensure they stay in sync. -## Installation - -The extension .zip file may be installed either interactively through -the graphical interface of Ghidra, or manually. The manual option -may be useful for "headless" installations (e.g., on servers only -available via a command line). - -### Graphical Installation - -Start Ghidra, and from the opening window, select from the menu: -`File > Install Extension`. Click the plus sign at the top of the -extensions window, navigate and select the .zip file in the file -browser and hit OK. The extension will be installed and a checkbox -will be marked next to the name of the extension in the window -to let you know it is installed and ready. - -The interface will ask you to restart Ghidra to start using -the extension. Simply restart, and then Kaiju's extra features will -be available for use interactively or in scripts. - -Some functionality may require enabling Kaiju plugins. To do this, -open the Code Browser then navigate to the menu `File > Configure`. -In the window that pops up, click the `Configure` link below -the "Experimental" category icon. A pop-up will display all available -experimental plugins, including Kaiju's plugins. Check any plugins -you wish to activate, then hit OK. You will now have access to -interactive plugin features. If a plugin is not visible, you -can find the plugin underneath the `Window` menu in the Code Browser. - ### First-Time "Headless" Gradle-based Installation -You may also install the extension directly on the command line via +If you compiled and built your own Kaiju extension, +you may alternately install the extension directly on the command line via use of gradle. Be sure to set `GHIDRA_INSTALL_DIR` as an environment variable first (if you built Kaiju too, then you should already have this defined), then run `gradle` as follows: @@ -186,7 +308,7 @@ to update properly if installed while Ghidra is running, leading to some odd bugs. If this happens to you, simply exit Ghidra and try reinstalling again. -### Consider Removing Your Old Installation First +#### Consider Removing Your Old Installation First It might be helpful to first completely remove any older install of Kaiju before updating to a newer release. We've seen some cases @@ -207,69 +329,8 @@ If you'd prefer to remove your old installation manually, perform a command like rm -rf $GHIDRA_INSTALL_DIR/Extensions/Ghidra/*kaiju*.zip $GHIDRA_INSTALL_DIR/Ghidra/Extensions/kaiju ``` - -## Usage - -Kaiju's tools may be used either in an interactive graphical way, -or via a "headless" mode more suited for batch jobs. - -### Interactive Graphical Interface - -Kaiju creates an interactive graphical interface (GUI) within Ghidra -utilizing Java Swing and Ghidra's plugin architecture. - -Most of Kaiju's tools are actually Analysis plugins that run automatically -when the "Auto Analysis" option is chosen, either upon import of -a new executable to disassemble, or by directly choosing -`Analysis > Auto Analyze...` from the code browser window. You will -see several CERT Analysis plugins selected by default in the Auto Analyze -tool, but you can enable/disable any as desired. - -The Analysis tools must be run before the various GUI tools will work, -however. In some corner cases, it may even be helpful to run the -Auto Analysis twice to ensure all of the metadata is produced -to create correct partitioning and disassembly information, which -in turn can influence the hashing results. - -Analyzers include: -- **DisasmImprovements** = improves the function partitioning of the - disassembly compared to the standard Ghidra partitioning. -- **Fn2Hash** = calculates function hashes for all functions in a program. - -The GUI tools include: -- **Function Hash Viewer** = a plugin that displays an interactive list -of functions in a program, and their hashes. Analysts can use this -to export one or more functions from a program into YARA signatures. -- **Function Set Intersection** = a plugin that displays a simple table -comparing the programs imported to a Ghidra project. Programs with -matching PIC hashes for functions are identified. Analysts can use -this information to focus on functions held in common by multiple -programs that may be interesting or useful in identifying families -of software. -- **OOAnalyzer JSON Importer** = a plugin that can -load, parse, and apply Pharos-generated OOAnalyzer results to object -oriented C++ executables in a Ghidra project. When launched, the -plugin will prompt the user for the JSON output file produced by -OOAnalyzer that contains information about recovered C++ -classes. After loading the JSON file, recovered C++ data types and -symbols found by OOAnalyzer are updated in the Ghidra Code -Browser. The plugin's design and implementation details are described -in our SEI blog post titled [Using OOAnalyzer to Reverse Engineer -Object Oriented Code with Ghidra][ooanalyzer-blog]. - -### Command-line "Headless" Mode - -Ghidra also supports a "headless" mode allowing tools to be run -in some circumstances without use of the interactive GUI. -These commands can therefore be utilized for scripting and -"batch mode" jobs of large numbers of files. - -Please see the `README.md` file in the `ghidra_scripts/` -directory for more information on using this mode. - - ## License -This software is licensed under a BSD-style license +This software is licensed under a simplified BSD-style license by the Software Engineering Institute at Carnegie Mellon University. Please find full details of this license in the `LICENSE.md` file diff --git a/extension.properties b/extension.properties index ff2fd17..551ec0e 100644 --- a/extension.properties +++ b/extension.properties @@ -1,5 +1,5 @@ name=CERT Kaiju description=CERT Kaiju - Binary Analysis Framework for Ghidra author=CERT Executable Code Analysis Team -createdOn=03/15/2021 +createdOn=03/29/2021 version=@extversion@ diff --git a/ghidra_scripts/exportCSVHeadless.java b/ghidra_scripts/exportCSVHeadless.java index a8b6cc6..461113c 100644 --- a/ghidra_scripts/exportCSVHeadless.java +++ b/ghidra_scripts/exportCSVHeadless.java @@ -88,8 +88,8 @@ protected void run() throws Exception { csv_fields_joined.add(fnhash.getMnemonicCountHash().toUpperCase()); // mnemonic_count_hash csv_fields_joined.add(fnhash.getMnemonicCategoryHash().toUpperCase()); // mnemonic_category_hash csv_fields_joined.add(fnhash.getMnemonicCategoryCountHash().toUpperCase()); // mnemonic_category_counts_hash - csv_fields_joined.add(fnhash.getMnemonicCountString()); // mnemonic_count_string - csv_fields_joined.add(fnhash.getMnemonicCategoryCountString()); // mnemonic_category_count_string + csv_fields_joined.add(fnhash.getMnemonicCountString().toUpperCase()); // mnemonic_count_string + csv_fields_joined.add(fnhash.getMnemonicCategoryCountString().toUpperCase()); // mnemonic_category_count_string csvFileWriter.write(csv_fields_joined.toString()+"\n"); } diff --git a/ghidra_scripts/exportXrefsToCSVHeadless.java b/ghidra_scripts/exportXrefsToCSVHeadless.java new file mode 100644 index 0000000..d274133 --- /dev/null +++ b/ghidra_scripts/exportXrefsToCSVHeadless.java @@ -0,0 +1,51 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +import java.io.File; +import java.io.FileWriter; +import java.util.StringJoiner; + +import ghidra.app.script.GhidraScript; + +import kaiju.plugins.fnxrefs.HeadlessXrefsToCSV; + +public class exportXrefsToCSVHeadless extends GhidraScript { + public File csvFile; + public FileWriter csvFileWriter; + + + @Override + protected void run() throws Exception { + String [] args = getScriptArgs(); + + csvFile = new File(args[0]); + + // note: this will automatically overwrite the filename given! + HeadlessXrefsToCSV.writeCSV(csvFile, currentProgram); + } + +} diff --git a/ghidra_scripts/exportYaraHeadless.java b/ghidra_scripts/exportYaraHeadless.java new file mode 100644 index 0000000..a62f213 --- /dev/null +++ b/ghidra_scripts/exportYaraHeadless.java @@ -0,0 +1,161 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.util.StringJoiner; +import java.lang.StringBuilder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import ghidra.app.script.GhidraScript; + +import ghidra.program.model.util.PropertyMapManager; +import ghidra.program.model.util.ObjectPropertyMap; +import ghidra.program.model.address.AddressIterator; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; +import ghidra.util.task.*; + +import kaiju.fnhash.internal.FnHashSaveable; +import kaiju.fnhash.internal.FnUtils; +import kaiju.plugins.fnhashviewer.HashViewerTableModel; +import kaiju.util.ByteArrayList; +import kaiju.util.HexUtils; + +public class exportYaraHeadless extends GhidraScript { + public File csvFile; + public PrintWriter csvPrintWriter; + + + @Override + protected void run() throws Exception { + String [] args = getScriptArgs(); + + csvFile = new File(args[0]); + csvPrintWriter = new PrintWriter(csvFile); + + TaskMonitor monitor = new ConsoleTaskMonitor(); + + + // create a new property map to store various function hashes + PropertyMapManager mapmgr = currentProgram.getUsrPropertyManager(); + ObjectPropertyMap fnhashobjmap; + // check if properties exist already, or if need to create + fnhashobjmap = mapmgr.getObjectPropertyMap("__CERT_Kaiju_FnHash"); + + AddressIterator addriter = fnhashobjmap.getPropertyIterator(); + + while(addriter.hasNext()){ + Address fn_addr = addriter.next(); + + FnHashSaveable prop = (FnHashSaveable) fnhashobjmap.getObject(fn_addr); + + String filename_value = currentProgram.getExecutablePath(); + + String md5_value = currentProgram.getExecutableMD5(); + + FnUtils fnu = null; + try { + fnu = new FnUtils(currentProgram.getFunctionManager().getFunctionAt(fn_addr), currentProgram, monitor); + } catch (Exception e) { + // TODO: can we do something better here? + return; + } + List fnbytes_list = fnu.getPICBytesList(); + List fnmask_list = fnu.getPICMask(); + ByteArrayList arrayOfBytes = new ByteArrayList(); + ByteArrayList arrayOfMasks = new ByteArrayList(); + for (int j = 0; j < fnbytes_list.size(); ++j) { + arrayOfBytes.add(fnbytes_list.get(j)); + arrayOfMasks.add(fnmask_list.get(j)); + } + // the HexUtils.byteArrayToHexString function handles the YARA generation + String bytes_value = HexUtils.byteArrayToHexString(arrayOfBytes.toArray(), " ", arrayOfMasks.toArray()); + + String addr_value = fn_addr.toString(); + + String pichash_value = prop.getPICHash(); + + String numbytes_value = prop.getNumBytes().toString(); + + String numinsns_value = prop.getNumInstructions().toString(); + + // write values in YARA format + writeYaraSignature(csvPrintWriter, filename_value, md5_value, bytes_value, addr_value, pichash_value, numbytes_value, numinsns_value, monitor); + + // two new lines to give a visual break + writeNewLine(csvPrintWriter); + writeNewLine(csvPrintWriter); + } + + csvPrintWriter.flush(); + csvPrintWriter.close(); + } + + private static void writeNewLine(PrintWriter writer) { + writer.print('\n'); + } + + /** + * Write the given fileds into YARA format and save the result into + * the file specified by the writer. + */ + private final static void writeYaraSignature(PrintWriter writer, String filename_value, String md5_value, String bytes_value, String addr_value, String pichash_value, String numbytes_value, String numinsns_value, TaskMonitor monitor) { + Calendar calendar = Calendar.getInstance(); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String todays_date = formatter.format(calendar.getTime()); + + StringBuilder yarasig = new StringBuilder(); + /* + rule Func_md5_[MD5]_[ADDR] + { + strings: + // File [FILENAME] @ [ADDR] ([DATE]) + // string $md5_[MD5]_[ADDR] contains [B] bytes and [I] instructions + $md5_[MD5]_[ADDR] = { [BYTES] } + condition: + all of them + } + */ + yarasig.append(String.format("rule Func_md5_%1$s_%2$s\n", md5_value, addr_value)); + yarasig.append("{\n"); + yarasig.append("strings:\n"); + yarasig.append(String.format("\t// File '%1$s' @ %2$s (%3$s)\n", filename_value, addr_value, todays_date)); + yarasig.append(String.format("\t// PIC Hash %1$s\n", pichash_value)); + yarasig.append(String.format("\t// string $md5_%1$s_%2$s contains %3$s bytes and %4$s instructions\n", md5_value, addr_value, numbytes_value, numinsns_value)); + yarasig.append(String.format("\t$md5_%1$s_%2$s = { %3$s }\n", md5_value, addr_value, bytes_value)); + yarasig.append("condition:\n"); + yarasig.append("\tall of them\n"); + yarasig.append("}\n"); + writer.print(yarasig.toString()); + } + +} diff --git a/runKaiju b/runKaiju new file mode 100644 index 0000000..668b64f --- /dev/null +++ b/runKaiju @@ -0,0 +1,139 @@ +#!/usr/bin/env bash + +#---------------------------------------------------------------------- +# CERT Kaiju Headless launcher (see HEADLESS_README.md) +#---------------------------------------------------------------------- + +### + # CERT Kaiju + # Copyright 2021 Carnegie Mellon University. + # + # NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + # INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + # MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + # INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + # MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + # CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + # TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + # + # Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + # + # [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + # Please see Copyright notice for non-US Government use and distribution. + # + # Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + # + # This Software includes and/or makes use of the following Third-Party Software subject to its own license: + # 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + # 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + # 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + # 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + # + # DM21-0087 + ### + +# Make sure some kind of java is on the path. It's required to run the LaunchSupport program. +if ! [ -x "$(command -v java)" ] ; then + echo "Java runtime not found. Please refer to the Ghidra Installation Guide's Troubleshooting section." + exit 1 +fi + +# Make sure this script can find the Ghidra installation. +if [ -z "$GHIDRA_INSTALL_DIR" ] ; then + echo "GHIDRA_INSTALL_DIR environment variable not set. Please refer to the Ghidra Installation Guide." + exit 1 +fi + +ProgName=$(basename $0) + +sub_help(){ + echo "Usage: $ProgName [options]" + echo "Subcommands:" + echo " fn2hash Export function hashes to CSV file" + echo " fn2yara Export function signatures to YARA file" + echo " xrefs Export function XREFS to CSV file" + echo "" + echo "For help with each subcommand run:" + echo "$ProgName -h|--help" + echo "" +} + +sub_fn2hash(){ + if [ "$#" -ne 1 ]; then + echo "fn2hash: Illegal number of parameters" + echo "For help run:" + echo "$ProgName fn2hash -h|--help" + exit 1 + fi + case $1 in + "-h" | "--help") + echo "Usage: $ProgName fn2hash " + echo " path to exe to analyze" + echo "Output: a CSV file with function hash data" + ;; + *) + echo "Running 'fn2hash' subcommand." + echo "First arg is '$1'." + $GHIDRA_INSTALL_DIR/support/analyzeHeadless /tmp tmpGhidraProj -okToDelete -deleteProject -import $1 -preScript setupScript.java -postScript exportCSVHeadless.java $1.FnHashes.csv + ;; + esac +} + +sub_fn2yara(){ + if [ "$#" -ne 1 ]; then + echo "fn2yara: Illegal number of parameters" + echo "For help run:" + echo "$ProgName fn2yara -h|--help" + exit 1 + fi + case $1 in + "-h" | "--help") + echo "Usage: $ProgName fn2yara " + echo " path to exe to analyze" + echo "Output: a YARA format file with function signatures" + ;; + *) + echo "Running 'fn2yara' subcommand." + echo "First arg is '$1'." + $GHIDRA_INSTALL_DIR/support/analyzeHeadless /tmp tmpGhidraProj -okToDelete -deleteProject -import $1 -preScript setupScript.java -postScript exportYaraHeadless.java $1.yara + ;; + esac +} + +sub_xrefs(){ + if [ "$#" -ne 1 ]; then + echo "xrefs: Illegal number of parameters" + echo "For help run:" + echo "$ProgName xrefs -h|--help" + exit 1 + fi + case $1 in + "-h" | "--help") + echo "Usage: $ProgName xrefs " + echo " path to exe to analyze" + echo "Output: a CSV file with xref data" + ;; + *) + echo "Running 'xrefs' subcommand." + echo "First arg is '$1'." + $GHIDRA_INSTALL_DIR/support/analyzeHeadless /tmp tmpGhidraProj -okToDelete -deleteProject -import $1 -preScript setupScript.java -postScript exportXrefsToCSVHeadless.java $1.Xref.csv + ;; + esac +} + +subcommand=$1 +case $subcommand in + "" | "-h" | "--help") + sub_help + ;; + "fn2hash" | "fn2yara" | "xrefs") + shift + sub_${subcommand} $@ + ;; + *) + echo "Error: '$subcommand' is not a known subcommand." >&2 + echo " Run '$ProgName --help' for a list of known subcommands." >&2 + exit 1 + ;; +esac + diff --git a/src/main/java/kaiju/fnhash/export/HeadlessToCSV.java b/src/main/java/kaiju/fnhash/export/HeadlessToCSV.java index 8d39ae7..2cfb326 100644 --- a/src/main/java/kaiju/fnhash/export/HeadlessToCSV.java +++ b/src/main/java/kaiju/fnhash/export/HeadlessToCSV.java @@ -86,8 +86,8 @@ public final static void writeCSV(File csvFile, Program currentProgram) throws E csv_fields_joined.add(fnhash.getMnemonicCountHash().toUpperCase()); // mnemonic_count_hash csv_fields_joined.add(fnhash.getMnemonicCategoryHash().toUpperCase()); // mnemonic_category_hash csv_fields_joined.add(fnhash.getMnemonicCategoryCountHash().toUpperCase()); // mnemonic_category_counts_hash - csv_fields_joined.add("MISSING MNEMONIC_COUNT_STRING"); // mnemonic_count_string - csv_fields_joined.add("MISSING MNEMONIC_CATEGORY_COUNT_STRING"); // mnemonic_category_count_string + csv_fields_joined.add(fnhash.getMnemonicCountString().toUpperCase()); // mnemonic_count_string + csv_fields_joined.add(fnhash.getMnemonicCategoryCountString().toUpperCase()); // mnemonic_category_count_string csvFileWriter.write(csv_fields_joined.toString()+"\n"); } diff --git a/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerContext.java b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerContext.java new file mode 100644 index 0000000..50257ed --- /dev/null +++ b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerContext.java @@ -0,0 +1,72 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +package kaiju.plugins.fnxrefs; + +import java.util.List; +import java.util.function.Predicate; + +import docking.ActionContext; +import ghidra.app.context.DataLocationListContext; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; +import ghidra.util.table.GhidraTable; + +public class FnXrefViewerContext extends ActionContext implements DataLocationListContext { + + private FnXrefViewerProvider xrefViewerProvider; + + FnXrefViewerContext(FnXrefViewerProvider provider, GhidraTable stringsTable) { + super(provider, stringsTable); + xrefViewerProvider = provider; + } + + GhidraTable getStringsTable() { + return (GhidraTable) getContextObject(); + } + + @Override + public int getCount() { + return xrefViewerProvider.getSelectedRowCount(); + } + + @Override + public Program getProgram() { + return xrefViewerProvider.getProgram(); + } + + @Override + public List getDataLocationList() { + return xrefViewerProvider.getSelectedDataLocationList(null); + } + + @Override + public List getDataLocationList(Predicate filter) { + return xrefViewerProvider.getSelectedDataLocationList(filter); + } +} diff --git a/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerPlugin.java b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerPlugin.java new file mode 100644 index 0000000..9cae483 --- /dev/null +++ b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerPlugin.java @@ -0,0 +1,324 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +package kaiju.plugins.fnxrefs; + +import javax.swing.ImageIcon; +import java.io.File; + +import docking.ActionContext; +import docking.action.*; +import docking.widgets.OptionDialog; +import docking.widgets.dialogs.SettingsDialog; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.table.GTable; +import docking.widgets.table.GTableToCSV; +import ghidra.MiscellaneousPluginPackage; +import ghidra.app.CorePluginPackage; +import ghidra.app.events.ProgramSelectionPluginEvent; +import ghidra.app.plugin.PluginCategoryNames; +import ghidra.app.plugin.ProgramPlugin; +import ghidra.app.plugin.core.data.DataSettingsDialog; +import ghidra.app.services.GoToService; +import ghidra.framework.model.*; +import ghidra.framework.options.Options; +import ghidra.framework.plugintool.PluginInfo; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.framework.preferences.Preferences; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.program.util.*; +import ghidra.util.HelpLocation; +import ghidra.util.exception.CancelledException; +import ghidra.util.table.GhidraTable; +import ghidra.util.table.SelectionNavigationAction; +import ghidra.util.table.actions.MakeProgramSelectionAction; +import ghidra.util.task.SwingUpdateManager; +import resources.Icons; +import resources.ResourceManager; + +import kaiju.KaijuPluginPackage; +import kaiju.fnhash.export.*; +import kaiju.util.MultiLogger; + +/** + * Plugin that provides an X-Refs (cross-references) table, addresses in a given Program + * that are referenced by other data and/or code in the Program. + *

+ * + */ +//@formatter:off +@PluginInfo( + status = PluginStatus.RELEASED, + packageName = KaijuPluginPackage.NAME, + category = PluginCategoryNames.ANALYSIS, + shortDescription = "CERT Program Function Xref Viewer", + description = "View function data and instruction cross-references (X-Refs) for a single program.", + servicesRequired = { GoToService.class } +) +//@formatter:on +public class FnXrefViewerPlugin extends ProgramPlugin implements DomainObjectListener { + + private DockingAction selectAction; + private DockingAction showSettingsAction; + private SelectionNavigationAction linkNavigationAction; + private FnXrefViewerProvider provider; + private MultiLogger logger; + private SwingUpdateManager reloadUpdateMgr; + + private static final String LAST_EXPORT_FILE = "LAST_EXPORT_DIR"; + + public FnXrefViewerPlugin(PluginTool tool) { + super(tool, false, false); + logger = MultiLogger.getInstance(); + } + + void doReload() { + provider.reload(); + } + + @Override + protected void init() { + super.init(); + + provider = new FnXrefViewerProvider(this); + reloadUpdateMgr = new SwingUpdateManager(100, 60000, this::doReload); + createActions(); + } + + private void createActions() { + + /** + * Refresh action + */ + DockingAction refreshAction = new DockingAction("Re-Analyze", getName()) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + return getCurrentProgram() != null; + } + + @Override + public void actionPerformed(ActionContext context) { + reload(); + } + }; + ImageIcon refreshIcon = Icons.REFRESH_ICON; + refreshAction.setDescription("Reruns the FnXref analyzer on the current program"); + refreshAction.setToolBarData(new ToolBarData(refreshIcon)); + refreshAction.setHelpLocation(new HelpLocation("FnXrefViewerPlugin", "ReAnalyze")); + tool.addLocalAction(provider, refreshAction); + + /** + * Export to CSV + */ + DockingAction exportCSVAction = new DockingAction("Export To CSV", getName()) { + + @Override + public boolean isEnabledForContext(ActionContext context) { + return getCurrentProgram() != null; + } + + @Override + public void actionPerformed(ActionContext context) { + File file = chooseExportFile(); + if (file != null) { + GTable table = ((FnXrefViewerProvider) context.getComponentProvider()).getTable(); + + int[] selectedRows = table.getSelectedRows(); + if (selectedRows.length == 0) { + // TODO: check if headless or GUI mode that just wants to export everything + try { + HeadlessXrefsToCSV.writeCSV(file, getCurrentProgram()); + } catch (Exception e) { + // TODO: do nothing? + } + } else { + GTableToCSV.writeCSV(file, table); + } + //reload(); + } + } + }; + ImageIcon arrowIconC = Icons.ARROW_DOWN_RIGHT_ICON; + exportCSVAction.setDescription("Exports selected or entire function list to CSV format"); + exportCSVAction.setToolBarData(new ToolBarData(arrowIconC)); + exportCSVAction.setPopupMenuData(new MenuData( + new String[] { "Export", "Export to CSV..." }, + ResourceManager.loadImage("images/application-vnd.oasis.opendocument.spreadsheet-template.png"), + "Y")); + exportCSVAction.setHelpLocation(new HelpLocation("FnXrefViewerPlugin", "CSV")); + tool.addLocalAction(provider, exportCSVAction); + + /** + * Program selection + */ + tool.addLocalAction(provider, new MakeProgramSelectionAction(this, provider.getTable())); + + linkNavigationAction = new SelectionNavigationAction(this, provider.getTable()); + tool.addLocalAction(provider, linkNavigationAction); + + // settings widget + showSettingsAction = new DockingAction("Settings", getName()) { + @Override + public void actionPerformed(ActionContext context) { + try { + DataSettingsDialog dialog = provider.getSelectedRowCount() == 1 + ? new DataSettingsDialog(currentProgram, provider.getSelectedData()) + : new DataSettingsDialog(currentProgram, provider.selectData()); + + tool.showDialog(dialog); + dialog.dispose(); + } + catch (CancelledException e) { + // TODO: do nothing? + } + } + + }; + showSettingsAction.setPopupMenuData(new MenuData(new String[] { "Settings..." }, "R")); + showSettingsAction.setDescription("Shows settings for the selected strings"); + showSettingsAction.setHelpLocation(new HelpLocation("FnXrefViewerPlugin", "Hash_Settings")); + tool.addLocalAction(provider, showSettingsAction); + + } + + private void selectData(ProgramSelection selection) { + ProgramSelectionPluginEvent pspe = + new ProgramSelectionPluginEvent("Selection", selection, currentProgram); + firePluginEvent(pspe); + processEvent(pspe); + } + + @Override + public void dispose() { + reloadUpdateMgr.dispose(); + provider.dispose(); + super.dispose(); + } + + @Override + protected void programDeactivated(Program program) { + program.removeListener(this); + provider.setProgram(null); + } + + @Override + protected void programActivated(Program program) { + program.addListener(this); + provider.setProgram(program); + } + + @Override + protected void locationChanged(ProgramLocation loc) { + if (linkNavigationAction.isSelected() && loc != null) { + provider.setProgram(loc.getProgram()); + provider.showProgramLocation(loc); + } + } + + @Override + public void domainObjectChanged(DomainObjectChangedEvent ev) { + if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) || + ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_MOVED) || + ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED) || + ev.containsEvent(ChangeManager.DOCR_FUNCTION_REMOVED) || + ev.containsEvent(ChangeManager.DOCR_DATA_TYPE_CHANGED)) { + + reload(); + + } + else if (ev.containsEvent(ChangeManager.DOCR_CODE_ADDED)) { + for (int i = 0; i < ev.numRecords(); ++i) { + DomainObjectChangeRecord doRecord = ev.getChangeRecord(i); + Object newValue = doRecord.getNewValue(); + switch (doRecord.getEventType()) { + case ChangeManager.DOCR_FUNCTION_REMOVED: + ProgramChangeRecord pcRec = (ProgramChangeRecord) doRecord; + provider.remove(pcRec.getStart(), pcRec.getEnd()); + break; + case ChangeManager.DOCR_FUNCTION_ADDED: + if (newValue instanceof Function) { + provider.add((Function) newValue); + } + break; + default: + //Msg.info(this, "Unhandled event type: " + doRecord.getEventType()); + break; + } + } + } + else if (ev.containsEvent(ChangeManager.DOCR_DATA_TYPE_SETTING_CHANGED)) { + // Unusual code: because the table model goes directly to the settings values + // during each repaint, we don't need to figure out which row was changed. + provider.getComponent().repaint(); + } + } + + void reload() { + reloadUpdateMgr.update(); + } + + private GhidraFileChooser createExportFileChooser() { + GhidraFileChooser chooser = new GhidraFileChooser(provider.getComponent()); + chooser.setTitle("Choose Function XREFS Export File"); + chooser.setApproveButtonText("OK"); + + String filepath = Preferences.getProperty(LAST_EXPORT_FILE); + if (filepath != null) { + chooser.setSelectedFile(new File(filepath)); + } + + return chooser; + } + + private File chooseExportFile() { + logger.debug(this, "Starting file selection dialog, waiting for user."); + GhidraFileChooser chooser = createExportFileChooser(); + File file = chooser.getSelectedFile(); + if (file == null) { + return null; + } + if (file.exists()) { + int result = OptionDialog.showYesNoDialog(provider.getComponent(), "Overwrite?", + "File exists. Do you want to overwrite?"); + + if (result != OptionDialog.OPTION_ONE) { + return null; + } + } + storeLastExportDirectory(file); + return file; + } + + private void storeLastExportDirectory(File file) { + Preferences.setProperty(LAST_EXPORT_FILE, file.getAbsolutePath()); + Preferences.store(); + } +} diff --git a/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerProvider.java b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerProvider.java new file mode 100644 index 0000000..cdbdfe1 --- /dev/null +++ b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerProvider.java @@ -0,0 +1,308 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +package kaiju.plugins.fnxrefs; + +import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +import javax.swing.*; +import javax.swing.table.TableColumn; + +import docking.ActionContext; +import docking.widgets.table.GTableTextCellEditor; +import docking.widgets.table.threaded.ThreadedTableModelListener; +import ghidra.app.services.GoToService; +import ghidra.framework.plugintool.ComponentProviderAdapter; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataUtilities; +import ghidra.program.model.data.StringDataInstance; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; +import ghidra.program.util.ProgramSelection; +import ghidra.util.HelpLocation; +import ghidra.util.table.*; +import ghidra.util.task.TaskMonitor; +import resources.ResourceManager; + +/** + * Provider for the defined strings table. + */ +public class FnXrefViewerProvider extends ComponentProviderAdapter { + + public static final ImageIcon ICON = ResourceManager.loadImage("images/dataW.gif"); + + private GhidraThreadedTablePanel threadedTablePanel; + private GhidraTableFilterPanel filterPanel; + private GhidraTable table; + private FnXrefViewerTableModel xrefModel; + private JComponent mainPanel; + private Program currentProgram; + private HelpLocation helpLocation; + private AtomicReference delayedShowProgramLocation = new AtomicReference<>(); + + FnXrefViewerProvider(FnXrefViewerPlugin plugin) { + super(plugin.getTool(), "CERT Function Xref Viewer", plugin.getName()); + mainPanel = createWorkPanel(); + setIcon(ICON); + helpLocation = new HelpLocation(plugin.getName(), plugin.getName()); + addToTool(); + } + + @Override + public void componentHidden() { + xrefModel.reload(null); + } + + @Override + public void componentShown() { + xrefModel.reload(currentProgram); + } + + @Override + public ActionContext getActionContext(MouseEvent event) { + return new FnXrefViewerContext(this, table); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + /* + * @see ghidra.framework.docking.HelpTopic#getHelpLocation() + */ + @Override + public HelpLocation getHelpLocation() { + return helpLocation; + } + + void setProgram(Program program) { + if (program == currentProgram) { + return; + } + currentProgram = program; + delayedShowProgramLocation.set(null); + if (isVisible()) { + xrefModel.reload(program); + } + } + + void dispose() { + currentProgram = null; + removeFromTool(); + threadedTablePanel.dispose(); + filterPanel.dispose(); + } + + private JComponent createWorkPanel() { + + xrefModel = new FnXrefViewerTableModel(tool); + + threadedTablePanel = new GhidraThreadedTablePanel<>(xrefModel, 1000); + table = threadedTablePanel.getTable(); + table.setName("FnXrefDataTable"); + table.setPreferredScrollableViewportSize(new Dimension(350, 150)); + table.getSelectionModel().addListSelectionListener(e -> notifyContextChanged()); + + xrefModel.addTableModelListener(e -> { + int rowCount = xrefModel.getRowCount(); + int unfilteredCount = xrefModel.getUnfilteredRowCount(); + + setSubTitle("" + rowCount + " functions" + + (rowCount != unfilteredCount ? " (of " + unfilteredCount + ")" : "")); + }); + + xrefModel.addThreadedTableModelListener(new ThreadedTableModelListener() { + + @Override + public void loadingStarted() { + // ignore + } + + @Override + public void loadingFinished(boolean wasCancelled) { + // loadingFinished gets called when the table is empty + // and then when it finishes loading. + // Only de-queue the delayedProgramLocation if we have records in the model. + if (xrefModel.getRowCount() != 0) { + ProgramLocation delayedProgLoc = delayedShowProgramLocation.getAndSet(null); + if (delayedProgLoc != null) { + doShowProgramLocation(delayedProgLoc); + } + } + } + + @Override + public void loadPending() { + // ignore + } + }); + + GoToService goToService = tool.getService(GoToService.class); + table.installNavigation(goToService, goToService.getDefaultNavigatable()); + + filterPanel = new GhidraTableFilterPanel<>(table, xrefModel); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(threadedTablePanel, BorderLayout.CENTER); + panel.add(filterPanel, BorderLayout.SOUTH); + + return panel; + } + + private void notifyContextChanged() { + tool.contextChanged(this); + } + + ProgramSelection selectData() { + return table.getProgramSelection(); + } + + void add(Function data) { + if (isVisible()) { + xrefModel.addDataInstance(currentProgram, data, TaskMonitor.DUMMY); + } + } + + void remove(Address addr) { + if (isVisible()) { + xrefModel.removeDataInstanceAt(addr); + } + } + + void remove(Address start, Address end) { + if (isVisible()) { + long count = end.subtract(start); + for (long offset = 0; offset < count; offset++) { + xrefModel.removeDataInstanceAt(start.add(offset)); + } + } + } + + void reload() { + if (isVisible()) { + xrefModel.reload(); + } + } + + public GhidraTable getTable() { + return table; + } + + public FnXrefViewerTableModel getModel() { + return xrefModel; + } + + private void doShowProgramLocation(ProgramLocation loc) { + ProgramLocation realLoc = xrefModel.findEquivProgramLocation(loc); + if (realLoc != null) { + int rowIndex = xrefModel.getViewIndex(realLoc); + if (rowIndex >= 0) { + table.selectRow(rowIndex); + table.scrollToSelectedRow(); + } + else { + getTool().setStatusInfo( + "Function at " + realLoc.getAddress() + " is filtered out of table view", false); + } + } + } + + public void showProgramLocation(ProgramLocation loc) { + if (loc == null) { + return; + } + + if (!xrefModel.isBusy()) { + doShowProgramLocation(loc); + } + else { + delayedShowProgramLocation.set(loc); + } + } + + public int getSelectedRowCount() { + return table.getSelectedRowCount(); + } + + public Data getSelectedData() { + int selectedRow = table.getSelectedRow(); + if (selectedRow < 0) { + return null; + } + ProgramLocation location = xrefModel.getRowObject(selectedRow); + return DataUtilities.getDataAtLocation(location); + } + + public List getSelectedDataList(Predicate filter) { + List list = new ArrayList<>(); + int[] selectedRows = table.getSelectedRows(); + for (int row : selectedRows) { + ProgramLocation location = xrefModel.getRowObject(row); + Data data = DataUtilities.getDataAtLocation(location); + if (passesFilter(data, filter)) { + list.add(data); + } + } + return list; + } + + public List getSelectedDataLocationList(Predicate filter) { + List result = new ArrayList<>(); + int[] selectedRows = table.getSelectedRows(); + for (int row : selectedRows) { + ProgramLocation location = xrefModel.getRowObject(row); + Data data = DataUtilities.getDataAtLocation(location); + if (passesFilter(data, filter)) { + result.add(location); + } + } + return result; + } + + private boolean passesFilter(Data data, Predicate filter) { + if (data == null) { + return false; + } + if (filter == null) { + return true; + } + return filter.test(data); + } + + public Program getProgram() { + return currentProgram; + } + +} diff --git a/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerTableModel.java b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerTableModel.java new file mode 100644 index 0000000..bb46dfe --- /dev/null +++ b/src/main/java/kaiju/plugins/fnxrefs/FnXrefViewerTableModel.java @@ -0,0 +1,272 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +package kaiju.plugins.fnxrefs; + +import java.util.HashMap; +import java.util.Map; + +import db.NoTransactionException; +import docking.widgets.table.DynamicTableColumn; +import docking.widgets.table.TableColumnDescriptor; +import ghidra.app.plugin.core.string.translate.ManualStringTranslationService; +import ghidra.docking.settings.Settings; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.ServiceProvider; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; +import ghidra.program.model.util.PropertyMapManager; +import ghidra.program.model.util.ObjectPropertyMap; +import ghidra.program.util.*; +import ghidra.util.StringUtilities; +import ghidra.util.Swing; +import ghidra.util.datastruct.Accumulator; +import ghidra.util.exception.CancelledException; +import ghidra.util.table.AddressBasedTableModel; +import ghidra.util.table.column.AbstractGColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; +import ghidra.util.table.field.AbstractProgramLocationTableColumn; +import ghidra.util.table.field.AddressBasedLocation; +import ghidra.util.task.TaskMonitor; + +import kaiju.util.HexUtils; +import kaiju.util.KaijuPropertyManager; + +/** + * Table model for the Fn Xrefs Viewer table. + *

+ * This implementation keeps a local index of Address to row object (which are ProgramLocations) + * so that DomainObjectChangedEvent events can be efficiently handled. + */ +public class FnXrefViewerTableModel extends AddressBasedTableModel { + + private Map rowsIndexedByAddress = new HashMap<>(); + + /** + * Columns defined by this table (useful for enum.ordinal()). + * WARNING: Update GTableToYARA if this enum ever is updated! + */ + public enum COLUMNS { + ADDRESS_COL, + DATA_XREF_COUNT_COL, + CODE_XREF_COUNT_COL + } + + FnXrefViewerTableModel(PluginTool tool) { + super("FnXref Viewer Table", tool, null, null); + } + + @Override + protected TableColumnDescriptor createTableColumnDescriptor() { + TableColumnDescriptor descriptor = new TableColumnDescriptor<>(); + + // These columns need to match the COLUMNS enum indexes + descriptor.addVisibleColumn(new DataLocationColumn(), 1, true); + descriptor.addVisibleColumn(new CodeXrefCountColumn()); + descriptor.addVisibleColumn(new DataXrefCountColumn()); + + return descriptor; + } + + @Override + protected void doLoad(Accumulator accumulator, TaskMonitor monitor) + throws CancelledException { + rowsIndexedByAddress.clear(); + + Program localProgram = getProgram(); + if (localProgram == null) { + return; + } + + Listing listing = localProgram.getListing(); + + ReferenceManager refman = localProgram.getReferenceManager(); + + monitor.setCancelEnabled(true); + monitor.initialize(listing.getNumDefinedData()); + Swing.allowSwingToProcessEvents(); + for (Function funcInstance : localProgram.getFunctionManager().getFunctions(true)) { + accumulator.add(createIndexedFunctionInstanceLocation(localProgram, funcInstance)); + monitor.checkCanceled(); + monitor.incrementProgress(1); + } + } + + private ProgramLocation createIndexedFunctionInstanceLocation(Program localProgram, Function data) { + ProgramLocation pl = new ProgramLocation(localProgram, data.getEntryPoint(), + null, null, 0, 0, 0); + rowsIndexedByAddress.put(data.getEntryPoint(), pl); + return pl; + } + + public void removeDataInstanceAt(Address addr) { + ProgramLocation progLoc = rowsIndexedByAddress.get(addr); + if (progLoc != null) { + removeObject(progLoc); + } + } + + public ProgramLocation findEquivProgramLocation(ProgramLocation pl) { + return (pl != null) ? rowsIndexedByAddress.get(pl.getAddress()) : null; + } + + public void addDataInstance(Program localProgram, Function data, TaskMonitor monitor) { + addObject(createIndexedFunctionInstanceLocation(localProgram, data)); + } + // localProgram.getFunctionManager().getFunctions(true) + // public void addDataInstance(Program localProgram, Data data, TaskMonitor monitor) { + // for (Data stringInstance : DefinedDataIterator.definedStrings(data)) { + // addObject(createIndexedFunctionInstanceLocation(localProgram, stringInstance)); + // } + // } + + @Override + public ProgramSelection getProgramSelection(int[] rows) { + AddressSet set = new AddressSet(); + for (int element : rows) { + ProgramLocation progLoc = filteredData.get(element); + set.add(progLoc.getAddress()); + } + return new ProgramSelection(set); + } + + public void reload(Program newProgram) { + setProgram(newProgram); + reload(); + } + + @Override + public Address getAddress(int row) { + return getRowObject(row).getAddress(); + } + +//================================================================================================== +// Inner Classes +//================================================================================================== + + private static class DataLocationColumn + extends AbstractProgramLocationTableColumn { + + @Override + public String getColumnName() { + return "Location"; + } + + @Override + public AddressBasedLocation getValue(ProgramLocation rowObject, Settings settings, + Program program, ServiceProvider serviceProvider) throws IllegalArgumentException { + return new AddressBasedLocation(rowObject.getProgram(), rowObject.getAddress()); + + } + + @Override + public ProgramLocation getProgramLocation(ProgramLocation rowObject, Settings settings, + Program program, ServiceProvider serviceProvider) { + return rowObject; + } + + } + + private static class CodeXrefCountColumn + extends AbstractProgramLocationTableColumn { + + @Override + public String getColumnName() { + return "Code X-Refs Count"; + } + + @Override + public String getValue(ProgramLocation rowObject, Settings settings, + Program program, ServiceProvider serviceProvider) throws IllegalArgumentException { + + // function entry point address + Address fnEntryAddress = rowObject.getAddress(); + + ReferenceManager refman = program.getReferenceManager(); + ReferenceIterator refiter = refman.getReferencesTo(fnEntryAddress); + //refman.getReferenceCountTo(fnEntryAddress); + + int codeRefCnt = 0; + for (Reference xref : refiter) { + if (!xref.getReferenceType().isData()) { + codeRefCnt++; + } + } + + return String.valueOf(codeRefCnt); + + } + + @Override + public ProgramLocation getProgramLocation(ProgramLocation rowObject, Settings settings, + Program program, ServiceProvider serviceProvider) { + return rowObject; + } + + } + + private static class DataXrefCountColumn + extends AbstractProgramLocationTableColumn { + + @Override + public String getColumnName() { + return "Data X-Refs Count"; + } + + @Override + public String getValue(ProgramLocation rowObject, Settings settings, + Program program, ServiceProvider serviceProvider) throws IllegalArgumentException { + + // function entry point address + Address fnEntryAddress = rowObject.getAddress(); + + ReferenceManager refman = program.getReferenceManager(); + ReferenceIterator refiter = refman.getReferencesTo(fnEntryAddress); + + int dataRefCnt = 0; + for (Reference xref : refiter) { + if (xref.getReferenceType().isData()) { + dataRefCnt++; + } + } + + return String.valueOf(dataRefCnt); + + } + + @Override + public ProgramLocation getProgramLocation(ProgramLocation rowObject, Settings settings, + Program program, ServiceProvider serviceProvider) { + return rowObject; + } + + } + +} diff --git a/src/main/java/kaiju/plugins/fnxrefs/HeadlessXrefsToCSV.java b/src/main/java/kaiju/plugins/fnxrefs/HeadlessXrefsToCSV.java new file mode 100644 index 0000000..7f9d1ec --- /dev/null +++ b/src/main/java/kaiju/plugins/fnxrefs/HeadlessXrefsToCSV.java @@ -0,0 +1,84 @@ +/*** + * CERT Kaiju + * Copyright 2021 Carnegie Mellon University. + * + * NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING + * INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY + * MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER + * INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR + * MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. + * CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT + * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * Released under a BSD (SEI)-style license, please see LICENSE.md or contact permission@sei.cmu.edu for full terms. + * + * [DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution. + * Please see Copyright notice for non-US Government use and distribution. + * + * Carnegie Mellon (R) and CERT (R) are registered in the U.S. Patent and Trademark Office by Carnegie Mellon University. + * + * This Software includes and/or makes use of the following Third-Party Software subject to its own license: + * 1. OpenJDK (http://openjdk.java.net/legal/gplv2+ce.html) Copyright 2021 Oracle. + * 2. Ghidra (https://github.com/NationalSecurityAgency/ghidra/blob/master/LICENSE) Copyright 2021 National Security Administration. + * 3. GSON (https://github.com/google/gson/blob/master/LICENSE) Copyright 2020 Google. + * 4. JUnit (https://github.com/junit-team/junit5/blob/main/LICENSE.md) Copyright 2020 JUnit Team. + * + * DM21-0087 + */ +package kaiju.plugins.fnxrefs; + +import java.io.File; +import java.io.FileWriter; +import java.util.StringJoiner; + +import ghidra.program.model.util.PropertyMapManager; +import ghidra.program.model.util.ObjectPropertyMap; +import ghidra.program.model.address.AddressIterator; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; + +import ghidra.util.Msg; + +public final class HeadlessXrefsToCSV { + + public final static void writeCSV(File csvFile, Program currentProgram) throws Exception { + + FileWriter csvFileWriter = new FileWriter(csvFile,true); + + ReferenceManager refman = currentProgram.getReferenceManager(); + + // TODO: loop over all function entry point address + for (Function fn : currentProgram.getFunctionManager().getFunctions(true)) { + Address fnEntryAddress = fn.getEntryPoint(); + ReferenceIterator refiter = refman.getReferencesTo(fnEntryAddress); + + int dataRefCnt = 0; + int codeRefCnt = 0; + for (Reference xref : refiter) { + if (xref.getReferenceType().isData()) { + dataRefCnt++; + } else { + codeRefCnt++; + } + } + + /* + * Build CSV Line + */ + + StringJoiner csv_fields_joined = new StringJoiner(","); + + csv_fields_joined.add("0x"+fnEntryAddress.toString().toUpperCase()); + csv_fields_joined.add(Integer.toString(codeRefCnt)); + csv_fields_joined.add(Integer.toString(dataRefCnt)); + + csvFileWriter.write(csv_fields_joined.toString()+"\n"); + } + + csvFileWriter.flush(); + csvFileWriter.close(); + } + +}