-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 72be3de
Showing
13 changed files
with
713 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
insert_final_newline = true | ||
charset = utf-8 | ||
|
||
[*.java] | ||
indent_style = tab | ||
indent_size = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
.gradle/ | ||
.idea/ | ||
.settings/ | ||
bin/ | ||
build/ | ||
dist/ | ||
gradle/ | ||
.antProperties.xml | ||
.project | ||
.pydevproject | ||
.classpath | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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------------------------------- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
name=@extname@ | ||
description=Ghidra firmware utilities | ||
author=Alex James <[email protected]> | ||
createdOn= | ||
version=@extversion@ |
73 changes: 73 additions & 0 deletions
73
src/main/java/firmware/option_rom/LegacyOptionROMHeader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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
113
src/main/java/firmware/option_rom/OptionROMConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.