From a0620c8c13d48fdac5b18f01a8038c6b547d4f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20V=C3=A1zquez=20Blanco?= Date: Fri, 1 Dec 2023 08:57:38 +0100 Subject: [PATCH] Initial commit --- .github/dependabot.yml | 7 + .github/workflows/codeql.yml | 50 ++ .github/workflows/main.yml | 63 +++ .gitignore | 10 + Module.manifest | 0 README.md | 52 +++ build.gradle | 59 +++ data/README.txt | 15 + extension.properties | 5 + ghidra_scripts/README.txt | 1 + lib/kaitai-struct-runtime-0.10.jar | Bin 0 -> 22043 bytes os/linux_x86_64/README.txt | 3 + os/mac_x86_64/README.txt | 3 + os/win_x86_64/README.txt | 3 + src/main/help/help/TOC_Source.xml | 57 +++ .../help/help/topics/devicetreeblob/help.html | 23 + .../devicetreeblob/DeviceTreeBlobPlugin.java | 113 +++++ src/main/java/devicetreeblob/Dtb.java | 436 ++++++++++++++++++ src/main/resources/images/README.txt | 2 + src/test/java/README.test.txt | 2 + 20 files changed, 904 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 Module.manifest create mode 100644 README.md create mode 100644 build.gradle create mode 100644 data/README.txt create mode 100644 extension.properties create mode 100644 ghidra_scripts/README.txt create mode 100644 lib/kaitai-struct-runtime-0.10.jar create mode 100644 os/linux_x86_64/README.txt create mode 100644 os/mac_x86_64/README.txt create mode 100644 os/win_x86_64/README.txt create mode 100644 src/main/help/help/TOC_Source.xml create mode 100644 src/main/help/help/topics/devicetreeblob/help.html create mode 100644 src/main/java/devicetreeblob/DeviceTreeBlobPlugin.java create mode 100644 src/main/java/devicetreeblob/Dtb.java create mode 100644 src/main/resources/images/README.txt create mode 100644 src/test/java/README.test.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..01f6d79 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "main" + schedule: + interval: "weekly" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..b2bdeae --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,50 @@ +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '30 5 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: 'ubuntu-latest' + timeout-minutes: 360 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java-kotlin' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Install Ghidra + uses: antoniovazquezblanco/setup-ghidra@v1.1.0 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9afde01 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,63 @@ +name: Build +on: [push, pull_request, workflow_dispatch] +permissions: + contents: write + +jobs: + build_for_ghidra: + runs-on: ubuntu-latest + + strategy: + matrix: + ghidra: + - "10.4" + - "10.3.3" + - "10.3.2" + - "10.3.1" + - "10.3" + - "10.2.3" + - "10.2.2" + - "10.2.1" + - "10.2" + - "10.1.5" + - "10.1.4" + - "10.1.3" + - "10.1.2" + - "10.1.1" + - "10.1" + - "10.0.4" + - "10.0.3" + - "10.0.2" + - "10.0.1" + - "10.0" + + steps: + - name: Clone Repository + uses: actions/checkout@v4 + + - name: Install Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Install Ghidra ${{ matrix.version }} + uses: antoniovazquezblanco/setup-ghidra@v1.1.0 + with: + version: ${{ matrix.ghidra }} + + - name: Build the extension for Ghidra ${{ matrix.version }} + uses: gradle/gradle-build-action@v2.10.0 + with: + gradle-version: 7.3 + arguments: -PGHIDRA_INSTALL_DIR=${{ env.GHIDRA_INSTALL_DIR }} + + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + if: github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v') + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file_glob: true + file: dist/*GhidraDeviceTreeBlob.zip + prerelease: ${{ github.ref == 'refs/heads/main' }} + overwrite: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f3a8e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +bin/ +dist/ +build/ +.classpath +.project +.pydevproject +.gradle +.settings +.idea +.DS_Store diff --git a/Module.manifest b/Module.manifest new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1298d57 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# Ghidra System Map exporter + +[![Build](https://github.com/antoniovazquezblanco/GhidraDeviceTreeBlob/actions/workflows/main.yml/badge.svg)](https://github.com/antoniovazquezblanco/GhidraDeviceTreeBlob/actions/workflows/main.yml) + +Import Device Tree Information onto your Ghidra memory map. This is usefull when reversing firmware from propietary devices that do not publish SVD files. + +If you have SVD files for your device, I recommend you try [SVD Loader Ghidra](https://github.com/leveldown-security/SVD-Loader-Ghidra). + + +## Installing + +Go to the [releases page](https://github.com/antoniovazquezblanco/GhidraDeviceTreeBlob/releases) and download the latest version for your Ghidra distribution. + +In Ghidra main window go to `File` > `Install extensions...`. In the new window press the `+` icon to import the downloaded zip. + + +## Usage + +In a CodeBrowser window press `File` > `Import DTB...`. + +A file dialog will allow you to select your device tree file and import it. Memory map will automatically be updated. + + +## Development + +### Development environment + +1. First, install [Eclipse for Java Developers](https://www.eclipse.org/downloads/packages/). +2. Once installed, open Eclipse and click on `Help` > `Install New Software...`. A window will pop up. +3. Click on `Add...` > `Archive...`. It will open a file selection dialog. In this dialog, please select `GhidraDev.zip` file from `/Extensions/Eclipse/GhidraDev/`. +4. Check Ghidra category (or GhidraDev entry). +5. Repeatedly click `Next`. +6. Accept the terms of the license agreement. +7. Check the `Unsigned` table entry and click `Trust Selected`. +8. Restart Eclipse... + +### Importing the project + +After all of that, if you still want to develop and/or contribute to the project, first clone this repository: +```bash +git clone git@github.com:antoniovazquezblanco/GhidraDeviceTreeBlob.git +``` + +In Eclipse: +1. Click on `File` > `Import...`. +2. In the dialog click on `General` > `Projects from Folder or Archive` > `Next`. +3. Click on `Directory...` and select the `GhidraDeviceTreeBlob` folder you have just cloned. +4. Click on `Finish`. +5. Right click on the just imported project `GhidraDev` > `Link Ghidra...`. +6. Select your desired Ghidra installation and click on `Finish`. + +You are now ready to develop! diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..b5e0c3a --- /dev/null +++ b/build.gradle @@ -0,0 +1,59 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Builds a Ghidra Extension for a given Ghidra installation. +// +// An absolute path to the Ghidra installation directory must be supplied either by setting the +// GHIDRA_INSTALL_DIR environment variable or Gradle project property: +// +// > export GHIDRA_INSTALL_DIR= +// > gradle +// +// or +// +// > gradle -PGHIDRA_INSTALL_DIR= +// +// Gradle should be invoked from the directory of the project to build. Please see the +// application.gradle.version property in /Ghidra/application.properties +// for the correction version of Gradle to use for the Ghidra installation you specify. + +//----------------------START "DO NOT MODIFY" SECTION------------------------------ +def ghidraInstallDir + +if (System.env.GHIDRA_INSTALL_DIR) { + ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR +} +else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { + ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") +} + +if (ghidraInstallDir) { + apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" +} +else { + throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") +} +//----------------------END "DO NOT MODIFY" SECTION------------------------------- + +repositories { + mavenCentral() +} + +dependencies { + implementation 'io.kaitai:kaitai-struct-runtime:0.10' +} + +// Exclude additional files from the built extension +// Ex: buildExtension.exclude '.idea/**' diff --git a/data/README.txt b/data/README.txt new file mode 100644 index 0000000..1222f67 --- /dev/null +++ b/data/README.txt @@ -0,0 +1,15 @@ +The "data" directory is intended to hold data files that will be used by this module and will +not end up in the .jar file, but will be present in the zip or tar file. Typically, data +files are placed here rather than in the resources directory if the user may need to edit them. + +An optional data/languages directory can exist for the purpose of containing various Sleigh language +specification files and importer opinion files. + +The data/buildLanguage.xml is used for building the contents of the data/languages directory. + +The skel language definition has been commented-out within the skel.ldefs file so that the +skeleton language does not show-up within Ghidra. + +See the Sleigh language documentation (docs/languages/index.html) for details Sleigh language +specification syntax. + \ No newline at end of file diff --git a/extension.properties b/extension.properties new file mode 100644 index 0000000..6d515c9 --- /dev/null +++ b/extension.properties @@ -0,0 +1,5 @@ +name=@extname@ +description=This plugin manages the import of DTB files to add memory map information to a program. +author=Antonio Vázquez Blanco +createdOn=27/11/2023 +version=@extversion@ diff --git a/ghidra_scripts/README.txt b/ghidra_scripts/README.txt new file mode 100644 index 0000000..9e408f4 --- /dev/null +++ b/ghidra_scripts/README.txt @@ -0,0 +1 @@ +Java source directory to hold module-specific Ghidra scripts. diff --git a/lib/kaitai-struct-runtime-0.10.jar b/lib/kaitai-struct-runtime-0.10.jar new file mode 100644 index 0000000000000000000000000000000000000000..4d35286b7bdb8cac642b1e0ed62a96fb82967aca GIT binary patch literal 22043 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 + + + + + + diff --git a/src/main/help/help/topics/devicetreeblob/help.html b/src/main/help/help/topics/devicetreeblob/help.html new file mode 100644 index 0000000..1f9d6a1 --- /dev/null +++ b/src/main/help/help/topics/devicetreeblob/help.html @@ -0,0 +1,23 @@ + + + + + + + + + + + Skeleton Help File for a Module + + + + +

Skeleton Help File for a Module

+ +

This is a simple skeleton help topic. For a better description of what should and should not + go in here, see the "sample" Ghidra extension in the Extensions/Ghidra directory, or see your + favorite help topic. In general, language modules do not have their own help topics.

+ + diff --git a/src/main/java/devicetreeblob/DeviceTreeBlobPlugin.java b/src/main/java/devicetreeblob/DeviceTreeBlobPlugin.java new file mode 100644 index 0000000..761f4d5 --- /dev/null +++ b/src/main/java/devicetreeblob/DeviceTreeBlobPlugin.java @@ -0,0 +1,113 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package devicetreeblob; + +import java.io.File; +import java.io.IOException; + +import javax.swing.JComponent; + +import docking.action.builder.ActionBuilder; +import docking.tool.ToolConstants; +import docking.widgets.filechooser.GhidraFileChooser; +import docking.widgets.filechooser.GhidraFileChooserMode; +import ghidra.app.CorePluginPackage; +import ghidra.app.context.ProgramActionContext; +import ghidra.app.plugin.ProgramPlugin; +import ghidra.app.plugin.core.analysis.AutoAnalysisManager; +import ghidra.app.plugin.PluginCategoryNames; +import ghidra.framework.plugintool.PluginInfo; +import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.util.PluginStatus; +import ghidra.framework.preferences.Preferences; +import ghidra.program.model.listing.Program; +import ghidra.util.Msg; +import ghidra.util.filechooser.ExtensionFileFilter; + +//@formatter:off +@PluginInfo( + status = PluginStatus.RELEASED, + packageName = CorePluginPackage.NAME, + category = PluginCategoryNames.COMMON, + shortDescription = "Import External Device Tree Blob Files", + description = "This plugin manages the import of DTB files to add memory map information to a program." +) +//@formatter:on +public class DeviceTreeBlobPlugin extends ProgramPlugin { + public static final String NAME = "Device Tree Blob"; + private static final String LAST_DTBFILE_PREFERENCE_KEY = "Dtb.LastFile"; + + public DeviceTreeBlobPlugin(PluginTool tool) { + super(tool); + createActions(); + } + + private void createActions() { + new ActionBuilder("Load DTB File", this.getName()).withContext(ProgramActionContext.class) + .validContextWhen(pac -> pac.getProgram() != null).menuPath(ToolConstants.MENU_FILE, "Load DTB File...") + .menuGroup("Import PDB", "5").onAction(pac -> loadDtb(pac)).buildAndInstall(tool); + } + + private void loadDtb(ProgramActionContext pac) { + Program program = pac.getProgram(); + AutoAnalysisManager currentAutoAnalysisManager = AutoAnalysisManager.getAnalysisManager(program); + if (currentAutoAnalysisManager.isAnalyzing()) { + Msg.showWarn(getClass(), null, "Load PDB", "Unable to load PDB file while analysis is running."); + return; + } + + File file = getDtbFileFromDialog(pac.getComponentProvider().getComponent()); + if (file == null) + return; + + Msg.info(getClass(), "Loading " + file.getPath()); + Dtb dtb; + try { + dtb = Dtb.fromFile(file.getAbsolutePath()); + } catch (IOException e) { + Msg.error(getClass(), "Could not parse DTB file!", e); + return; + } + + + + + } + + private File getDtbFileFromDialog(JComponent parent) { + GhidraFileChooser chooser = new GhidraFileChooser(parent); + chooser.addFileFilter(ExtensionFileFilter.forExtensions("Device Tree Blobs", "dtb")); + chooser.setMultiSelectionEnabled(false); + chooser.setApproveButtonText("Choose"); + chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY); + chooser.setTitle("Select DTB"); + + String lastFile = Preferences.getProperty(LAST_DTBFILE_PREFERENCE_KEY); + if (lastFile != null) { + chooser.setSelectedFile(new File(lastFile)); + } + + File file = chooser.getSelectedFile(); + chooser.dispose(); + + if (file == null || !file.isFile()) + return null; + + Preferences.setProperty(LAST_DTBFILE_PREFERENCE_KEY, file.getPath()); + return file; + } + +} diff --git a/src/main/java/devicetreeblob/Dtb.java b/src/main/java/devicetreeblob/Dtb.java new file mode 100644 index 0000000..54af76a --- /dev/null +++ b/src/main/java/devicetreeblob/Dtb.java @@ -0,0 +1,436 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild +package devicetreeblob; + +import io.kaitai.struct.ByteBufferKaitaiStream; +import io.kaitai.struct.KaitaiStruct; +import io.kaitai.struct.KaitaiStream; +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; +import java.util.ArrayList; +import java.nio.charset.Charset; + +/** + * Also referred to as Devicetree Blob (DTB). It is a flat binary encoding + * of data (primarily devicetree data, although other data is possible as well). + * The data is internally stored as a tree of named nodes and properties. Nodes + * contain properties and child nodes, while properties are name-value pairs. + * + * The Devicetree Blobs (`.dtb` files) are compiled from the Devicetree Source + * files (`.dts`) through the Devicetree compiler (DTC). + * + * On Linux systems that support this, the blobs can be accessed in + * `/sys/firmware/fdt`: + * + * * + * + * The encoding of strings used in the `strings_block` and `structure_block` is + * actually a subset of ASCII: + * + * + * + * Example files: + * + * * + * @see Source + * @see Source + */ +public class Dtb extends KaitaiStruct { + public static Dtb fromFile(String fileName) throws IOException { + return new Dtb(new ByteBufferKaitaiStream(fileName)); + } + + public enum Fdt { + BEGIN_NODE(1), + END_NODE(2), + PROP(3), + NOP(4), + END(9); + + private final long id; + Fdt(long id) { this.id = id; } + public long id() { return id; } + private static final Map byId = new HashMap(5); + static { + for (Fdt e : Fdt.values()) + byId.put(e.id(), e); + } + public static Fdt byId(long id) { return byId.get(id); } + } + + public Dtb(KaitaiStream _io) { + this(_io, null, null); + } + + public Dtb(KaitaiStream _io, KaitaiStruct _parent) { + this(_io, _parent, null); + } + + public Dtb(KaitaiStream _io, KaitaiStruct _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root == null ? this : _root; + _read(); + } + private void _read() { + this.magic = this._io.readBytes(4); + if (!(Arrays.equals(magic(), new byte[] { -48, 13, -2, -19 }))) { + throw new KaitaiStream.ValidationNotEqualError(new byte[] { -48, 13, -2, -19 }, magic(), _io(), "/seq/0"); + } + this.totalSize = this._io.readU4be(); + this.ofsStructureBlock = this._io.readU4be(); + this.ofsStringsBlock = this._io.readU4be(); + this.ofsMemoryReservationBlock = this._io.readU4be(); + this.version = this._io.readU4be(); + this.minCompatibleVersion = this._io.readU4be(); + if (!(minCompatibleVersion() <= version())) { + throw new KaitaiStream.ValidationGreaterThanError(version(), minCompatibleVersion(), _io(), "/seq/6"); + } + this.bootCpuidPhys = this._io.readU4be(); + this.lenStringsBlock = this._io.readU4be(); + this.lenStructureBlock = this._io.readU4be(); + } + public static class MemoryBlock extends KaitaiStruct { + public static MemoryBlock fromFile(String fileName) throws IOException { + return new MemoryBlock(new ByteBufferKaitaiStream(fileName)); + } + + public MemoryBlock(KaitaiStream _io) { + this(_io, null, null); + } + + public MemoryBlock(KaitaiStream _io, Dtb _parent) { + this(_io, _parent, null); + } + + public MemoryBlock(KaitaiStream _io, Dtb _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.entries = new ArrayList(); + { + int i = 0; + while (!this._io.isEof()) { + this.entries.add(new MemoryBlockEntry(this._io, this, _root)); + i++; + } + } + } + private ArrayList entries; + private Dtb _root; + private Dtb _parent; + public ArrayList entries() { return entries; } + public Dtb _root() { return _root; } + public Dtb _parent() { return _parent; } + } + public static class FdtBlock extends KaitaiStruct { + public static FdtBlock fromFile(String fileName) throws IOException { + return new FdtBlock(new ByteBufferKaitaiStream(fileName)); + } + + public FdtBlock(KaitaiStream _io) { + this(_io, null, null); + } + + public FdtBlock(KaitaiStream _io, Dtb _parent) { + this(_io, _parent, null); + } + + public FdtBlock(KaitaiStream _io, Dtb _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.nodes = new ArrayList(); + { + FdtNode _it; + int i = 0; + do { + _it = new FdtNode(this._io, this, _root); + this.nodes.add(_it); + i++; + } while (!(_it.type() == Dtb.Fdt.END)); + } + } + private ArrayList nodes; + private Dtb _root; + private Dtb _parent; + public ArrayList nodes() { return nodes; } + public Dtb _root() { return _root; } + public Dtb _parent() { return _parent; } + } + public static class MemoryBlockEntry extends KaitaiStruct { + public static MemoryBlockEntry fromFile(String fileName) throws IOException { + return new MemoryBlockEntry(new ByteBufferKaitaiStream(fileName)); + } + + public MemoryBlockEntry(KaitaiStream _io) { + this(_io, null, null); + } + + public MemoryBlockEntry(KaitaiStream _io, Dtb.MemoryBlock _parent) { + this(_io, _parent, null); + } + + public MemoryBlockEntry(KaitaiStream _io, Dtb.MemoryBlock _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.address = this._io.readU8be(); + this.size = this._io.readU8be(); + } + private long address; + private long size; + private Dtb _root; + private Dtb.MemoryBlock _parent; + /** + * physical address of a reserved memory region + */ + public long address() { return address; } + + /** + * size of a reserved memory region + */ + public long size() { return size; } + public Dtb _root() { return _root; } + public Dtb.MemoryBlock _parent() { return _parent; } + } + public static class Strings extends KaitaiStruct { + public static Strings fromFile(String fileName) throws IOException { + return new Strings(new ByteBufferKaitaiStream(fileName)); + } + + public Strings(KaitaiStream _io) { + this(_io, null, null); + } + + public Strings(KaitaiStream _io, Dtb _parent) { + this(_io, _parent, null); + } + + public Strings(KaitaiStream _io, Dtb _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.strings = new ArrayList(); + { + int i = 0; + while (!this._io.isEof()) { + this.strings.add(new String(this._io.readBytesTerm((byte) 0, false, true, true), Charset.forName("ASCII"))); + i++; + } + } + } + private ArrayList strings; + private Dtb _root; + private Dtb _parent; + public ArrayList strings() { return strings; } + public Dtb _root() { return _root; } + public Dtb _parent() { return _parent; } + } + public static class FdtProp extends KaitaiStruct { + public static FdtProp fromFile(String fileName) throws IOException { + return new FdtProp(new ByteBufferKaitaiStream(fileName)); + } + + public FdtProp(KaitaiStream _io) { + this(_io, null, null); + } + + public FdtProp(KaitaiStream _io, Dtb.FdtNode _parent) { + this(_io, _parent, null); + } + + public FdtProp(KaitaiStream _io, Dtb.FdtNode _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.lenProperty = this._io.readU4be(); + this.ofsName = this._io.readU4be(); + this.property = this._io.readBytes(lenProperty()); + this.padding = this._io.readBytes(KaitaiStream.mod(-(_io().pos()), 4)); + } + private String name; + public String name() { + if (this.name != null) + return this.name; + KaitaiStream io = _root().stringsBlock()._io(); + long _pos = io.pos(); + io.seek(ofsName()); + this.name = new String(io.readBytesTerm((byte) 0, false, true, true), Charset.forName("ASCII")); + io.seek(_pos); + return this.name; + } + private long lenProperty; + private long ofsName; + private byte[] property; + private byte[] padding; + private Dtb _root; + private Dtb.FdtNode _parent; + public long lenProperty() { return lenProperty; } + public long ofsName() { return ofsName; } + public byte[] property() { return property; } + public byte[] padding() { return padding; } + public Dtb _root() { return _root; } + public Dtb.FdtNode _parent() { return _parent; } + } + public static class FdtNode extends KaitaiStruct { + public static FdtNode fromFile(String fileName) throws IOException { + return new FdtNode(new ByteBufferKaitaiStream(fileName)); + } + + public FdtNode(KaitaiStream _io) { + this(_io, null, null); + } + + public FdtNode(KaitaiStream _io, Dtb.FdtBlock _parent) { + this(_io, _parent, null); + } + + public FdtNode(KaitaiStream _io, Dtb.FdtBlock _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.type = Dtb.Fdt.byId(this._io.readU4be()); + { + Fdt on = type(); + if (on != null) { + switch (type()) { + case BEGIN_NODE: { + this.body = new FdtBeginNode(this._io, this, _root); + break; + } + case PROP: { + this.body = new FdtProp(this._io, this, _root); + break; + } + } + } + } + } + private Fdt type; + private KaitaiStruct body; + private Dtb _root; + private Dtb.FdtBlock _parent; + public Fdt type() { return type; } + public KaitaiStruct body() { return body; } + public Dtb _root() { return _root; } + public Dtb.FdtBlock _parent() { return _parent; } + } + public static class FdtBeginNode extends KaitaiStruct { + public static FdtBeginNode fromFile(String fileName) throws IOException { + return new FdtBeginNode(new ByteBufferKaitaiStream(fileName)); + } + + public FdtBeginNode(KaitaiStream _io) { + this(_io, null, null); + } + + public FdtBeginNode(KaitaiStream _io, Dtb.FdtNode _parent) { + this(_io, _parent, null); + } + + public FdtBeginNode(KaitaiStream _io, Dtb.FdtNode _parent, Dtb _root) { + super(_io); + this._parent = _parent; + this._root = _root; + _read(); + } + private void _read() { + this.name = new String(this._io.readBytesTerm((byte) 0, false, true, true), Charset.forName("ASCII")); + this.padding = this._io.readBytes(KaitaiStream.mod(-(_io().pos()), 4)); + } + private String name; + private byte[] padding; + private Dtb _root; + private Dtb.FdtNode _parent; + public String name() { return name; } + public byte[] padding() { return padding; } + public Dtb _root() { return _root; } + public Dtb.FdtNode _parent() { return _parent; } + } + private MemoryBlock memoryReservationBlock; + public MemoryBlock memoryReservationBlock() { + if (this.memoryReservationBlock != null) + return this.memoryReservationBlock; + long _pos = this._io.pos(); + this._io.seek(ofsMemoryReservationBlock()); + this._raw_memoryReservationBlock = this._io.readBytes((ofsStructureBlock() - ofsMemoryReservationBlock())); + KaitaiStream _io__raw_memoryReservationBlock = new ByteBufferKaitaiStream(_raw_memoryReservationBlock); + this.memoryReservationBlock = new MemoryBlock(_io__raw_memoryReservationBlock, this, _root); + this._io.seek(_pos); + return this.memoryReservationBlock; + } + private FdtBlock structureBlock; + public FdtBlock structureBlock() { + if (this.structureBlock != null) + return this.structureBlock; + long _pos = this._io.pos(); + this._io.seek(ofsStructureBlock()); + this._raw_structureBlock = this._io.readBytes(lenStructureBlock()); + KaitaiStream _io__raw_structureBlock = new ByteBufferKaitaiStream(_raw_structureBlock); + this.structureBlock = new FdtBlock(_io__raw_structureBlock, this, _root); + this._io.seek(_pos); + return this.structureBlock; + } + private Strings stringsBlock; + public Strings stringsBlock() { + if (this.stringsBlock != null) + return this.stringsBlock; + long _pos = this._io.pos(); + this._io.seek(ofsStringsBlock()); + this._raw_stringsBlock = this._io.readBytes(lenStringsBlock()); + KaitaiStream _io__raw_stringsBlock = new ByteBufferKaitaiStream(_raw_stringsBlock); + this.stringsBlock = new Strings(_io__raw_stringsBlock, this, _root); + this._io.seek(_pos); + return this.stringsBlock; + } + private byte[] magic; + private long totalSize; + private long ofsStructureBlock; + private long ofsStringsBlock; + private long ofsMemoryReservationBlock; + private long version; + private long minCompatibleVersion; + private long bootCpuidPhys; + private long lenStringsBlock; + private long lenStructureBlock; + private Dtb _root; + private KaitaiStruct _parent; + private byte[] _raw_memoryReservationBlock; + private byte[] _raw_structureBlock; + private byte[] _raw_stringsBlock; + public byte[] magic() { return magic; } + public long totalSize() { return totalSize; } + public long ofsStructureBlock() { return ofsStructureBlock; } + public long ofsStringsBlock() { return ofsStringsBlock; } + public long ofsMemoryReservationBlock() { return ofsMemoryReservationBlock; } + public long version() { return version; } + public long minCompatibleVersion() { return minCompatibleVersion; } + public long bootCpuidPhys() { return bootCpuidPhys; } + public long lenStringsBlock() { return lenStringsBlock; } + public long lenStructureBlock() { return lenStructureBlock; } + public Dtb _root() { return _root; } + public KaitaiStruct _parent() { return _parent; } + public byte[] _raw_memoryReservationBlock() { return _raw_memoryReservationBlock; } + public byte[] _raw_structureBlock() { return _raw_structureBlock; } + public byte[] _raw_stringsBlock() { return _raw_stringsBlock; } +} diff --git a/src/main/resources/images/README.txt b/src/main/resources/images/README.txt new file mode 100644 index 0000000..f20ae77 --- /dev/null +++ b/src/main/resources/images/README.txt @@ -0,0 +1,2 @@ +The "src/resources/images" directory is intended to hold all image/icon files used by +this module. diff --git a/src/test/java/README.test.txt b/src/test/java/README.test.txt new file mode 100644 index 0000000..11b8a4a --- /dev/null +++ b/src/test/java/README.test.txt @@ -0,0 +1,2 @@ +The "test" directory is intended to hold unit test cases. The package structure within +this folder should correspond to that found in the "src" folder.