Skip to content

Commit

Permalink
Ghidra: Implement BinExport.java Ghidra script
Browse files Browse the repository at this point in the history
  • Loading branch information
gemesa committed Oct 17, 2024
1 parent 624377d commit ee0e7b3
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 2 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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".
Expand All @@ -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 <project_location> <project_name> -import <file>
```
Run `BinExport.java` which will generate the `.BinExport` file specified in `BinExport.properties`:

```
$ ./analyzeHeadless <project_location> <project_name> -process <file> -propertiesPath <path> -preScript BinExport.java -noanalysis
```

Alternatively, use command-line arguments instead of `BinExport.properties`:

```
$ ./analyzeHeadless <project_location> <project_name> -process <file> -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
Expand Down
117 changes: 117 additions & 0 deletions java/ghidra_scripts/BinExport.java
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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;
}
}

0 comments on commit ee0e7b3

Please sign in to comment.