Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
al3xtjames committed May 28, 2019
0 parents commit 72be3de
Show file tree
Hide file tree
Showing 13 changed files with 713 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .editorconfig
@@ -0,0 +1,10 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8

[*.java]
indent_style = tab
indent_size = 4
12 changes: 12 additions & 0 deletions .gitignore
@@ -0,0 +1,12 @@
.gradle/
.idea/
.settings/
bin/
build/
dist/
gradle/
.antProperties.xml
.project
.pydevproject
.classpath

Empty file added Module.manifest
Empty file.
50 changes: 50 additions & 0 deletions README.md
@@ -0,0 +1,50 @@
Ghidra Firmware Utilities
=========================

Various modules for [Ghidra][1] to assist with PC firmware reverse-engineering.
This was accepted as a [coreboot project][2] for GSoC 2019.

## Features (very much WIP)
### PCI option ROM loader
- Implements a FS loader for PCI option ROMs (handles hybrid ROMs,
e.g. legacy x86 + UEFI)
- Loads uncompressed UEFI executables from PCI option ROMs
- Calculates entry point address for legacy x86 option ROMs (still needs to be
manually loaded as a raw real-mode binary)
- TODO: Write loader for legacy x86 option ROMs (automatically select
real-mode x86)
- TODO: Implement support for compressed UEFI executables

## Planned functionality / TODO
### Firmware image loader
- Implement FS loader for firmware images
- Write parsers for Intel IFD (BIOS region), coreboot CBFS/FMAP, and UEFI
firmware volumes

### UEFI loader
- Write helper script to import GUIDs/etc (similar to [ida-efitools][3])

## Building & Installation

Ghidra's standard Gradle build system is used. Set the `GHIDRA_INSTALL_DIR`
environment variable before building:

```bash
export GHIDRA_INSTALL_DIR="/path/to/ghidra"
gradle
```

The module ZIP will be output to `dist/`. Use **File > Install Extensions** and
select the green plus to browse to the extension. Restart Ghidra when prompted.

## Usage

### PCI option ROM loader
Add a PCI option ROM to a Ghidra project. When prompted to select an import
mode, select **File system**. The images contained within the option ROM will
be displayed, and can be imported for analysis. Information for each image can
be displayed by selecting **Get Info** in the right-click menu for an image.

[1]: https://ghidra-sre.org/
[2]: https://summerofcode.withgoogle.com/projects/#6413737605464064
[3]: https://github.com/danse-macabre/ida-efitools
33 changes: 33 additions & 0 deletions build.gradle
@@ -0,0 +1,33 @@
// Builds a Ghidra Extension for a given Ghidra installation.
//
// An absolute path to the Ghidra installation directory must be supplied either by setting the
// GHIDRA_INSTALL_DIR environment variable or Gradle project property:
//
// > export GHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
// > gradle
//
// or
//
// > gradle -PGHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
//
// Gradle should be invoked from the directory of the project to build. Please see the
// application.gradle.version property in <GHIDRA_INSTALL_DIR>/Ghidra/application.properties
// for the correction version of Gradle to use for the Ghidra installation you specify.

//----------------------START "DO NOT MODIFY" SECTION------------------------------
def ghidraInstallDir

if (System.env.GHIDRA_INSTALL_DIR) {
ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR
}
else if (project.hasProperty("GHIDRA_INSTALL_DIR")) {
ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR")
}

if (ghidraInstallDir) {
apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle"
}
else {
throw new GradleException("GHIDRA_INSTALL_DIR is not defined!")
}
//----------------------END "DO NOT MODIFY" SECTION-------------------------------
5 changes: 5 additions & 0 deletions extension.properties
@@ -0,0 +1,5 @@
name=@extname@
description=Ghidra firmware utilities
author=Alex James <theracermaster@gmail.com>
createdOn=
version=@extversion@
73 changes: 73 additions & 0 deletions src/main/java/firmware/option_rom/LegacyOptionROMHeader.java
@@ -0,0 +1,73 @@
/* ###
* IP: GHIDRA
*
* 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
*
* http://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.
*/
package firmware.option_rom;

import ghidra.app.util.bin.BinaryReader;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Formatter;

public class LegacyOptionROMHeader extends OptionROMHeader {
// Original header fields
private byte imageSize;
private byte[] entryPointInstruction;

private short entryPointOffset;
private byte[] x86Image;

public LegacyOptionROMHeader(BinaryReader reader) throws IOException {
super(reader);
reader.setPointerIndex(0x2);
imageSize = reader.readNextByte();
entryPointInstruction = reader.readNextByteArray(3);

// The entry point field usually contains a relative JMP instruction. Decode it to find the
// address of the entry point.
entryPointOffset = 0x3;
int executableSize;
if (entryPointInstruction[0] == (byte) 0xEB) {
entryPointOffset += entryPointInstruction[1];
entryPointOffset += 0x2;
executableSize = imageSize * OptionROMConstants.ROM_SIZE_UNIT - entryPointOffset;
} else if (entryPointInstruction[0] == (byte) 0xE9) {
entryPointOffset +=
(short) (entryPointInstruction[2] << 8 | entryPointInstruction[1] & 0xFF);
entryPointOffset += 0x3;
executableSize = imageSize * OptionROMConstants.ROM_SIZE_UNIT - entryPointOffset;
} else {
executableSize = imageSize * OptionROMConstants.ROM_SIZE_UNIT - 0x3;
}

reader.setPointerIndex(entryPointOffset);
x86Image = reader.readNextByteArray(executableSize);
}

@Override
public ByteArrayInputStream getImageStream() {
return new ByteArrayInputStream(x86Image);
}

@Override
public String toString() {
Formatter formatter = new Formatter();
formatter.format("Entry Point Instruction: %02X %02X %02X\n", entryPointInstruction[0],
entryPointInstruction[1], entryPointInstruction[2]);
formatter.format("Decoded Entry Point Address: 0x%X\n", entryPointOffset);
formatter.format("%s\n", super.toString());
return formatter.toString();
}
}
113 changes: 113 additions & 0 deletions src/main/java/firmware/option_rom/OptionROMConstants.java
@@ -0,0 +1,113 @@
/* ###
* IP: GHIDRA
*
* 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
*
* http://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.
*/
package firmware.option_rom;

public final class OptionROMConstants {
// PCI option ROM signature
public static final short ROM_SIGNATURE = (short) 0xAA55;
public static final byte[] ROM_SIGNATURE_BYTES = {0x55, (byte) 0xAA};

// PCI data structure header signature
public static final String PCIR_SIGNATURE = "PCIR";

// ROM size unit
// The image length field in the PCI data structure header is in units of 512 bytes.
public static final int ROM_SIZE_UNIT = 512;

// PCI option ROM code type field
public static final class CodeType {
public static final byte PC_AT_COMPATIBLE = 0;
public static final byte OPEN_FIRMWARE = 1;
public static final byte PA_RISC = 2;
public static final byte EFI = 3;

public static String toString(byte codeType) {
switch (codeType) {
case PC_AT_COMPATIBLE:
return "PC-AT Compatible";
case OPEN_FIRMWARE:
return "Open Firmware";
case PA_RISC:
return "PA-RISC";
case EFI:
return "EFI";
default:
return String.format("Unknown code type (0x%X)", codeType);
}
}
}

// (U)EFI option ROM signature
public static final int EFI_SIGNATURE = 0x0EF1;

// (U)EFI image subsystems
public static final class EFIImageSubsystem {
public static final short APPLICATION = 10;
public static final short BOOT_SERVICE_DRIVER = 11;
public static final short RUNTIME_DRIVER = 12;

public static String toString(short subsystem) {
switch (subsystem) {
case APPLICATION:
return "EFI Application";
case BOOT_SERVICE_DRIVER:
return "EFI Boot Service Driver";
case RUNTIME_DRIVER:
return "EFI Runtime Driver";
default:
return String.format("Unknown EFI subsystem (0x%X)", subsystem);
}
}
}

// (U)EFI image machine types
public static final class EFIImageMachineType {
public static final short IA32 = 0x014C;
public static final short IA64 = 0x0200;
public static final short EBC = 0x0EBC;
public static final short X64 = (short) 0x8664;
public static final short ARMTHUMB_MIXED = 0x01C2;
public static final short AARCH64 = (short) 0xAA64;
public static final short RISCV32 = 0x5032;
public static final short RISCV64 = 0x5064;
public static final short RISCV128 = 0x5128;

public static String toString(short machineType) {
switch (machineType) {
case IA32:
return "x86";
case IA64:
return "IA64";
case EBC:
return "EFI Byte Code";
case X64:
return "x86_64";
case ARMTHUMB_MIXED:
return "ARM (mixed with Thumb)";
case AARCH64:
return "AArch64";
case RISCV32:
return "RISC-V (32-bit)";
case RISCV64:
return "RISC-V (64-bit)";
case RISCV128:
return "RISC-V (128-bit)";
default:
return String.format("Unknown EFI machine type (0x%X)", machineType);
}
}
}
}

0 comments on commit 72be3de

Please sign in to comment.