├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE.txt ├── Module.manifest ├── README.md ├── build.gradle ├── data ├── README.txt ├── gen_prf.sh ├── guids.csv ├── uefi_aarch64.gdt ├── uefi_arm.gdt ├── uefi_ia32.gdt └── uefi_x64.gdt ├── extension.properties ├── ghidra_scripts ├── README.txt └── UEFIHelper.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib └── README.txt ├── os ├── linux_x86_64 │ └── README.txt ├── mac_x86_64 │ └── README.txt └── win_x86_64 │ └── README.txt └── src ├── efidecompress └── c │ ├── efidecompress.c │ └── efidecompress.h └── main └── java ├── firmware ├── cbfs │ ├── CBFSCompressionAttribute.java │ ├── CBFSConstants.java │ ├── CBFSFile.java │ ├── CBFSFileAttribute.java │ ├── CBFSFileAttributeFactory.java │ ├── CBFSFileSystem.java │ ├── CBFSFileSystemFactory.java │ └── CBFSHeader.java ├── common │ ├── EFIDecompressor.class │ ├── EFIDecompressor.java │ ├── FileAttributeUtils.java │ ├── TianoDecompressor.java │ └── UUIDUtils.java ├── fmap │ ├── FlashMapArea.java │ ├── FlashMapConstants.java │ ├── FlashMapFileSystem.java │ ├── FlashMapFileSystemFactory.java │ └── FlashMapHeader.java ├── ifd │ ├── IntelFlashDescriptor.java │ ├── IntelFlashDescriptorConstants.java │ ├── IntelFlashFileSystem.java │ ├── IntelFlashFileSystemFactory.java │ └── IntelFlashRegion.java ├── option_rom │ ├── DeviceList.java │ ├── LegacyOptionROMHeader.java │ ├── LegacyOptionROMLoader.java │ ├── OptionROMConstants.java │ ├── OptionROMFileSystem.java │ ├── OptionROMFileSystemFactory.java │ ├── OptionROMHeader.java │ ├── OptionROMHeaderFactory.java │ ├── PCIDataStructureHeader.java │ └── UEFIOptionROMHeader.java ├── uefi_fv │ ├── FFSCompressedSection.java │ ├── FFSFreeformSubtypeSection.java │ ├── FFSGUIDDefinedSection.java │ ├── FFSGenericSection.java │ ├── FFSRawFile.java │ ├── FFSSection.java │ ├── FFSSectionFactory.java │ ├── FFSUISection.java │ ├── FFSVersionSection.java │ ├── FFSVolumeImageSection.java │ ├── UEFIFFSConstants.java │ ├── UEFIFFSFile.java │ ├── UEFIFile.java │ ├── UEFIFirmwareVolumeConstants.java │ ├── UEFIFirmwareVolumeFileSystem.java │ ├── UEFIFirmwareVolumeFileSystemFactory.java │ └── UEFIFirmwareVolumeHeader.java └── uefi_te │ ├── EFIImageDataDirectory.java │ ├── EFIImageSectionHeader.java │ ├── TELoader.java │ ├── TerseExecutableConstants.java │ └── TerseExecutableHeader.java └── util └── JNILibraryLoader.java /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | 8 | [*.java] 9 | indent_style = tab 10 | indent_size = 4 11 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: ['**'] 6 | pull_request: 7 | workflow_dispatch: 8 | release: 9 | types: [published] 10 | 11 | permissions: 12 | contents: write 13 | 14 | jobs: 15 | build-natives: 16 | strategy: 17 | matrix: 18 | os: 19 | - macos-latest 20 | - windows-latest 21 | - ubuntu-latest 22 | 23 | runs-on: ${{ matrix.os }} 24 | 25 | steps: 26 | - name: Clone Repository 27 | uses: actions/checkout@v3 28 | 29 | - name: Install Java 30 | uses: actions/setup-java@v4 31 | with: 32 | distribution: 'temurin' 33 | java-version: '17' 34 | 35 | - name: Install Ghidra 36 | uses: antoniovazquezblanco/setup-ghidra@4ecf6e0dc501f8efc1d74d93645c6d5c1df7f441 # v1.2.1 37 | with: 38 | auth_token: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | - name: Setup Gradle 41 | uses: gradle/gradle-build-action@v2.11.1 42 | 43 | - name: Build natives 44 | run: ./gradlew efidecompressSharedLibrary copyLibraries -PGHIDRA_INSTALL_DIR=${{ env.GHIDRA_INSTALL_DIR }} 45 | 46 | - name: Upload natives 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: libefidecompress_${{ matrix.os }} 50 | path: | 51 | os/*/* 52 | !os/*/README.txt 53 | 54 | build-extension: 55 | strategy: 56 | matrix: 57 | ghidra: 58 | - "11.0.3" 59 | - "11.0.2" 60 | - "11.0.1" 61 | - "11.0" 62 | - "10.4" 63 | - "10.3.3" 64 | - "10.3.2" 65 | - "10.3.1" 66 | - "10.3" 67 | - "10.2.3" 68 | - "10.2.2" 69 | - "10.2.1" 70 | - "10.2" 71 | 72 | needs: build-natives 73 | runs-on: ubuntu-latest 74 | 75 | steps: 76 | - name: Clone Repository 77 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 78 | with: 79 | fetch-depth: 0 80 | 81 | - name: Install Java 82 | uses: actions/setup-java@v4 83 | with: 84 | distribution: 'temurin' 85 | java-version: '17' 86 | 87 | - name: Install Ghidra 88 | uses: antoniovazquezblanco/setup-ghidra@4ecf6e0dc501f8efc1d74d93645c6d5c1df7f441 # v1.2.1 89 | with: 90 | auth_token: ${{ secrets.GITHUB_TOKEN }} 91 | version: ${{ matrix.ghidra }} 92 | 93 | - name: Setup Gradle 94 | uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 95 | 96 | - name: Download precompiled natives 97 | uses: actions/download-artifact@v4 98 | with: 99 | path: os/ 100 | merge-multiple: true 101 | 102 | - name: Build the plugin 103 | run: ./gradlew -PGHIDRA_INSTALL_DIR=${{ env.GHIDRA_INSTALL_DIR }} 104 | 105 | - name: Upload to Artifacts 106 | uses: actions/upload-artifact@v4 107 | with: 108 | name: firmware_utils_ghidra_${{ matrix.ghidra }} 109 | path: dist/*.zip 110 | 111 | release: 112 | runs-on: "ubuntu-latest" 113 | needs: build-extension 114 | if: github.event_name == 'release' 115 | 116 | steps: 117 | - name: Download binaries 118 | uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 119 | with: 120 | pattern: firmware_utils_ghidra_* 121 | path: dist/ 122 | merge-multiple: true 123 | 124 | - name: Upload to Releases 125 | uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # 2.9.0 126 | with: 127 | repo_token: ${{ secrets.GITHUB_TOKEN }} 128 | file: dist/*.zip 129 | tag: ${{ github.ref }} 130 | file_glob: true 131 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | .settings/ 4 | bin/ 5 | build/ 6 | dist/ 7 | .antProperties.xml 8 | .project 9 | .pydevproject 10 | .classpath 11 | gradle.properties 12 | *.dll 13 | *.dylib 14 | *.jar 15 | *.so 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 Alex James . 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Module.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/Module.manifest -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ghidra Firmware Utilities 2 | ========================= 3 | 4 | Various modules for [Ghidra][1] to assist with PC firmware reverse engineering. 5 | This was accepted as a [coreboot project for GSoC 2019][2]. 6 | 7 | ## Features 8 | ### PCI option ROM loader 9 | - Implements a FS loader for PCI option ROMs (handles hybrid ROMs with 10 | multiple images, e.g. legacy x86 + UEFI) 11 | - Loads UEFI executables from PCI option ROMs (including compressed images) 12 | - Defines the entry point function and various header data types for legacy 13 | x86 option ROMs 14 | 15 | ### Firmware image loader 16 | - Implements a FS loader for Flash Map (FMAP) images and Intel Flash 17 | Descriptor (IFD) images (shows flash regions) 18 | - Implements a FS loader for Coreboot Filesystem (CBFS) images (displays 19 | included files and handles compression) 20 | - Implements a FS loader for UEFI firmware volumes and nested firmware 21 | filesystem (FFS) file/FFS section parsing 22 | 23 | ### Terse Executable (TE) loader 24 | - Implements a binary loader for TE binaries (frequently used in UEFI PI) 25 | 26 | ### UEFI helper script 27 | - Includes data type libraries for base UEFI types (taken from EDK2 MdePkg) 28 | - Fixes the signature of the entry point function 29 | - Defines known GUIDs in the binary's .data/.text segments 30 | - Locates and defines global copies of UEFI table pointers (gBS/gRT/gST/etc) 31 | 32 | ## Building & Installation 33 | JDK 11 (or newer) and Ghidra 10.1 (or newer) are required. 34 | 35 | Ghidra's standard Gradle build system is used. Set the `GHIDRA_INSTALL_DIR` 36 | environment variable before building, or set it as a Gradle property (useful 37 | for building in an IDE): 38 | 39 | ### Environment variable 40 | ```bash 41 | $ export GHIDRA_INSTALL_DIR="/path/to/ghidra" 42 | $ ./gradlew 43 | ``` 44 | 45 | ### Gradle property 46 | ```bash 47 | echo GHIDRA_INSTALL_DIR=/path/to/ghidra > gradle.properties 48 | ``` 49 | 50 | The module ZIP will be output to `dist/`. Use **File > Install Extensions** and 51 | select the green plus to browse to the extension. Restart Ghidra when prompted. 52 | 53 | For proper functionality, the plugin should be built with the same JRE used 54 | by your Ghidra installation. If you have multiple Java runtime environments 55 | installed, select the correct JRE by setting the `JAVA_HOME` environment 56 | variable before building. 57 | 58 | ## Usage 59 | ### PCI option ROM loader 60 | Add a PCI option ROM to a Ghidra project. Legacy x86 option ROMs can be 61 | directly loaded for analysis. Ensure that the binary format is set to 62 | **x86 PCI Option ROM**, and import the binary. 63 | 64 | UEFI option ROMs or option ROMs that contain more than one image should be 65 | imported using the filesystem loader. When prompted to select an import mode, 66 | select **File system**. The images contained within the option ROM will be 67 | displayed, and can be imported for analysis. Legacy x86 images will be handled 68 | the x86 PCI Option ROM loader, and UEFI images will be handled by the PE32 69 | loader (compression is supported). Information for each image can be displayed 70 | by selecting **Get Info** in the right-click menu. 71 | 72 | ### Firmware image loader 73 | Add a supported firmware image to a Ghidra project. The firmware image loader 74 | supports Intel images with a Flash Descriptor, coreboot images with a FMAP/CBFS 75 | layout, and UEFI firmware volumes. The **File system** import mode can be used 76 | to view embedded files within the specified firmware image. 77 | 78 | Note that some UEFI firmware images may store nested firmware volumes within 79 | freeform/raw files (or freeform/raw FFS sections). Such files can be imported 80 | as firmware volumes by selecting **Open File System** in the right-click menu 81 | for the specified freeform/raw file. If no nested firmware volume is found, an 82 | error message will be displayed (`No file system provider for...`). 83 | 84 | ### UEFI helper script 85 | The helper script is included in the plugin's ghidra_scripts directory, which 86 | should be automatically added to the list of script directories in Ghidra. 87 | 88 | Run the UEFI helper script by selecting UEFIHelper.java in the Script Manager 89 | window (accessed from **Window -> Script Manager**). 90 | 91 | To modify the UEFI data type library, modify the PRF template in 92 | `data/gen_prf.sh` as necessary and generate new PRF files. Open the generated 93 | PRF file in **File -> Parse C Source**. Build the updated data type library 94 | by selecting **Parse to File...**. Overwrite the original data type libraries 95 | in `data` and rebuild the plugin. 96 | 97 | ### Related projects 98 | These are some interesting projects related to UEFI reversing: 99 | - [efiXplorer][3] - IDA plugin for UEFI firmware analysis and reverse 100 | engineering automation 101 | - [Ghidra-EFI-Byte-Code-Processor][4] - EFI Byte Code (EBC) processor module 102 | for Ghidra 103 | 104 | ## License 105 | Apache 2.0, with some exceptions: 106 | - `src/efidecompress/c/efidecompress.c`: BSD 107 | 108 | ## Credits 109 | `src/efidecompress/c/efidecompress.c` is a lightly modified version of 110 | [Decompress.c][5] from uefi-firmware-parser (which itself is derived from 111 | [the original in EDK2 BaseTools][6]). 112 | 113 | `lib/xz-1.8.jar` is taken from the [XZ for Java][7] project. 114 | 115 | The IFD FS loader in `src/main/java/firmware/ifd` used the parser from 116 | [UEFITool][8] as a reference. 117 | 118 | The GUID database in `data/guids.csv` is taken from [UEFITool][9]. 119 | 120 | The UEFI data type libraries in `data/uefi_*.gdt` were generated with 121 | `data/gen_prf.sh`, which is partially based off the UEFI parser definition 122 | from [a Ghidra pull request by wrffrz][10]. These data type libraries use 123 | headers from [EDK2 MdePkg][11]. 124 | 125 | [GhidraVitaLoader by xerpi][12] was used as a reference for some parts of the 126 | UEFI helper script. 127 | 128 | [1]: https://ghidra-sre.org/ 129 | [2]: https://summerofcode.withgoogle.com/projects/#6413737605464064 130 | [3]: https://github.com/binarly-io/efiXplorer 131 | [4]: https://github.com/meromwolff/Ghidra-EFI-Byte-Code-Processor 132 | [5]: https://github.com/theopolis/uefi-firmware-parser/blob/21106baf019db9dcd046a3c01ee7b32212de45a5/uefi_firmware/compression/Tiano/Decompress.c 133 | [6]: https://github.com/tianocore/edk2/blob/2e351cbe8e190271b3716284fc1076551d005472/BaseTools/Source/C/Common/Decompress.c 134 | [7]: https://tukaani.org/xz/java.html 135 | [8]: https://github.com/LongSoft/UEFITool 136 | [9]: https://github.com/LongSoft/UEFITool/blob/f863caac9df1c5258e9bcc0441a695b6a3bbaf7c/common/guids.csv 137 | [10]: https://github.com/NationalSecurityAgency/ghidra/pull/501#issuecomment-498374810 138 | [11]: https://github.com/tianocore/edk2/tree/d21e5dbbbf11589113d39619b3e01eb1e8966819/MdePkg/Include 139 | [12]: https://github.com/xerpi/GhidraVitaLoader 140 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Builds a Ghidra Extension for a given Ghidra installation. 2 | // 3 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 4 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 5 | // 6 | // > export GHIDRA_INSTALL_DIR= 7 | // > gradle 8 | // 9 | // or 10 | // 11 | // > gradle -PGHIDRA_INSTALL_DIR= 12 | // 13 | // Gradle should be invoked from the directory of the project to build. Please see the 14 | // application.gradle.version property in /Ghidra/application.properties 15 | // for the correction version of Gradle to use for the Ghidra installation you specify. 16 | 17 | import org.apache.tools.ant.taskdefs.condition.Os 18 | 19 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 20 | def ghidraInstallDir 21 | 22 | if (System.env.GHIDRA_INSTALL_DIR) { 23 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 24 | } 25 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 26 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 27 | } 28 | 29 | if (ghidraInstallDir) { 30 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 31 | } 32 | else { 33 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 34 | } 35 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 36 | 37 | apply plugin: "c" 38 | model { 39 | platforms { 40 | default64 { 41 | if (operatingSystem.Windows && architecture.i386 && 42 | System.properties['sun.arch.data.model'] == 64) { 43 | architecture "x64" 44 | } 45 | } 46 | } 47 | 48 | components { 49 | efidecompress(NativeLibrarySpec) { 50 | targetPlatform "default64" 51 | sources { 52 | c { 53 | source { 54 | srcDir "src/efidecompress/c" 55 | include "efidecompress.c" 56 | } 57 | } 58 | } 59 | 60 | binaries.all { 61 | cCompiler.args "-DCONFIG_JNI" 62 | if (targetPlatform.operatingSystem.macOsX) { 63 | cCompiler.args "-I", "${System.properties['java.home']}/include" 64 | cCompiler.args "-I", "${System.properties['java.home']}/include/darwin" 65 | cCompiler.args "-mmacosx-version-min=10.9" 66 | linker.args "-mmacosx-version-min=10.9" 67 | } else if (targetPlatform.operatingSystem.linux) { 68 | cCompiler.args "-I", "${System.properties['java.home']}/include" 69 | cCompiler.args "-I", "${System.properties['java.home']}/include/linux" 70 | cCompiler.args "-D_FILE_OFFSET_BITS=64" 71 | } else if (targetPlatform.operatingSystem.windows) { 72 | cCompiler.args "-I${System.properties['java.home']}\\include" 73 | cCompiler.args "-I${System.properties['java.home']}\\include\\win32" 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | task copyLibraries(type: Copy, dependsOn: "efidecompressSharedLibrary") { 81 | if (Os.isFamily(Os.FAMILY_MAC)) { 82 | from "$buildDir/libs/efidecompress/shared/libefidecompress.dylib" into "os/mac_x86_64" 83 | } else if (Os.isFamily(Os.FAMILY_UNIX)) { 84 | from "$buildDir/libs/efidecompress/shared/libefidecompress.so" into "os/linux_x86_64" 85 | } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { 86 | from "$buildDir/libs/efidecompress/shared/efidecompress.dll" into "os/win_x86_64" 87 | } 88 | } 89 | 90 | buildExtension.dependsOn "copyLibraries" 91 | 92 | task cleanLibraries(type: Delete) { 93 | delete fileTree("lib").matching { 94 | include "*.jar" 95 | } 96 | 97 | delete fileTree("os").matching { 98 | include "mac_x86_64/*.dylib" 99 | include "linux_x86_64/*.so" 100 | include "win_x86_64/*.dll" 101 | } 102 | } 103 | 104 | clean.dependsOn "cleanLibraries" 105 | -------------------------------------------------------------------------------- /data/README.txt: -------------------------------------------------------------------------------- 1 | The "data" directory is intended to hold data files that will be used by this module and will 2 | not end up in the .jar file, but will be present in the zip or tar file. Typically, data 3 | files are placed here rather than in the resources directory if the user may need to edit them. 4 | 5 | An optional data/languages directory can exist for the purpose of containing various Sleigh language 6 | specification files and importer opinion files. 7 | 8 | The data/build.xml is used for building the contents of the data/languages directory. 9 | 10 | The skel language definition has been commented-out within the skel.ldefs file so that the 11 | skeleton language does not show-up within Ghidra. 12 | 13 | See the Sleigh language documentation (docs/languages/sleigh.htm or sleigh.pdf) for details 14 | on Sleigh language specification syntax. 15 | 16 | -------------------------------------------------------------------------------- /data/gen_prf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z "$EDK2_PATH" ]; then 4 | echo "EDK2_PATH is not set" 5 | exit 1 6 | fi 7 | 8 | if [ -z "$ARCH" ]; then 9 | echo "ARCH is not set" 10 | exit 1 11 | fi 12 | 13 | cat << EOF > "uefi_$(echo $ARCH | tr '[:upper:]' '[:lower:]').prf" 14 | Uefi/UefiBaseType.h 15 | Uefi/UefiSpec.h 16 | PiDxe.h 17 | PiMm.h 18 | PiPei.h 19 | PiSmm.h 20 | Library/DxeCoreEntryPoint.h 21 | Library/PeiCoreEntryPoint.h 22 | Library/PeimEntryPoint.h 23 | Library/StandaloneMmDriverEntryPoint.h 24 | Library/UefiApplicationEntryPoint.h 25 | Library/UefiDriverEntryPoint.h 26 | $(find "$EDK2_PATH/MdePkg/Include/Pi" -type f) 27 | $(find "$EDK2_PATH/MdePkg/Include/Ppi" -type f) 28 | $(find "$EDK2_PATH/MdePkg/Include/Protocol" -type f) 29 | $(find "$EDK2_PATH/MdePkg/Include/IndustryStandard" -type f) 30 | 31 | -I"$EDK2_PATH/MdePkg/Include/$ARCH" 32 | -I"$EDK2_PATH/MdePkg/Include" 33 | EOF 34 | -------------------------------------------------------------------------------- /data/uefi_aarch64.gdt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/data/uefi_aarch64.gdt -------------------------------------------------------------------------------- /data/uefi_arm.gdt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/data/uefi_arm.gdt -------------------------------------------------------------------------------- /data/uefi_ia32.gdt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/data/uefi_ia32.gdt -------------------------------------------------------------------------------- /data/uefi_x64.gdt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/data/uefi_x64.gdt -------------------------------------------------------------------------------- /extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=Ghidra firmware utilities 3 | author=Alex James 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /ghidra_scripts/README.txt: -------------------------------------------------------------------------------- 1 | Java source directory to hold module-specific Ghidra scripts. 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /lib/README.txt: -------------------------------------------------------------------------------- 1 | The "lib" directory is intended to hold Jar files which this module 2 | is dependent upon. This directory may be eliminated from a specific 3 | module if no other Jar files are needed. 4 | -------------------------------------------------------------------------------- /os/linux_x86_64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/linux_x86_64" directory is intended to hold Linux native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /os/mac_x86_64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/mac_x86_64" directory is intended to hold macOS (OS X) native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /os/win_x86_64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/win_x86_64" directory is intended to hold MS Windows native binaries (.exe) 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /src/efidecompress/c/efidecompress.h: -------------------------------------------------------------------------------- 1 | /** @file 2 | Processor or Compiler specific defines for all supported processors. 3 | 4 | This file is stand alone self consistent set of definitions. 5 | 6 | Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
7 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 8 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 9 | 10 | **/ 11 | 12 | #ifndef EFIDECOMPRESS_H 13 | #define EFIDECOMPRESS_H 14 | 15 | #include 16 | 17 | // 18 | // EFI Error Codes common to all execution phases 19 | // 20 | 21 | typedef uint64_t RETURN_STATUS; 22 | 23 | /// 24 | /// Set the upper bit to indicate EFI Error. 25 | /// 26 | #define ENCODE_ERROR(a) ((RETURN_STATUS) (0x8000000000000000LL | (a))) 27 | 28 | #define RETURN_SUCCESS 0 29 | #define RETURN_LOAD_ERROR ENCODE_ERROR (1) 30 | #define RETURN_INVALID_PARAMETER ENCODE_ERROR (2) 31 | #define RETURN_UNSUPPORTED ENCODE_ERROR (3) 32 | #define RETURN_BAD_BUFFER_SIZE ENCODE_ERROR (4) 33 | #define RETURN_BUFFER_TOO_SMALL ENCODE_ERROR (5) 34 | #define RETURN_NOT_READY ENCODE_ERROR (6) 35 | #define RETURN_DEVICE_ERROR ENCODE_ERROR (7) 36 | #define RETURN_WRITE_PROTECTED ENCODE_ERROR (8) 37 | #define RETURN_OUT_OF_RESOURCES ENCODE_ERROR (9) 38 | #define RETURN_VOLUME_CORRUPTED ENCODE_ERROR (10) 39 | #define RETURN_VOLUME_FULL ENCODE_ERROR (11) 40 | #define RETURN_NO_MEDIA ENCODE_ERROR (12) 41 | #define RETURN_MEDIA_CHANGED ENCODE_ERROR (13) 42 | #define RETURN_NOT_FOUND ENCODE_ERROR (14) 43 | #define RETURN_ACCESS_DENIED ENCODE_ERROR (15) 44 | #define RETURN_NO_RESPONSE ENCODE_ERROR (16) 45 | #define RETURN_NO_MAPPING ENCODE_ERROR (17) 46 | #define RETURN_TIMEOUT ENCODE_ERROR (18) 47 | #define RETURN_NOT_STARTED ENCODE_ERROR (19) 48 | #define RETURN_ALREADY_STARTED ENCODE_ERROR (20) 49 | #define RETURN_ABORTED ENCODE_ERROR (21) 50 | #define RETURN_ICMP_ERROR ENCODE_ERROR (22) 51 | #define RETURN_TFTP_ERROR ENCODE_ERROR (23) 52 | #define RETURN_PROTOCOL_ERROR ENCODE_ERROR (24) 53 | #define RETURN_INCOMPATIBLE_VERSION ENCODE_ERROR (25) 54 | #define RETURN_SECURITY_VIOLATION ENCODE_ERROR (26) 55 | #define RETURN_CRC_ERROR ENCODE_ERROR (27) 56 | #define RETURN_END_OF_MEDIA ENCODE_ERROR (28) 57 | #define RETURN_END_OF_FILE ENCODE_ERROR (31) 58 | 59 | #endif // !EFIDECOMPRESS_H 60 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSCompressionAttribute.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.app.util.bin.ByteArrayProvider; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * Parser for CBFS file compression attributes, which are CBFS file attributes with the compression 26 | * tag. The structure is as follows: 27 | * 28 | *
29 |  *   CBFS File Compression Attribute
30 |  *   +------+------+------------------------------------------------+
31 |  *   | Type | Size | Description                                    |
32 |  *   +------+------+------------------------------------------------+
33 |  *   | u32  |    4 | Attribute Tag (0x42435A4C, big endian)         |
34 |  *   | u32  |    4 | Attribute Size (including Tag and Size fields) |
35 |  *   | u32  |    4 | Compression Type                               |
36 |  *   | u32  |    4 | Uncompressed Size                              |
37 |  *   +------+------+------------------------------------------------+
38 |  * 
39 | * 40 | * See CBFSConstants.CompressionAlgorithm for possible Compression Type values. 41 | */ 42 | public class CBFSCompressionAttribute extends CBFSFileAttribute { 43 | // Original header fields 44 | private final int compressionType; 45 | private final long uncompressedSize; 46 | 47 | /** 48 | * Constructs a CBFSCompressionAttribute from a specified BinaryReader. 49 | * 50 | * @param reader the specified BinaryReader 51 | */ 52 | public CBFSCompressionAttribute(BinaryReader reader) throws IOException { 53 | super(reader); 54 | if (getTag() != CBFSConstants.AttributeTag.COMPRESSION) { 55 | throw new IOException("Attribute tag mismatch: expected compression (0x42435A4C)"); 56 | } 57 | 58 | ByteArrayProvider provider = new ByteArrayProvider(super.getData()); 59 | BinaryReader dataReader = new BinaryReader(provider, false); 60 | compressionType = dataReader.readNextInt(); 61 | uncompressedSize = dataReader.readNextUnsignedInt(); 62 | } 63 | 64 | /** 65 | * Returns the compression algorithm used by the CBFS file. 66 | * 67 | * @return the compression algorithm used by the CBFS file 68 | */ 69 | public int getCompressionType() { 70 | return compressionType; 71 | } 72 | 73 | /** 74 | * Returns the uncompressed size of the CBFS file. 75 | * 76 | * @return the uncompressed size of the CBFS file 77 | */ 78 | public long getUncompressedLength() { 79 | return uncompressedSize; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSConstants.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import java.nio.charset.StandardCharsets; 20 | 21 | /* 22 | * Various coreboot File System (CBFS) constants. 23 | */ 24 | public final class CBFSConstants { 25 | // CBFS header signature 26 | public static final String CBFS_HEADER_SIGNATURE = "ORBC"; 27 | 28 | // CBFS file signature 29 | public static final byte[] CBFS_FILE_SIGNATURE = "LARCHIVE".getBytes(StandardCharsets.US_ASCII); 30 | 31 | 32 | // Minimum size of the CBFS file structure 33 | public static final int CBFS_FILE_SIZE = 24; 34 | 35 | // Minimum size of the CBFS file attributes structure 36 | public static final int CBFS_FILE_ATTRIBUTES_SIZE = 8; 37 | 38 | // CBFS file types 39 | public static final class FileType { 40 | public static final int BOOT_BLOCK = 0x01; 41 | public static final int CBFS_HEADER = 0x02; 42 | public static final int STAGE = 0x10; 43 | public static final int PAYLOAD = 0x20; 44 | public static final int FIT = 0x21; 45 | public static final int OPTION_ROM = 0x30; 46 | public static final int BOOT_SPLASH = 0x40; 47 | public static final int RAW = 0x50; 48 | public static final int VSA = 0x51; 49 | public static final int MBI = 0x52; 50 | public static final int MICROCODE = 0x53; 51 | public static final int FSP = 0x60; 52 | public static final int MRC = 0x61; 53 | public static final int MMA = 0x62; 54 | public static final int EFI = 0x63; 55 | public static final int STRUCT = 0x70; 56 | public static final int CMOS_DEFAULT = 0xAA; 57 | public static final int SPD = 0xAB; 58 | public static final int MRC_CACHE = 0xAC; 59 | public static final int CMOS_LAYOUT = 0x01AA; 60 | public static final int NULL = 0xFFFFFFFF; 61 | 62 | public static String toString(int type) { 63 | switch (type) { 64 | case BOOT_BLOCK: 65 | return "Boot Block"; 66 | case CBFS_HEADER: 67 | return "CBFS Header"; 68 | case STAGE: 69 | return "Stage"; 70 | case PAYLOAD: 71 | return "Payload"; 72 | case FIT: 73 | return "Firmware Interface Table"; 74 | case OPTION_ROM: 75 | return "Option ROM"; 76 | case BOOT_SPLASH: 77 | return "Boot Splash Image"; 78 | case RAW: 79 | return "Raw"; 80 | case VSA: 81 | return "VSA"; 82 | case MBI: 83 | return "MBI"; 84 | case MICROCODE: 85 | return "CPU Microcode"; 86 | case FSP: 87 | return "Intel FSP"; 88 | case MRC: 89 | return "Intel MRC"; 90 | case MMA: 91 | return "MMA"; 92 | case EFI: 93 | return "EFI"; 94 | case STRUCT: 95 | return "Struct"; 96 | case CMOS_DEFAULT: 97 | return "CMOS Defaults"; 98 | case SPD: 99 | return "Memory SPD"; 100 | case MRC_CACHE: 101 | return "MRC Cache"; 102 | case CMOS_LAYOUT: 103 | return "CMOS Layout"; 104 | case NULL: 105 | return "Null (Padding)"; 106 | default: 107 | return "Unknown"; 108 | } 109 | } 110 | } 111 | 112 | // CBFS file attribute tags 113 | public static final class AttributeTag { 114 | public static final int UNUSED = 0; 115 | public static final int UNUSED_2 = 0xFFFFFFFF; 116 | public static final int COMPRESSION = 0x42435A4C; 117 | public static final int HASH = 0x68736148; 118 | public static final int POSITION = 0x42435350; 119 | public static final int ALIGNMENT = 0x42434C41; 120 | public static final int PADDING = 0x47444150; 121 | } 122 | 123 | // CBFS compression algorithms 124 | public static final class CompressionAlgorithm { 125 | public static final int NONE = 0; 126 | public static final int LZMA = 1; 127 | public static final int LZ4 = 2; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSFile.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import java.io.ByteArrayInputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.util.Arrays; 23 | 24 | import ghidra.app.util.bin.ByteArrayProvider; 25 | import ghidra.app.util.bin.ByteProvider; 26 | import ghidra.formats.gfilesystem.FSUtilities; 27 | import ghidra.formats.gfilesystem.FileCache; 28 | import ghidra.formats.gfilesystem.FileSystemService; 29 | import ghidra.formats.gfilesystem.fileinfo.FileAttributeType; 30 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 31 | import ghidra.util.exception.CancelledException; 32 | import ghidra.util.task.TaskMonitor; 33 | import org.apache.commons.compress.compressors.lz4.FramedLZ4CompressorInputStream; 34 | import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream; 35 | 36 | import ghidra.app.util.bin.BinaryReader; 37 | 38 | /** 39 | * Parser for CBFS files, which have the following structure: 40 | * 41 | * CBFS File Header 42 | * +---------+--------------------------------+ 43 | * | Type | Size | Description | 44 | * +---------+--------------------------------+ 45 | * | char[8] | 8 | Signature ("LARCHIVE") | 46 | * | u32 | 4 | File Size | 47 | * | u32 | 4 | File Type | 48 | * | u32 | 4 | File Attributes Offset | 49 | * | u32 | 4 | Offset of File Contents | 50 | * | char[] | var | File Name (C string) | 51 | * +---------+--------------------------------+ 52 | * 53 | * If nonzero, the File Attributes Offset field points to a CBFS file attribute structure. See 54 | * CBFSFileAttribute for details on this structure. 55 | * 56 | * The File Name field is a null-terminated C string. 57 | */ 58 | public class CBFSFile { 59 | // Original header fields 60 | private final byte[] signature; 61 | private final int size; 62 | private final int type; 63 | private final long attributesOffset; 64 | private final long offset; 65 | private final String name; 66 | 67 | private CBFSFileAttribute attribute; 68 | private final byte[] data; 69 | 70 | /** 71 | * Constructs a CBFSFile from a specified BinaryReader. 72 | * 73 | * @param reader the specified BinaryReader 74 | */ 75 | public CBFSFile(BinaryReader reader) throws IOException { 76 | long startIndex = reader.getPointerIndex(); 77 | 78 | signature = reader.readNextByteArray(CBFSConstants.CBFS_FILE_SIGNATURE.length); 79 | if (!Arrays.equals(CBFSConstants.CBFS_FILE_SIGNATURE, signature)) { 80 | throw new IOException("Not a valid CBFS file"); 81 | } 82 | 83 | size = reader.readNextInt(); 84 | type = reader.readNextInt(); 85 | attributesOffset = reader.readNextUnsignedInt(); 86 | offset = reader.readNextUnsignedInt(); 87 | name = reader.readNextAsciiString(); 88 | 89 | // The attributes offset should point past the end of the CBFS file structure. 90 | if (attributesOffset != 0 && attributesOffset > CBFSConstants.CBFS_FILE_SIZE) { 91 | // TODO: Handle nested file attributes: additional file attributes may be stored in the 92 | // first attribute's data section 93 | reader.setPointerIndex(startIndex + attributesOffset); 94 | attribute = CBFSFileAttributeFactory.parseCBFSFileAttribute(reader); 95 | } 96 | 97 | reader.setPointerIndex(startIndex + offset); 98 | data = reader.readNextByteArray(size); 99 | } 100 | 101 | /** 102 | * Returns a ByteProvider for the contents of the current file. Compressed files will be 103 | * wrapped in a CompressorInputStream for transparent extraction. 104 | * 105 | * @return a ByteProvider for the contents of the current file 106 | */ 107 | public ByteProvider getByteProvider() throws IOException, CancelledException { 108 | // Extract the file if compression is used (specified in a compression attribute). 109 | if (attribute != null && attribute instanceof CBFSCompressionAttribute) { 110 | InputStream is = new ByteArrayInputStream(data); 111 | int compressionType = ((CBFSCompressionAttribute) attribute).getCompressionType(); 112 | switch (compressionType) { 113 | case CBFSConstants.CompressionAlgorithm.NONE: break; 114 | case CBFSConstants.CompressionAlgorithm.LZMA: is = new LZMACompressorInputStream(is); break; 115 | case CBFSConstants.CompressionAlgorithm.LZ4: is = new FramedLZ4CompressorInputStream(is); break; 116 | default: throw new IOException("Unsupported CBFS compression type: " + compressionType); 117 | } 118 | 119 | FileCache.FileCacheEntryBuilder tmpFileBuilder = FileSystemService.getInstance().createTempFile(-1); 120 | FSUtilities.streamCopy(is, tmpFileBuilder, TaskMonitor.DUMMY); 121 | FileCache.FileCacheEntry fce = tmpFileBuilder.finish(); 122 | return FileSystemService.getInstance().getNamedTempFile(fce, name); 123 | } else { 124 | return new ByteArrayProvider(data); 125 | } 126 | } 127 | 128 | /** 129 | * Returns the name of the current file. 130 | * 131 | * @return the name of the current file 132 | */ 133 | public String getName() { 134 | return name; 135 | } 136 | 137 | /** 138 | * Returns the offset of the current file's contents. 139 | * 140 | * @return the offset of the current file's contents. 141 | */ 142 | public long getOffset() { 143 | return offset; 144 | } 145 | 146 | /** 147 | * Returns the current file's type. 148 | * 149 | * @return the current file's type 150 | */ 151 | public int getType() { 152 | return type; 153 | } 154 | 155 | /** 156 | * Returns the size of the current file. 157 | * 158 | * @return the size of the current file 159 | */ 160 | public int length() { 161 | return size; 162 | } 163 | 164 | /** 165 | * Returns FileAttributes for the current file. 166 | * 167 | * @return FileAttributes for the current file 168 | */ 169 | public FileAttributes getFileAttributes() { 170 | FileAttributes attributes = new FileAttributes(); 171 | attributes.add(FileAttributeType.NAME_ATTR, name); 172 | attributes.add("File Type", CBFSConstants.FileType.toString(type)); 173 | if (attribute != null && attribute instanceof CBFSCompressionAttribute) { 174 | switch (((CBFSCompressionAttribute) attribute).getCompressionType()) { 175 | case CBFSConstants.CompressionAlgorithm.LZMA: 176 | attributes.add("Compression Type", "LZMA"); 177 | case CBFSConstants.CompressionAlgorithm.LZ4: 178 | attributes.add("Compression Type", "LZ4"); 179 | attributes.add(FileAttributeType.COMPRESSED_SIZE_ATTR, Long.valueOf(size)); 180 | break; 181 | default: 182 | attributes.add(FileAttributeType.SIZE_ATTR, Long.valueOf(size)); 183 | } 184 | } else { 185 | attributes.add(FileAttributeType.SIZE_ATTR, Long.valueOf(size)); 186 | } 187 | 188 | return attributes; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSFileAttribute.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * Parser for CBFS file attributes, which have the following structure: 25 | * 26 | *
27 |  *   CBFS File Attribute
28 |  *   +------+------+------------------------------------------------+
29 |  *   | Type | Size | Description                                    |
30 |  *   +------+------+------------------------------------------------+
31 |  *   | u32  |    4 | Attribute Tag                                  |
32 |  *   | u32  |    4 | Attribute Size (including Tag and Size fields) |
33 |  *   | u8[] |  var | Attribute Data (depends on Tag)                |
34 |  *   +------+------+------------------------------------------------+
35 |  * 
36 | * 37 | * The Attribute Tag field is used to determine the attribute type; the contents of the Attribute 38 | * Data field are dependent on the attribute type. See CBFSConstants.AttributeTag for possible code 39 | * type values. 40 | * 41 | * This parser does not support nested file attributes, which cbfstool does support. 42 | */ 43 | public class CBFSFileAttribute { 44 | // Original header fields 45 | private final int tag; 46 | private final int size; 47 | private final byte[] data; 48 | 49 | /** 50 | * Constructs a FlashMapArea from a specified BinaryReader. 51 | * 52 | * @param reader the specified BinaryReader 53 | */ 54 | public CBFSFileAttribute(BinaryReader reader) throws IOException { 55 | tag = reader.readNextInt(); 56 | // This is the size of the whole attribute structure, including the tag and length fields. 57 | size = reader.readNextInt(); 58 | data = reader.readNextByteArray(size - 8); 59 | } 60 | 61 | public int getTag() { 62 | return tag; 63 | } 64 | 65 | public byte[] getData() { 66 | return data; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSFileAttributeFactory.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.util.Msg; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * Factory for constructing the correct CBFS file attribute based off the attribute tag field. 26 | */ 27 | public class CBFSFileAttributeFactory { 28 | private CBFSFileAttributeFactory() {} 29 | 30 | /** 31 | * Constructs a CBFSFileAttribute from a specified BinaryReader by checking the attribute tag 32 | * field. 33 | * 34 | * @param reader the specified BinaryReader 35 | * @return the parsed CBFSFileAttribute 36 | */ 37 | public static CBFSFileAttribute parseCBFSFileAttribute(BinaryReader reader) throws IOException { 38 | int tag = reader.peekNextInt(); 39 | Msg.debug(CBFSFileAttribute.class, String.format("Found attribute with tag 0x%X", tag)); 40 | 41 | switch (tag) { 42 | case CBFSConstants.AttributeTag.COMPRESSION: 43 | return new CBFSCompressionAttribute(reader); 44 | default: 45 | return new CBFSFileAttribute(reader); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSFileSystem.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | 22 | import ghidra.app.util.bin.BinaryReader; 23 | import ghidra.app.util.bin.ByteProvider; 24 | import ghidra.app.util.bin.ByteProviderWrapper; 25 | import ghidra.formats.gfilesystem.*; 26 | import ghidra.formats.gfilesystem.annotations.FileSystemInfo; 27 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 28 | import ghidra.util.Msg; 29 | import ghidra.util.exception.CancelledException; 30 | import ghidra.util.task.TaskMonitor; 31 | 32 | @FileSystemInfo(type = "cbfs", description = "Coreboot File System", factory = CBFSFileSystemFactory.class) 33 | public class CBFSFileSystem implements GFileSystem { 34 | private final FSRLRoot fsFSRL; 35 | private final FileSystemIndexHelper fsih; 36 | private final FileSystemRefManager refManager = new FileSystemRefManager(this); 37 | private ByteProvider provider; 38 | 39 | public CBFSFileSystem(FSRLRoot fsFSRL) { 40 | this.fsFSRL = fsFSRL; 41 | this.fsih = new FileSystemIndexHelper<>(this, fsFSRL); 42 | } 43 | 44 | public void mount(ByteProvider provider, TaskMonitor monitor) throws IOException, CancelledException { 45 | this.provider = provider; 46 | BinaryReader reader = new BinaryReader(provider, false); 47 | 48 | // The first CBFS file should contain the CBFS master header. 49 | CBFSHeader header = new CBFSHeader(reader); 50 | Msg.debug(this, String.format("%s alignment = 0x%X", header.getName(), header.getAlignment())); 51 | reader.align(header.getAlignment()); 52 | 53 | monitor.initialize(reader.length()); 54 | monitor.setMessage("Mounting CBFS"); 55 | // Read each CBFS file. 56 | while (reader.length() - reader.getPointerIndex() > 0) { 57 | monitor.checkCanceled(); 58 | monitor.setProgress(reader.getPointerIndex()); 59 | 60 | CBFSFile cbfsFile = new CBFSFile(reader); 61 | reader.align(header.getAlignment()); 62 | String name = cbfsFile.getName().length() > 0 ? cbfsFile.getName().replace('/', '_') : "(empty)"; 63 | Msg.debug(this, String.format("%s size = 0x%X, data offset = 0x%X, type = %s", name, cbfsFile.length(), 64 | cbfsFile.getOffset(), CBFSConstants.FileType.toString(cbfsFile.getType()))); 65 | 66 | // Ignore empty CBFS files (used for padding). 67 | if (cbfsFile.getType() == CBFSConstants.FileType.NULL) { 68 | continue; 69 | } 70 | 71 | fsih.storeFileWithParent(name, null, -1, false, cbfsFile.length(), cbfsFile); 72 | } 73 | } 74 | 75 | @Override 76 | public GFile lookup(String path) { 77 | return fsih.lookup(path); 78 | } 79 | 80 | @Override 81 | public void close() throws IOException { 82 | refManager.onClose(); 83 | if (provider != null) { 84 | provider.close(); 85 | provider = null; 86 | } 87 | 88 | fsih.clear(); 89 | } 90 | 91 | @Override 92 | public boolean isClosed() { 93 | return provider == null; 94 | } 95 | 96 | @Override 97 | public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException, CancelledException { 98 | CBFSFile cbfsFile = fsih.getMetadata(file); 99 | return new ByteProviderWrapper(cbfsFile.getByteProvider(), file.getFSRL()); 100 | } 101 | 102 | @Override 103 | public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) { 104 | CBFSFile cbfsFile = fsih.getMetadata(file); 105 | return cbfsFile.getFileAttributes(); 106 | } 107 | 108 | @Override 109 | public FSRLRoot getFSRL() { 110 | return fsFSRL; 111 | } 112 | 113 | @Override 114 | public List getListing(GFile directory) { 115 | return fsih.getListing(directory); 116 | } 117 | 118 | @Override 119 | public String getName() { 120 | return fsFSRL.getContainer().getName(); 121 | } 122 | 123 | @Override 124 | public FileSystemRefManager getRefManager() { 125 | return refManager; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSFileSystemFactory.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import java.io.IOException; 20 | import java.util.Arrays; 21 | 22 | import ghidra.app.util.bin.ByteProvider; 23 | import ghidra.formats.gfilesystem.FSRL; 24 | import ghidra.formats.gfilesystem.FSRLRoot; 25 | import ghidra.formats.gfilesystem.FileSystemService; 26 | import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; 27 | import ghidra.formats.gfilesystem.factory.GFileSystemProbeBytesOnly; 28 | import ghidra.util.exception.CancelledException; 29 | import ghidra.util.task.TaskMonitor; 30 | 31 | public class CBFSFileSystemFactory implements GFileSystemFactoryByteProvider, 32 | GFileSystemProbeBytesOnly { 33 | @Override 34 | public boolean probeStartBytes(FSRL containerFSRL, byte[] startBytes) { 35 | return Arrays.equals(startBytes, 0, CBFSConstants.CBFS_FILE_SIGNATURE.length, 36 | CBFSConstants.CBFS_FILE_SIGNATURE, 0, CBFSConstants.CBFS_FILE_SIGNATURE.length); 37 | } 38 | 39 | @Override 40 | public int getBytesRequired() { 41 | return CBFSConstants.CBFS_FILE_SIGNATURE.length; 42 | } 43 | 44 | @Override 45 | public CBFSFileSystem create(FSRLRoot fsrlRoot, ByteProvider byteProvider, FileSystemService fsService, 46 | TaskMonitor monitor) throws IOException, CancelledException { 47 | CBFSFileSystem fs = new CBFSFileSystem(fsrlRoot); 48 | try { 49 | fs.mount(byteProvider, monitor); 50 | return fs; 51 | } catch (IOException ioe) { 52 | fs.close(); 53 | throw ioe; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/firmware/cbfs/CBFSHeader.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.cbfs; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.util.exception.CancelledException; 21 | 22 | import java.io.IOException; 23 | 24 | /** 25 | * Parser for the CBFS master header, which has the following structure. 26 | * 27 | *
 28 |  *   CBFS Master Header
 29 |  *   +----------+------+--------------------------------+
 30 |  *   | Type    | Size | Description                     |
 31 |  *   +---------+------+---------------------------------+
 32 |  *   | char[4] |    4 | Signature ("ORBC")              |
 33 |  *   | u32     |    4 | Version                         |
 34 |  *   | u32     |    4 | ROM Size                        |
 35 |  *   | u32     |    4 | Boot Block Size                 |
 36 |  *   | u32     |    4 | CBFS File Alignment             |
 37 |  *   | u32     |    4 | CBFS Offset (from start of ROM) |
 38 |  *   | u32     |    4 | Architecture                    |
 39 |  *   +---------+------+---------------------------------+
 40 |  * 
41 | * 42 | * There are four bytes of padding after the end of the CBFS master header. 43 | */ 44 | public class CBFSHeader extends CBFSFile { 45 | // Original header fields 46 | private final String signature; 47 | private final long version; 48 | private final long romSize; 49 | private final long bootBlockSize; 50 | private final int alignment; 51 | private final long offset; 52 | private final int architecture; 53 | 54 | /** 55 | * Constructs a CBFSHeader from a specified BinaryReader. 56 | * 57 | * @param reader the specified BinaryReader 58 | */ 59 | public CBFSHeader(BinaryReader reader) throws IOException, CancelledException { 60 | super(reader); 61 | if (getType() != CBFSConstants.FileType.CBFS_HEADER) { 62 | throw new IOException("Not a valid CBFS header"); 63 | } 64 | 65 | BinaryReader headerReader = new BinaryReader(getByteProvider(), false); 66 | 67 | signature = headerReader.readNextAsciiString(CBFSConstants.CBFS_HEADER_SIGNATURE.length()); 68 | if (!signature.equals(CBFSConstants.CBFS_HEADER_SIGNATURE)) { 69 | throw new IOException("Not a valid CBFS header"); 70 | } 71 | 72 | version = headerReader.readNextUnsignedInt(); 73 | romSize = headerReader.readNextUnsignedInt(); 74 | bootBlockSize = headerReader.readNextUnsignedInt(); 75 | alignment = headerReader.readNextInt(); 76 | offset = headerReader.readNextUnsignedInt(); 77 | architecture = headerReader.readNextInt(); 78 | } 79 | 80 | /** 81 | * Returns the CBFS file alignment in the current CBFS master header. 82 | * 83 | * @return the CBFS file alignment in the current CBFS master header 84 | */ 85 | public int getAlignment() { 86 | return alignment; 87 | } 88 | 89 | /** 90 | * Returns the size of the boot block. 91 | * 92 | * @return the size of the boot block 93 | */ 94 | public long getBootBlockSize() { 95 | return bootBlockSize; 96 | } 97 | 98 | /** 99 | * Returns the offset of the first CBFS file. 100 | * 101 | * @return the offset of the first CBFS file 102 | */ 103 | public long getOffset() { 104 | return offset; 105 | } 106 | 107 | /** 108 | * Returns the size of the ROM. 109 | * 110 | * @return the size of the ROM 111 | */ 112 | public long getROMSize() { 113 | return romSize; 114 | } 115 | 116 | /** 117 | * Returns the version of the current CBFS master header. 118 | * 119 | * @return the version of the current CBFS master header 120 | */ 121 | public long getVersion() { 122 | return version; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/firmware/common/EFIDecompressor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3xtjames/ghidra-firmware-utils/2e2af1eecac5ad8e8ee3fdb5e2be827a6cb24722/src/main/java/firmware/common/EFIDecompressor.class -------------------------------------------------------------------------------- /src/main/java/firmware/common/EFIDecompressor.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.common; 18 | 19 | import ghidra.util.Msg; 20 | import util.JNILibraryLoader; 21 | 22 | /** 23 | * Handles the decompression of images compressed with the EFI Compression Algorithm. 24 | */ 25 | public class EFIDecompressor { 26 | static { 27 | try { 28 | JNILibraryLoader.loadLibrary("efidecompress"); 29 | } catch (Throwable t) { 30 | Msg.showError(EFIDecompressor.class, null, "EFI Decompressor", 31 | "Failed to load libefidecompress native library"); 32 | Msg.error(EFIDecompressor.class, t); 33 | } 34 | } 35 | 36 | private EFIDecompressor() {} 37 | 38 | /** 39 | * Decompresses the specified compressed image. Implemented by the efidecompress native 40 | * library. 41 | * 42 | * @param compressedImage the compressed image 43 | */ 44 | private static native byte[] nativeDecompress(byte[] compressedImage); 45 | 46 | /** 47 | * Decompress the specified compressed image. 48 | * 49 | * @param compressedImage the compressed image 50 | */ 51 | public static byte[] decompress(byte[] compressedImage) { 52 | return nativeDecompress(compressedImage); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/firmware/common/FileAttributeUtils.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.common; 18 | 19 | import ghidra.formats.gfilesystem.fileinfo.FileAttribute; 20 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 21 | 22 | /** 23 | * Various FileAttributes utilities. 24 | */ 25 | public class FileAttributeUtils { 26 | private FileAttributeUtils() {} 27 | 28 | /** 29 | * Adds all FileAttributes from src to dest. 30 | * 31 | * @param dest the destination FileAttributes object 32 | * @param src the source FileAttributes object 33 | */ 34 | public static void addAll(FileAttributes dest, FileAttributes src) { 35 | for (FileAttribute attribute : src.getAttributes()) { 36 | dest.add(attribute.getAttributeType(), attribute.getAttributeDisplayName(), 37 | attribute.getAttributeValue()); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/firmware/common/TianoDecompressor.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.common; 18 | 19 | import ghidra.util.Msg; 20 | import util.JNILibraryLoader; 21 | 22 | /** 23 | * Handles the decompression of images compressed with the Tiano Compression Algorithm. 24 | */ 25 | public class TianoDecompressor { 26 | static { 27 | try { 28 | JNILibraryLoader.loadLibrary("efidecompress"); 29 | } catch (Throwable t) { 30 | Msg.showError(TianoDecompressor.class, null, "Tiano Decompressor", 31 | "Failed to load libefidecompress native library"); 32 | } 33 | } 34 | 35 | private TianoDecompressor() {} 36 | 37 | /** 38 | * Decompresses the specified compressed image. Implemented by the efidecompress native 39 | * library. 40 | * 41 | * @param compressedImage the compressed image 42 | */ 43 | private static native byte[] nativeDecompress(byte[] compressedImage); 44 | 45 | /** 46 | * Decompress the specified compressed image. 47 | * 48 | * @param compressedImage the compressed image 49 | */ 50 | public static byte[] decompress(byte[] compressedImage) { 51 | return nativeDecompress(compressedImage); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/firmware/common/UUIDUtils.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.common; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.framework.Application; 21 | import ghidra.util.Msg; 22 | 23 | import java.io.BufferedReader; 24 | import java.io.File; 25 | import java.io.FileReader; 26 | import java.io.IOException; 27 | import java.util.HashMap; 28 | import java.util.UUID; 29 | 30 | /** 31 | * Various UUID utilities. 32 | */ 33 | public class UUIDUtils { 34 | private static HashMap guidMap = new HashMap<>(); 35 | 36 | static { 37 | try { 38 | // Read the list of GUIDs to populate the GUID database. 39 | File guidDbFile = Application.getModuleDataFile("guids.csv").getFile(true); 40 | try (BufferedReader reader = new BufferedReader(new FileReader(guidDbFile))) { 41 | for (String line; (line = reader.readLine()) != null;) { 42 | String[] parsedLine = line.split(","); 43 | guidMap.put(UUID.fromString(parsedLine[0]), parsedLine[1]); 44 | } 45 | } 46 | 47 | Msg.debug(UUIDUtils.class, "Imported " + guidMap.size() + " GUIDs"); 48 | } catch (IOException e) { 49 | Msg.error(UUIDUtils.class, "Failed to import GUID database"); 50 | } 51 | } 52 | 53 | private UUIDUtils() {} 54 | 55 | /** 56 | * Checks if the specified UUID is present in the UUID database. 57 | * 58 | * @param uuid the specified UUID 59 | * @return if a UUID is present in the UUID map 60 | */ 61 | public static boolean dbContains(UUID uuid) { 62 | return guidMap.containsKey(uuid); 63 | } 64 | 65 | /** 66 | * Constructs a UUID from a specified BinaryReader. 67 | * 68 | * @param reader the specified BinaryReader 69 | * @return the constructed UUID 70 | */ 71 | public static UUID fromBinaryReader(BinaryReader reader) throws IOException { 72 | boolean wasReaderLittleEndian = reader.isLittleEndian(); 73 | 74 | reader.setLittleEndian(true); 75 | long guidData1 = reader.readNextUnsignedInt() << 32; 76 | long guidData2 = (reader.readNextUnsignedShort() << 16) & 0xFFFFFFFFL; 77 | long guidData3 = reader.readNextUnsignedShort(); 78 | long uuidMsb = guidData1 | guidData2 | guidData3; 79 | 80 | reader.setLittleEndian(false); 81 | long uuidLsb = reader.readNextLong(); 82 | 83 | reader.setLittleEndian(wasReaderLittleEndian); 84 | return new UUID(uuidMsb, uuidLsb); 85 | } 86 | 87 | /** 88 | * Retrieves the name for a specified UUID. If no name is found, a string representation of the 89 | * UUID is returned. 90 | * 91 | * @return the name for the specified UUID 92 | */ 93 | public static String getName(UUID uuid) { 94 | if (guidMap == null || !guidMap.containsKey(uuid)) { 95 | return uuid.toString(); 96 | } 97 | 98 | return guidMap.get(uuid); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/firmware/fmap/FlashMapArea.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.fmap; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.app.util.bin.ByteProvider; 21 | import ghidra.app.util.bin.ByteProviderWrapper; 22 | import ghidra.formats.gfilesystem.fileinfo.FileAttributeType; 23 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 24 | 25 | import java.io.IOException; 26 | 27 | /** 28 | * Parser for flash areas, which have the following structure: 29 | * 30 | *
 31 |  *   Flash Map Header
 32 |  *   +----------+------+-------------------------------------------------------+
 33 |  *   | Type     | Size | Description                                           |
 34 |  *   +----------+------+-------------------------------------------------------+
 35 |  *   | u32      |    4 | Offset (Relative to Base Address in Flash Map Header) |
 36 |  *   | u32      |    4 | Size of Flash Area                                    |
 37 |  *   | char[32] |   32 | Name of Flash Area                                    |
 38 |  *   | u16      |    2 | Flash Area Flags                                      |
 39 |  *   +----------+------+-------------------------------------------------------+
 40 |  * 
41 | * 42 | * See FlashMapConstants.AreaFlags for possible Flash Area Flags. 43 | */ 44 | public class FlashMapArea { 45 | // Original header fields 46 | private final long offset; 47 | private final int size; 48 | private final String name; 49 | private final short flags; 50 | 51 | private final ByteProvider provider; 52 | 53 | /** 54 | * Constructs a FlashMapArea from a specified BinaryReader. 55 | * 56 | * @param reader the specified BinaryReader 57 | */ 58 | public FlashMapArea(BinaryReader reader) throws IOException { 59 | offset = reader.readNextUnsignedInt(); 60 | size = reader.readNextInt(); 61 | name = reader.readNextAsciiString(FlashMapConstants.FMAP_NAME_LEN).trim(); 62 | flags = reader.readNextShort(); 63 | 64 | provider = new ByteProviderWrapper(reader.getByteProvider(), offset, size); 65 | } 66 | 67 | /** 68 | * Returns a ByteProvider for the contents of the current flash area. 69 | * 70 | * @return a ByteProvider for the contents of the current flash area 71 | */ 72 | public ByteProvider getByteProvider() { 73 | return provider; 74 | } 75 | 76 | /** 77 | * Returns the name of the current flash area. 78 | * 79 | * @return the name of the current flash area 80 | */ 81 | public String getName() { 82 | return name; 83 | } 84 | 85 | /** 86 | * Returns the offset of the current flash area (relative to the base). 87 | * 88 | * @return the offset of the current flash area 89 | */ 90 | public long getOffset() { 91 | return offset; 92 | } 93 | 94 | /** 95 | * Returns the length of the current flash area. 96 | * 97 | * @return the length of the current flash area 98 | */ 99 | public int length() { 100 | return size; 101 | } 102 | 103 | /** 104 | * Returns FileAttributes for the current flash area. 105 | * 106 | * @return FileAttributes for the current flash area 107 | */ 108 | public FileAttributes getFileAttributes() { 109 | FileAttributes attributes = new FileAttributes(); 110 | attributes.add(FileAttributeType.NAME_ATTR, name); 111 | attributes.add(FileAttributeType.SIZE_ATTR, Long.valueOf(size)); 112 | attributes.add("Offset", offset); 113 | attributes.add("Flags", FlashMapConstants.FlashAreaFlags.toString(flags)); 114 | return attributes; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/firmware/fmap/FlashMapConstants.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.fmap; 18 | 19 | import java.util.Formatter; 20 | 21 | /** 22 | * Various Flash Map (FMAP) constants. 23 | */ 24 | public final class FlashMapConstants { 25 | // Flash Map signature 26 | public static final String FMAP_SIGNATURE = "__FMAP__"; 27 | 28 | // Flash Map name length (including null terminator) 29 | public static final int FMAP_NAME_LEN = 32; 30 | 31 | // Flash area flags 32 | public static final class FlashAreaFlags { 33 | public static final byte STATIC = 1 << 0; 34 | public static final byte COMPRESSED = 1 << 1; 35 | public static final byte READ_ONLY = 1 << 2; 36 | public static final byte PRESERVE = 1 << 3; 37 | 38 | public static String toString(short flags) { 39 | Formatter formatter = new Formatter(); 40 | if (flags == 0) { 41 | formatter.format("None "); 42 | } else if ((flags & STATIC) != 0) { 43 | formatter.format("Static "); 44 | } else if ((flags & COMPRESSED) != 0) { 45 | formatter.format("Compressed "); 46 | } else if ((flags & READ_ONLY) != 0) { 47 | formatter.format("Read-only "); 48 | } else if ((flags & PRESERVE) != 0) { 49 | formatter.format("Preserved "); 50 | } 51 | 52 | return formatter.toString(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/firmware/fmap/FlashMapFileSystem.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.fmap; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | 22 | import ghidra.app.util.bin.BinaryReader; 23 | import ghidra.app.util.bin.ByteProvider; 24 | import ghidra.app.util.bin.ByteProviderWrapper; 25 | import ghidra.formats.gfilesystem.*; 26 | import ghidra.formats.gfilesystem.annotations.FileSystemInfo; 27 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 28 | import ghidra.util.task.TaskMonitor; 29 | 30 | @FileSystemInfo(type = "fmap", description = "Flash Map", factory = FlashMapFileSystemFactory.class) 31 | public class FlashMapFileSystem implements GFileSystem { 32 | private final FSRLRoot fsFSRL; 33 | private final FileSystemIndexHelper fsih; 34 | private final FileSystemRefManager refManager = new FileSystemRefManager(this); 35 | private ByteProvider provider; 36 | 37 | public FlashMapFileSystem(FSRLRoot fsFSRL) { 38 | this.fsFSRL = fsFSRL; 39 | this.fsih = new FileSystemIndexHelper<>(this, fsFSRL); 40 | } 41 | 42 | public void mount(ByteProvider provider, long offset, TaskMonitor monitor) throws IOException { 43 | this.provider = provider; 44 | BinaryReader reader = new BinaryReader(provider, true); 45 | reader.setPointerIndex(offset); 46 | FlashMapHeader header = new FlashMapHeader(reader); 47 | FlashMapArea[] areas = header.getAreas(); 48 | for (FlashMapArea area : areas) { 49 | fsih.storeFileWithParent(area.getName(), null, -1, false, area.length(), area); 50 | } 51 | } 52 | 53 | @Override 54 | public GFile lookup(String path) { 55 | return fsih.lookup(path); 56 | } 57 | 58 | @Override 59 | public void close() throws IOException { 60 | refManager.onClose(); 61 | if (provider != null) { 62 | provider.close(); 63 | provider = null; 64 | } 65 | 66 | fsih.clear(); 67 | } 68 | 69 | @Override 70 | public boolean isClosed() { 71 | return provider == null; 72 | } 73 | 74 | @Override 75 | public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException { 76 | FlashMapArea area = fsih.getMetadata(file); 77 | return new ByteProviderWrapper(area.getByteProvider(), file.getFSRL()); 78 | } 79 | 80 | @Override 81 | public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) { 82 | FlashMapArea area = fsih.getMetadata(file); 83 | return area.getFileAttributes(); 84 | } 85 | 86 | @Override 87 | public FSRLRoot getFSRL() { 88 | return fsFSRL; 89 | } 90 | 91 | @Override 92 | public List getListing(GFile directory) { 93 | return fsih.getListing(directory); 94 | } 95 | 96 | @Override 97 | public String getName() { 98 | return fsFSRL.getContainer().getName(); 99 | } 100 | 101 | @Override 102 | public FileSystemRefManager getRefManager() { 103 | return refManager; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/firmware/fmap/FlashMapFileSystemFactory.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.fmap; 18 | 19 | import java.io.IOException; 20 | import java.nio.charset.StandardCharsets; 21 | 22 | import ghidra.app.util.bin.ByteProvider; 23 | import ghidra.formats.gfilesystem.FSRLRoot; 24 | import ghidra.formats.gfilesystem.FileSystemService; 25 | import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; 26 | import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider; 27 | import ghidra.util.Msg; 28 | import ghidra.util.task.TaskMonitor; 29 | 30 | public class FlashMapFileSystemFactory implements GFileSystemFactoryByteProvider, 31 | GFileSystemProbeByteProvider { 32 | @Override 33 | public boolean probe(ByteProvider provider, FileSystemService fsService, TaskMonitor monitor) throws IOException { 34 | return findFMapSignatureOffset(provider) != -1; 35 | } 36 | 37 | @Override 38 | public FlashMapFileSystem create(FSRLRoot fsrlRoot, ByteProvider provider, FileSystemService fsService, 39 | TaskMonitor monitor) throws IOException { 40 | FlashMapFileSystem fs = new FlashMapFileSystem(fsrlRoot); 41 | try { 42 | long offset = findFMapSignatureOffset(provider); 43 | if (offset < 0) { 44 | throw new IOException("FMAP signature not found"); 45 | } 46 | 47 | fs.mount(provider, offset, monitor); 48 | return fs; 49 | } catch (IOException ioe) { 50 | fs.close(); 51 | throw ioe; 52 | } 53 | } 54 | 55 | private long findFMapSignatureOffset(ByteProvider provider) throws IOException { 56 | long remainingLength = provider.length(); 57 | long offset = 0; 58 | while (remainingLength >= FlashMapConstants.FMAP_SIGNATURE.length()) { 59 | String signature = new String(provider.readBytes(offset, FlashMapConstants.FMAP_SIGNATURE.length()), 60 | StandardCharsets.US_ASCII); 61 | if (signature.equals(FlashMapConstants.FMAP_SIGNATURE)) { 62 | Msg.debug(this, String.format("Found FMAP signature at 0x%X", offset)); 63 | return offset; 64 | } 65 | 66 | offset += FlashMapConstants.FMAP_SIGNATURE.length(); 67 | remainingLength -= FlashMapConstants.FMAP_SIGNATURE.length(); 68 | } 69 | 70 | return -1; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/firmware/fmap/FlashMapHeader.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.fmap; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | 21 | import java.io.IOException; 22 | 23 | /** 24 | * Parser for Flash Map (FMAP) layouts, as used by Chromium OS devices and coreboot. 25 | * 26 | * The flash layout is described by the flash area structures, which appear directly after the 27 | * flash map header. Note that the flash map header is not required to be at the start of the 28 | * firmware binary. 29 | * 30 | *
 31 |  *   Flash Map Header
 32 |  *   +----------+------+-------------------------+
 33 |  *   | Type     | Size | Description             |
 34 |  *   +----------+------+-------------------------+
 35 |  *   | char[8]  |    8 | Signature ("__FMAP__")  |
 36 |  *   | u8       |    1 | Major Version           |
 37 |  *   | u8       |    1 | Minor Version           |
 38 |  *   | u64      |    8 | Base Address            |
 39 |  *   | u32      |    4 | Size                    |
 40 |  *   | char[32] |   32 | Name of Firmware Binary |
 41 |  *   | u16      |    2 | Number of flash areas   |
 42 |  *   +----------+------+-------------------------+
 43 |  * 
44 | * 45 | * The flash area structures immediately follow the flash map header. See FlashMapArea for a 46 | * description of the fields. 47 | */ 48 | public class FlashMapHeader { 49 | // Original header fields 50 | private final String signature; 51 | private final int majorVersion; 52 | private final int minorVersion; 53 | private final long baseAddress; 54 | private final long size; 55 | private final String name; 56 | private final int numAreas; 57 | 58 | private final FlashMapArea[] areas; 59 | 60 | /** 61 | * Constructs a FlashMapHeader from a specified BinaryReader. 62 | * 63 | * @param reader the specified BinaryReader 64 | */ 65 | public FlashMapHeader(BinaryReader reader) throws IOException { 66 | signature = reader.readNextAsciiString(FlashMapConstants.FMAP_SIGNATURE.length()); 67 | if (!signature.equals(FlashMapConstants.FMAP_SIGNATURE)) { 68 | throw new IOException("Not a valid flash map"); 69 | } 70 | 71 | // Read the header fields. 72 | majorVersion = reader.readNextUnsignedByte(); 73 | minorVersion = reader.readNextUnsignedByte(); 74 | baseAddress = reader.readNextLong(); 75 | size = reader.readNextUnsignedInt(); 76 | name = reader.readNextAsciiString(FlashMapConstants.FMAP_NAME_LEN).trim(); 77 | 78 | // Read each flash area. 79 | numAreas = reader.readNextUnsignedShort(); 80 | areas = new FlashMapArea[numAreas]; 81 | for (int i = 0; i < numAreas; i++) { 82 | areas[i] = new FlashMapArea(reader); 83 | } 84 | } 85 | 86 | /** 87 | * Returns an array of FlashMapAreas. 88 | * 89 | * @return an array of FlashMapAreas 90 | */ 91 | public FlashMapArea[] getAreas() { 92 | return areas; 93 | } 94 | 95 | /** 96 | * Returns the base address for the current firmware binary. 97 | * 98 | * @return the base address for the current firmware binary 99 | */ 100 | public long getBaseAddress() { 101 | return baseAddress; 102 | } 103 | 104 | /** 105 | * Returns the name of the current firmware binary. 106 | * 107 | * @return the name of the current firmware binary 108 | */ 109 | public String getName() { 110 | return name; 111 | } 112 | 113 | /** 114 | * Returns the version string for the current firmware binary. 115 | * 116 | * @return the version string for the current firmware binary 117 | */ 118 | public String getVersion() { 119 | return String.format("%d.%d", majorVersion, minorVersion); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/firmware/ifd/IntelFlashDescriptorConstants.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.ifd; 18 | 19 | /** 20 | * Various Intel Flash Descriptor constants. 21 | */ 22 | public final class IntelFlashDescriptorConstants { 23 | // Intel Flash Descriptor signature (little endian) 24 | public static final int IFD_SIGNATURE = 0x0FF0A55A; 25 | public static final byte[] IFD_SIGNATURE_BYTES = {0x5A, (byte) 0xA5, (byte) 0xF0, 0x0F}; 26 | 27 | // Flash region types 28 | public static final class FlashRegionType { 29 | public static final int FLASH_DESCRIPTOR = 0; 30 | public static final int BIOS = 1; 31 | public static final int MANAGEMENT_ENGINE = 2; 32 | public static final int GIGABIT_ETHERNET = 3; 33 | public static final int PLATFORM_DATA = 4; 34 | public static final int DEVICE_EXPANSION_1 = 5; 35 | public static final int SECONDARY_BIOS = 6; 36 | public static final int MICROCODE = 7; 37 | public static final int EMBEDDED_CONTROLLER = 8; 38 | public static final int DEVICE_EXPANSION_2 = 9; 39 | public static final int INNOVATION_ENGINE = 10; 40 | public static final int TEN_GIGABIT_ETHERNET_1 = 11; 41 | public static final int TEN_GIGABIT_ETHERNET_2 = 12; 42 | public static final int PLATFORM_TRUST_TECHNOLOGY = 15; 43 | 44 | public static String toString(int type) { 45 | switch (type) { 46 | case FLASH_DESCRIPTOR: 47 | return "Flash Descriptor"; 48 | case BIOS: 49 | return "BIOS"; 50 | case MANAGEMENT_ENGINE: 51 | return "Intel Management Engine"; 52 | case GIGABIT_ETHERNET: 53 | return "Gigabit Ethernet"; 54 | case PLATFORM_DATA: 55 | return "Platform Data"; 56 | case DEVICE_EXPANSION_1: 57 | return "Device Expansion 1"; 58 | case SECONDARY_BIOS: 59 | return "Secondary BIOS"; 60 | case MICROCODE: 61 | return "CPU Microcode"; 62 | case EMBEDDED_CONTROLLER: 63 | return "Embedded Controller"; 64 | case DEVICE_EXPANSION_2: 65 | return "Device Expansion 2"; 66 | case INNOVATION_ENGINE: 67 | return "Intel Innovation Engine"; 68 | case TEN_GIGABIT_ETHERNET_1: 69 | return "10 Gigabit Ethernet 1"; 70 | case TEN_GIGABIT_ETHERNET_2: 71 | return "10 Gigabit Ethernet 2"; 72 | case PLATFORM_TRUST_TECHNOLOGY: 73 | return "Platform Trust Technology"; 74 | default: 75 | return "Reserved"; 76 | } 77 | } 78 | } 79 | 80 | // Size of the flash descriptor region 81 | public static final int DESCRIPTOR_SIZE = 4096; 82 | 83 | // Flash read frequencies 84 | // Used to determine IFD version 85 | public static final class FlashFrequency { 86 | public static final int FREQ_20_MHZ = 0; 87 | public static final int FREQ_50_MHZ_30_MHZ = 4; 88 | public static final int FREQ_17_MHz = 6; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/firmware/ifd/IntelFlashFileSystem.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.ifd; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | 22 | import ghidra.app.util.bin.*; 23 | import ghidra.formats.gfilesystem.*; 24 | import ghidra.formats.gfilesystem.annotations.FileSystemInfo; 25 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 26 | import ghidra.util.task.TaskMonitor; 27 | 28 | @FileSystemInfo(type = "ifd", description = "Intel Flash Descriptor", factory = IntelFlashFileSystemFactory.class) 29 | public class IntelFlashFileSystem implements GFileSystem { 30 | private final FSRLRoot fsFSRL; 31 | private final FileSystemIndexHelper fsih; 32 | private final FileSystemRefManager refManager = new FileSystemRefManager(this); 33 | private ByteProvider provider; 34 | 35 | public IntelFlashFileSystem(FSRLRoot fsFSRL) { 36 | this.fsFSRL = fsFSRL; 37 | this.fsih = new FileSystemIndexHelper<>(this, fsFSRL); 38 | } 39 | 40 | public void mount(ByteProvider provider, long offset, TaskMonitor monitor) throws IOException { 41 | this.provider = provider; 42 | BinaryReader reader = new BinaryReader(provider, true); 43 | boolean hasZeroVector = offset >= 16; 44 | 45 | if (hasZeroVector) { 46 | reader.setPointerIndex(offset - 16); 47 | } 48 | 49 | IntelFlashDescriptor ifd = new IntelFlashDescriptor(reader, hasZeroVector); 50 | List regions = ifd.getRegions(); 51 | 52 | for (IntelFlashRegion region : regions) { 53 | String regionName = String.format("Region %02d - %s", region.getType(), 54 | IntelFlashDescriptorConstants.FlashRegionType.toString(region.getType())); 55 | fsih.storeFileWithParent(regionName, null, -1, false, region.length(), region); 56 | } 57 | } 58 | 59 | @Override 60 | public GFile lookup(String path) { 61 | return fsih.lookup(path); 62 | } 63 | 64 | @Override 65 | public void close() throws IOException { 66 | refManager.onClose(); 67 | if (provider != null) { 68 | provider.close(); 69 | provider = null; 70 | } 71 | 72 | fsih.clear(); 73 | } 74 | 75 | @Override 76 | public boolean isClosed() { 77 | return provider == null; 78 | } 79 | 80 | @Override 81 | public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException { 82 | IntelFlashRegion region = fsih.getMetadata(file); 83 | return new ByteProviderWrapper(region.getByteProvider(), file.getFSRL()); 84 | } 85 | 86 | @Override 87 | public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) { 88 | IntelFlashRegion region = fsih.getMetadata(file); 89 | return region.getFileAttributes(); 90 | } 91 | 92 | @Override 93 | public FSRLRoot getFSRL() { 94 | return fsFSRL; 95 | } 96 | 97 | @Override 98 | public List getListing(GFile directory) { 99 | return fsih.getListing(directory); 100 | } 101 | 102 | @Override 103 | public String getName() { 104 | return fsFSRL.getContainer().getName(); 105 | } 106 | 107 | @Override 108 | public FileSystemRefManager getRefManager() { 109 | return refManager; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/firmware/ifd/IntelFlashFileSystemFactory.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.ifd; 18 | 19 | import java.io.IOException; 20 | 21 | import ghidra.app.util.bin.BinaryReader; 22 | import ghidra.app.util.bin.ByteProvider; 23 | import ghidra.formats.gfilesystem.FSRLRoot; 24 | import ghidra.formats.gfilesystem.FileSystemService; 25 | import ghidra.formats.gfilesystem.factory.GFileSystemFactoryByteProvider; 26 | import ghidra.formats.gfilesystem.factory.GFileSystemProbeByteProvider; 27 | import ghidra.util.Msg; 28 | import ghidra.util.task.TaskMonitor; 29 | 30 | public class IntelFlashFileSystemFactory implements GFileSystemFactoryByteProvider, 31 | GFileSystemProbeByteProvider { 32 | @Override 33 | public boolean probe(ByteProvider provider, FileSystemService fsService, TaskMonitor monitor) throws IOException { 34 | return findIFDSignatureOffset(provider) != -1; 35 | } 36 | 37 | @Override 38 | public IntelFlashFileSystem create(FSRLRoot fsrlRoot, ByteProvider provider, FileSystemService fsService, 39 | TaskMonitor monitor) throws IOException { 40 | IntelFlashFileSystem fs = new IntelFlashFileSystem(fsrlRoot); 41 | try { 42 | long offset = findIFDSignatureOffset(provider); 43 | if (offset < 0) { 44 | throw new IOException("IFD signature not found"); 45 | } 46 | 47 | fs.mount(provider, offset, monitor); 48 | return fs; 49 | } catch (IOException ioe) { 50 | fs.close(); 51 | throw ioe; 52 | } 53 | } 54 | 55 | private long findIFDSignatureOffset(ByteProvider provider) throws IOException { 56 | long offset = 0; 57 | long remainingLength = provider.length(); 58 | BinaryReader reader = new BinaryReader(provider, true); 59 | while (remainingLength >= 4) { 60 | int signature = reader.readInt(offset); 61 | if (signature == IntelFlashDescriptorConstants.IFD_SIGNATURE) { 62 | if (remainingLength <= IntelFlashDescriptorConstants.DESCRIPTOR_SIZE - 16) { 63 | // Ignore binaries which lack regions other than the flash descriptor. 64 | return -1; 65 | } 66 | 67 | Msg.debug(this, String.format("Found IFD signature at 0x%X", offset)); 68 | return offset; 69 | } 70 | 71 | offset += 4; 72 | remainingLength -= 4; 73 | } 74 | 75 | return -1; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/firmware/ifd/IntelFlashRegion.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.ifd; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.app.util.bin.ByteProvider; 21 | import ghidra.app.util.bin.ByteProviderWrapper; 22 | import ghidra.formats.gfilesystem.fileinfo.FileAttributeType; 23 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 24 | 25 | /** 26 | * Parser for Intel flash regions (using region information defined in a flash descriptor). 27 | */ 28 | public class IntelFlashRegion { 29 | private final int baseAddress; 30 | private final int length; 31 | private final int type; 32 | private final ByteProvider provider; 33 | 34 | /** 35 | * Constructs an IntelFlashRegion from a specified BinaryReader. 36 | * 37 | * @param reader the specified BinaryReader 38 | * @param length the length of the flash region in bytes 39 | * @param type the region type (see IntelFlashDescriptorConstants.FlashRegionType) 40 | */ 41 | public IntelFlashRegion(BinaryReader reader, int length, int type) { 42 | baseAddress = (int) reader.getPointerIndex(); 43 | this.length = length; 44 | this.type = type; 45 | provider = new ByteProviderWrapper(reader.getByteProvider(), baseAddress, length); 46 | } 47 | 48 | /** 49 | * Returns a ByteProvider for the contents of the current flash region. 50 | * 51 | * @return a ByteProvider for the contents of the current flash region 52 | */ 53 | public ByteProvider getByteProvider() { 54 | return provider; 55 | } 56 | 57 | /** 58 | * Returns the current flash region's type. 59 | * 60 | * @return the current flash region's type 61 | */ 62 | public int getType() { 63 | return type; 64 | } 65 | 66 | /** 67 | * Returns the length of the current flash region. 68 | * 69 | * @return the length of the current flash region 70 | */ 71 | public int length() { 72 | return length; 73 | } 74 | 75 | public FileAttributes getFileAttributes() { 76 | FileAttributes attributes = new FileAttributes(); 77 | attributes.add(FileAttributeType.NAME_ATTR, IntelFlashDescriptorConstants.FlashRegionType.toString(type)); 78 | attributes.add(FileAttributeType.SIZE_ATTR, Long.valueOf(length)); 79 | attributes.add("Base Address", String.format("%#x", baseAddress)); 80 | return attributes; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/firmware/option_rom/DeviceList.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.option_rom; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.app.util.bin.StructConverter; 21 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 22 | import ghidra.program.model.data.ArrayDataType; 23 | import ghidra.program.model.data.DataType; 24 | import ghidra.program.model.data.Structure; 25 | import ghidra.program.model.data.StructureDataType; 26 | 27 | import java.io.IOException; 28 | import java.util.ArrayList; 29 | import java.util.Formatter; 30 | 31 | /** 32 | * Parser for the PCI expansion ROM device list. The device list is a zero-terminated list of 33 | * supported device IDs. The Device List Offset field in revision 3 of the PCI data structure is 34 | * used to calculate the device list's location. 35 | */ 36 | public class DeviceList implements StructConverter { 37 | private final ArrayList deviceList; 38 | 39 | /** 40 | * Constructs a DeviceList from a specified BinaryReader. 41 | * 42 | * @param reader the specified BinaryReader 43 | */ 44 | public DeviceList(BinaryReader reader) throws IOException { 45 | deviceList = new ArrayList<>(); 46 | 47 | // The device ID list is zero-terminated. 48 | short lastDeviceID = reader.readNextShort(); 49 | while (lastDeviceID != 0) { 50 | deviceList.add(lastDeviceID); 51 | lastDeviceID = reader.readNextShort(); 52 | } 53 | } 54 | 55 | /** 56 | * Returns FileAttributes for the current image. 57 | * 58 | * @return FileAttributes for the current image 59 | */ 60 | public FileAttributes getFileAttributes() { 61 | FileAttributes attributes = new FileAttributes(); 62 | Formatter formatter = new Formatter(); 63 | for (short deviceID : deviceList) { 64 | formatter.format("0x%02X ", deviceID); 65 | } 66 | 67 | attributes.add("Supported Device IDs", formatter.toString()); 68 | return attributes; 69 | } 70 | 71 | @Override 72 | public DataType toDataType() { 73 | Structure structure = new StructureDataType("device_list_t", 0); 74 | if (deviceList.size() > 0) { 75 | structure.add(new ArrayDataType(WORD, deviceList.size(), 2), "supported_device_ids", 76 | null); 77 | } 78 | 79 | structure.add(WORD, 2, "terminator", null); 80 | return structure; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/firmware/option_rom/LegacyOptionROMHeader.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.option_rom; 18 | 19 | import ghidra.app.util.bin.BinaryReader; 20 | import ghidra.formats.gfilesystem.fileinfo.FileAttributes; 21 | import ghidra.program.model.data.ArrayDataType; 22 | import ghidra.program.model.data.DataType; 23 | import ghidra.program.model.data.Structure; 24 | import ghidra.program.model.data.StructureDataType; 25 | import ghidra.util.Msg; 26 | 27 | import java.io.IOException; 28 | 29 | /** 30 | * Parser for legacy x86/PC compatible option ROM images. There are additional fields in the ROM 31 | * header: 32 | * 33 | *
 34 |  *   ROM Header
 35 |  *   +---------+------+------------------------------------+
 36 |  *   | Type    | Size | Description                        |
 37 |  *   +---------+------+------------------------------------+
 38 |  *   | u16     |    2 | Signature (0xAA55, little endian)  |
 39 |  *   | u8      |    1 | Image Size (in units of 512 bytes) |
 40 |  *   | u8[3]   |    3 | Entry Point                        |
 41 |  *   | u8[18]  |   18 | Reserved                           |
 42 |  *   | u16     |    2 | PCI Data Structure Offset          |
 43 |  *   +---------+------+------------------------------------+
 44 |  * 
45 | * 46 | * The Entry Point field in the ROM header usually contains a JMP (rel8 or rel16) instruction. 47 | */ 48 | public class LegacyOptionROMHeader extends OptionROMHeader { 49 | // Original header fields 50 | private final byte imageSize; 51 | private final byte[] entryPointInstruction; 52 | 53 | private int entryPointOffset; 54 | 55 | /** 56 | * Constructs a LegacyOptionROMHeader from a specified BinaryReader. 57 | * 58 | * @param reader the specified BinaryReader 59 | */ 60 | public LegacyOptionROMHeader(BinaryReader reader) throws IOException { 61 | super(reader); 62 | byte codeType = getPCIRHeader().getCodeType(); 63 | if (codeType != OptionROMConstants.CodeType.PC_AT_COMPATIBLE) { 64 | throw new IOException("Code type mismatch: expected PC-AT compatible (0), got " + 65 | OptionROMConstants.CodeType.toString(codeType) + " (" + codeType + ')'); 66 | } 67 | 68 | reader.setPointerIndex(0x2); 69 | imageSize = reader.readNextByte(); 70 | entryPointInstruction = reader.readNextByteArray(3); 71 | 72 | // The entry point field usually contains a relative CALL or JMP instruction. Decode it to find the address of 73 | // the entry point. 74 | entryPointOffset = 0x3; 75 | if (entryPointInstruction[0] == (byte) 0xE8) { 76 | // CALL rel16 (relative to next instruction) 77 | entryPointOffset += ((entryPointInstruction[2] & 0xFF) << 8 | entryPointInstruction[1] & 0xFF) & 0xFFFF; 78 | entryPointOffset += 0x3; // Size of the instruction (offset to next instruction) 79 | } 80 | else if (entryPointInstruction[0] == (byte) 0xEB) { 81 | // JMP rel8 (relative to next instruction) 82 | entryPointOffset += entryPointInstruction[1] & 0xFF; 83 | entryPointOffset += 0x2; // Size of the instruction (offset to next instruction) 84 | } 85 | else if (entryPointInstruction[0] == (byte) 0xE9) { 86 | // JMP rel16 (relative to next instruction) 87 | entryPointOffset += ((entryPointInstruction[2] & 0xFF) << 8 | entryPointInstruction[1] & 0xFF) & 0xFFFF; 88 | entryPointOffset += 0x3; // Size of the instruction (offset to next instruction) 89 | } 90 | 91 | Msg.debug(this, String.format("Entry point instruction: %x %x %x", entryPointInstruction[0], 92 | entryPointInstruction[1], entryPointInstruction[2])); 93 | Msg.debug(this, String.format("Entry point offset: %#x", entryPointOffset)); 94 | 95 | reader.setPointerIndex(0); 96 | } 97 | 98 | /** 99 | * Returns the decoded entry point offset. 100 | * 101 | * @return the decoded entry point offset 102 | */ 103 | public int getEntryPointOffset() { 104 | return entryPointOffset; 105 | } 106 | 107 | /** 108 | * Returns FileAttributes for the current image. 109 | * 110 | * @return FileAttributes for the current image 111 | */ 112 | public FileAttributes getFileAttributes() { 113 | FileAttributes attributes = super.getFileAttributes(); 114 | attributes.add("Entry Point Instruction", String.format("%02X %02X %02X", entryPointInstruction[0], 115 | entryPointInstruction[1], entryPointInstruction[2])); 116 | attributes.add("Decoded Entry Point Address", String.format("%#x", entryPointOffset)); 117 | return attributes; 118 | } 119 | 120 | @Override 121 | public DataType toDataType() { 122 | Structure structure = new StructureDataType("x86_option_rom_header_t", 0); 123 | structure.add(WORD, 2, "signature", null); 124 | structure.add(BYTE, 1, "image_size", null); 125 | structure.add(new ArrayDataType(BYTE, 0x3, 1), "entry_point_instruction", null); 126 | structure.add(new ArrayDataType(BYTE, 0x12, 1), "reserved", null); 127 | structure.add(POINTER, 2, "pcir_offset", null); 128 | return structure; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/firmware/option_rom/LegacyOptionROMLoader.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package firmware.option_rom; 18 | 19 | import ghidra.app.util.Option; 20 | import ghidra.app.util.bin.BinaryReader; 21 | import ghidra.app.util.bin.ByteProvider; 22 | import ghidra.app.util.importer.MessageLog; 23 | import ghidra.app.util.opinion.AbstractLibrarySupportLoader; 24 | import ghidra.app.util.opinion.LoadSpec; 25 | import ghidra.program.flatapi.FlatProgramAPI; 26 | import ghidra.program.model.address.Address; 27 | import ghidra.program.model.lang.LanguageCompilerSpecPair; 28 | import ghidra.program.model.listing.Program; 29 | import ghidra.util.Msg; 30 | import ghidra.util.task.TaskMonitor; 31 | 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | import java.util.ArrayList; 35 | import java.util.Collection; 36 | import java.util.List; 37 | 38 | public class LegacyOptionROMLoader extends AbstractLibrarySupportLoader { 39 | @Override 40 | public Collection findSupportedLoadSpecs(ByteProvider provider) { 41 | ArrayList loadSpecs = new ArrayList<>(); 42 | BinaryReader reader = new BinaryReader(provider, true); 43 | try { 44 | new LegacyOptionROMHeader(reader); 45 | } catch (IOException e) { 46 | return loadSpecs; 47 | } 48 | 49 | loadSpecs.add(new LoadSpec(this, 0, 50 | new LanguageCompilerSpecPair("x86:LE:16:Real Mode", "default"), true)); 51 | return loadSpecs; 52 | } 53 | 54 | @Override 55 | protected void load(ByteProvider provider, LoadSpec loadSpec, List