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 0000000..4d35286 Binary files /dev/null and b/lib/kaitai-struct-runtime-0.10.jar differ diff --git a/os/linux_x86_64/README.txt b/os/linux_x86_64/README.txt new file mode 100644 index 0000000..7dd33ce --- /dev/null +++ b/os/linux_x86_64/README.txt @@ -0,0 +1,3 @@ +The "os/linux_x86_64" directory is intended to hold Linux native binaries +which this module is dependent upon. This directory may be eliminated for a specific +module if native binaries are not provided for the corresponding platform. diff --git a/os/mac_x86_64/README.txt b/os/mac_x86_64/README.txt new file mode 100644 index 0000000..fbf2469 --- /dev/null +++ b/os/mac_x86_64/README.txt @@ -0,0 +1,3 @@ +The "os/mac_x86_64" directory is intended to hold macOS (OS X) native binaries +which this module is dependent upon. This directory may be eliminated for a specific +module if native binaries are not provided for the corresponding platform. diff --git a/os/win_x86_64/README.txt b/os/win_x86_64/README.txt new file mode 100644 index 0000000..e035995 --- /dev/null +++ b/os/win_x86_64/README.txt @@ -0,0 +1,3 @@ +The "os/win_x86_64" directory is intended to hold MS Windows native binaries (.exe) +which this module is dependent upon. This directory may be eliminated for a specific +module if native binaries are not provided for the corresponding platform. diff --git a/src/main/help/help/TOC_Source.xml b/src/main/help/help/TOC_Source.xml new file mode 100644 index 0000000..a34f62e --- /dev/null +++ b/src/main/help/help/TOC_Source.xml @@ -0,0 +1,57 @@ + + + + + + + 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.