From ee0e7b3b23863140990346f5e7cb2ec47f345554 Mon Sep 17 00:00:00 2001 From: Andras Gemes Date: Wed, 16 Oct 2024 16:01:01 +0200 Subject: [PATCH] Ghidra: Implement BinExport.java Ghidra script --- README.md | 30 +++++++- java/ghidra_scripts/BinExport.java | 117 +++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 java/ghidra_scripts/BinExport.java diff --git a/README.md b/README.md index 734e6838..d3e53120 100644 --- a/README.md +++ b/README.md @@ -190,8 +190,6 @@ directory. ### Ghidra -There is only minimal integration into the Ghidra UI at this time. - 1. Open or create a project. For new projects, import a file first using `File`|`Import File...` 2. Right-click a file in the current project list and select `Export...` from the context menu. 3. In the "Export" dialog, under "Format", choose "Binary Export (v2) for BinDiff". @@ -200,6 +198,34 @@ There is only minimal integration into the Ghidra UI at this time. 5. Optional: click "Options..." to set additional export options. 6. Click "OK", then click "OK" again to dismiss the "Export Results Summary" dialog. +#### Scripting + +The `BinExport.java` Ghidra script can be run in both headless and GUI modes. In GUI mode, it is available under the `BinExport` category in the Script Manager. For headless mode, a `BinExport.properties` file with the following content (or similar, depending on the options you want to use) can be used: + +``` +Choose export file Export = test.BinExport +Choose options IDA Pro Compatibility = "Subtract Imagebase;Remap mnemonics;Prepend Namespace to Function Names" +``` + +##### Example usage in headless mode + +Create a project, import and analyze a binary: + +``` +$ ./analyzeHeadless -import +``` +Run `BinExport.java` which will generate the `.BinExport` file specified in `BinExport.properties`: + +``` +$ ./analyzeHeadless -process -propertiesPath -preScript BinExport.java -noanalysis +``` + +Alternatively, use command-line arguments instead of `BinExport.properties`: + +``` +$ ./analyzeHeadless -process -preScript BinExport.java test.BinExport "Prepend Namespace to Function Names" -noanalysis +``` + ## How to build Below are build instructions for the native code plugins for IDA Pro and diff --git a/java/ghidra_scripts/BinExport.java b/java/ghidra_scripts/BinExport.java new file mode 100644 index 00000000..06d794d4 --- /dev/null +++ b/java/ghidra_scripts/BinExport.java @@ -0,0 +1,117 @@ +// Copyright 2019-2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// @category BinExport + +import com.google.security.binexport.BinExport2Builder; +import com.google.security.binexport.IdaProMnemonicMapper; +import com.google.security.zynamics.BinExport.BinExport2; +import ghidra.app.script.GhidraScript; +import ghidra.app.util.exporter.ExporterException; +import ghidra.framework.model.DomainObject; +import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.listing.Program; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * Exports Ghidra disassembly data into BinExport v2 format. + * + * @author Andras Gemes + * @author Christian Blichmann + */ +public class BinExport extends GhidraScript { + + /** Stringized version number allowing for scriptable update. */ + private static final String BINEXPORT_VERSION = "12"; + + /** Copyright that appears in the console. */ + private static final String BINEXPORT_COPYRIGHT = + "BinExport " + BINEXPORT_VERSION + " (c)2019-2024 Google LLC"; + + /** Display name that appears in the console. */ + private static final String BINEXPORT_DISPLAY_NAME = "Binary BinExport (v2) for BinDiff"; + + // Choice names + /** Whether to subtract the program image base from addresses for export. */ + private static final String IDAPRO_COMPAT_OPT_SUBTRACT_IMAGEBASE = "Subtract Imagebase"; + + /** Whether to remap Ghidra's mnemonics into IDA Pro style ones. */ + private static final String IDAPRO_COMPAT_OPT_REMAP_MNEMONICS = "Remap mnemonics"; + + /** Whether to prepend "namespace::" to function names where the namespace is not "Global" */ + private static final String IDAPRO_COMPAT_OPT_PREPEND_NAMESPACE = + "Prepend Namespace to Function Names"; + + @Override + protected void run() throws Exception { + println(BINEXPORT_DISPLAY_NAME); + println(BINEXPORT_COPYRIGHT); + File outputFile = askFile("Choose export file", "Export"); + + List choices = + askChoices( + "Choose options", + "IDA Pro Compatibility", + Arrays.asList( + IDAPRO_COMPAT_OPT_SUBTRACT_IMAGEBASE, + IDAPRO_COMPAT_OPT_REMAP_MNEMONICS, + IDAPRO_COMPAT_OPT_PREPEND_NAMESPACE)); + + DomainObject domainObj = currentProgram; + AddressSetView addrSet = currentProgram.getMemory(); + + if (!export(outputFile, domainObj, addrSet, choices)) { + printerr("Export failed"); + } else { + println("Export successful"); + } + } + + public boolean export( + File file, DomainObject domainObj, AddressSetView addrSet, List selectedOptions) + throws ExporterException, IOException { + + if (!(domainObj instanceof Program)) { + printerr("Unsupported type: " + domainObj.getClass().getName()); + return false; + } + + Program program = (Program) domainObj; + + var builder = new BinExport2Builder(program, addrSet); + + if (selectedOptions.contains(IDAPRO_COMPAT_OPT_REMAP_MNEMONICS)) { + builder.setMnemonicMapper(new IdaProMnemonicMapper(program.getLanguage())); + } + if (selectedOptions.contains(IDAPRO_COMPAT_OPT_SUBTRACT_IMAGEBASE)) { + builder.setAddressOffset(program.getImageBase().getOffset()); + } + if (selectedOptions.contains(IDAPRO_COMPAT_OPT_PREPEND_NAMESPACE)) { + builder.setPrependNamespace(true); + } + + final BinExport2 proto = builder.build(); + + println("Writing BinExport2 file"); + try (var outputStream = new FileOutputStream(file)) { + proto.writeTo(outputStream); + } + + return true; + } +}