mirror of
https://github.com/Maschell/GhidraRPXLoader.git
synced 2024-11-23 00:29:15 +01:00
Now rpx/rpl files can be imported directly.
This commit is contained in:
parent
a366f0eafc
commit
f9bc953bd6
@ -6,7 +6,7 @@ This is a (WIP) simple extension to open .rpx and .rpl files with Ghidra.
|
|||||||
|
|
||||||
Install the extension by using the `Install Extensions` option inside Ghidra or extracting the .zip manually into `[GHIDRA_ROOT]\Ghidra\Extensions`. Make sure to restart the program after installing.
|
Install the extension by using the `Install Extensions` option inside Ghidra or extracting the .zip manually into `[GHIDRA_ROOT]\Ghidra\Extensions`. Make sure to restart the program after installing.
|
||||||
|
|
||||||
Once the extension is installed, you can open a .rpx/.rpl via `File->Open File System...`. After opening the file, you should a filetree with a `converted.elf`, right click on it and select `Import`, confirm by pressing `Single file`.
|
Once the extension is installed, you import a .rpx/.rpl via `File->Import...`.
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
|
5
data/languages/RPX.opinion
Normal file
5
data/languages/RPX.opinion
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<opinions>
|
||||||
|
<constraint loader="Wii U Executable (RPX/RPL)" compilerSpecID="default">
|
||||||
|
<constraint primary="0" processor="PowerPC" endian="big" size="32" />
|
||||||
|
</constraint>
|
||||||
|
</opinions>
|
27
src/main/java/de/mas/ghidra/utils/Utils.java
Normal file
27
src/main/java/de/mas/ghidra/utils/Utils.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package de.mas.ghidra.utils;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
private Utils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grows a ByteBuffer if needed.
|
||||||
|
*
|
||||||
|
* @param buffer the original buffer
|
||||||
|
* @param size the needed size.
|
||||||
|
* @return A byte buffer with the expected size. If the buffer was big enough,
|
||||||
|
* the original buffer will be returned, otherwise a new one will be
|
||||||
|
* created.
|
||||||
|
*/
|
||||||
|
public static ByteBuffer checkAndGrowByteBuffer(ByteBuffer buffer, long size) {
|
||||||
|
// This probably the worst way to do this.
|
||||||
|
if (buffer.remaining() < size) {
|
||||||
|
ByteBuffer newBuffer = ByteBuffer.allocate((int) (buffer.capacity() + size - buffer.remaining()));
|
||||||
|
newBuffer.put(buffer.array());
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
155
src/main/java/de/mas/ghidra/wiiu/RPXUtils.java
Normal file
155
src/main/java/de/mas/ghidra/wiiu/RPXUtils.java
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package de.mas.ghidra.wiiu;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
|
import de.mas.ghidra.utils.Utils;
|
||||||
|
import generic.continues.RethrowContinuesFactory;
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfConstants;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfException;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfHeader;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfSectionHeader;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class RPXUtils {
|
||||||
|
private static byte[] RPX_MAGIC = new byte[] { 0x7F, 0x45, 0x4C, 0x46, 0x01, 0x02, 0x01, (byte) 0xCA, (byte) 0xFE };
|
||||||
|
public static final int SHF_RPL_ZLIB = 0x08000000;
|
||||||
|
public static final int SHT_NOBITS = 0x00000008;
|
||||||
|
|
||||||
|
public static final int SHT_RPL_EXPORTS = 0x80000001;
|
||||||
|
public static final int SHT_RPL_IMPORTS = 0x80000002;
|
||||||
|
public static final int SHT_RPL_CRCS = 0x80000003;
|
||||||
|
public static final int SHT_RPL_FILEINFO = 0x80000004;
|
||||||
|
|
||||||
|
public static byte[] convertRPX(ByteProvider bProvider, TaskMonitor monitor)
|
||||||
|
throws ElfException, IOException, CancelledException, DataFormatException {
|
||||||
|
ElfHeader elfFile = ElfHeader.createElfHeader(RethrowContinuesFactory.INSTANCE, bProvider);
|
||||||
|
elfFile.parse();
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(0);
|
||||||
|
|
||||||
|
long shdr_elf_offset = elfFile.e_ehsize() & 0xFFFFFFFF;
|
||||||
|
long shdr_data_elf_offset = shdr_elf_offset + elfFile.e_shnum() * elfFile.e_shentsize();
|
||||||
|
|
||||||
|
for (ElfSectionHeader h : elfFile.getSections()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
long curSize = h.getSize();
|
||||||
|
long flags = h.getFlags();
|
||||||
|
long offset = h.getOffset();
|
||||||
|
|
||||||
|
if (offset != 0) {
|
||||||
|
if ((flags & SHT_NOBITS) != SHT_NOBITS) {
|
||||||
|
byte[] data = h.getData();
|
||||||
|
|
||||||
|
if ((flags & SHF_RPL_ZLIB) == SHF_RPL_ZLIB) {
|
||||||
|
monitor.setMessage("Decompressing section " + h.getTypeAsString());
|
||||||
|
long section_size_inflated = ByteBuffer.wrap(Arrays.copyOf(data, 4)).getInt() & 0xFFFFFFFF;
|
||||||
|
Inflater inflater = new Inflater();
|
||||||
|
inflater.setInput(data, 4, (int) h.getSize() - 4); // the first byte is the size
|
||||||
|
|
||||||
|
byte[] decompressed = new byte[(int) section_size_inflated];
|
||||||
|
|
||||||
|
inflater.inflate(decompressed);
|
||||||
|
|
||||||
|
inflater.end();
|
||||||
|
|
||||||
|
// Is this alignment really necessary?
|
||||||
|
curSize = (section_size_inflated + 0x03) & ~0x3;
|
||||||
|
flags &= ~SHF_RPL_ZLIB;
|
||||||
|
data = decompressed;
|
||||||
|
}
|
||||||
|
long newEnd = shdr_data_elf_offset + curSize;
|
||||||
|
|
||||||
|
buffer = Utils.checkAndGrowByteBuffer(buffer, newEnd);
|
||||||
|
buffer.position((int) shdr_data_elf_offset);
|
||||||
|
// System.out.println("Write data " + String.format("%08X",
|
||||||
|
// shdr_data_elf_offset));
|
||||||
|
buffer.put(data);
|
||||||
|
offset = shdr_data_elf_offset;
|
||||||
|
shdr_data_elf_offset += curSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hacky way to fix import relocations
|
||||||
|
if (h.getType() == ElfSectionHeaderConstants.SHT_SYMTAB) {
|
||||||
|
monitor.setMessage("Fix import relocations " + h.getTypeAsString());
|
||||||
|
int symbolCount = (int) ((int) (curSize) / h.getEntrySize());
|
||||||
|
long entryPos = 0;
|
||||||
|
for (int i = 0; i < symbolCount; i++) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
long test_offset = (int) (offset + entryPos + 4);
|
||||||
|
buffer.position((int) test_offset);
|
||||||
|
int val = buffer.getInt();
|
||||||
|
|
||||||
|
if ((val & 0xF0000000L) == 0xC0000000L) {
|
||||||
|
long fixedAddress = val - 0xC0000000L + 0x01000000L;
|
||||||
|
buffer.position((int) test_offset);
|
||||||
|
buffer.putInt((int) fixedAddress);
|
||||||
|
}
|
||||||
|
entryPos += h.getEntrySize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = Utils.checkAndGrowByteBuffer(buffer, shdr_elf_offset + 0x28);
|
||||||
|
|
||||||
|
monitor.setMessage("Converting section " + h.getTypeAsString());
|
||||||
|
|
||||||
|
buffer.position((int) shdr_elf_offset);
|
||||||
|
System.out.println("Write header " + String.format("%08X", shdr_elf_offset));
|
||||||
|
buffer.putInt(h.getName());
|
||||||
|
if (h.getType() == SHT_RPL_CRCS || h.getType() == SHT_RPL_FILEINFO || h.getType() == SHT_RPL_EXPORTS
|
||||||
|
|| h.getType() == SHT_RPL_IMPORTS) {
|
||||||
|
buffer.putInt(ElfSectionHeaderConstants.SHT_NULL);
|
||||||
|
} else {
|
||||||
|
buffer.putInt(h.getType());
|
||||||
|
}
|
||||||
|
buffer.putInt((int) flags);
|
||||||
|
|
||||||
|
// Hacky way to fix import relocations
|
||||||
|
if ((h.getAddress() & 0xF0000000L) == 0xC0000000L) {
|
||||||
|
long fixedAddress = h.getAddress() - 0xC0000000L + 0x01000000L;
|
||||||
|
buffer.putInt((int) fixedAddress);
|
||||||
|
} else {
|
||||||
|
buffer.putInt((int) h.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.putInt((int) offset);
|
||||||
|
buffer.putInt((int) curSize);
|
||||||
|
buffer.putInt(h.getLink());
|
||||||
|
buffer.putInt(h.getInfo());
|
||||||
|
buffer.putInt((int) h.getAddressAlignment());
|
||||||
|
buffer.putInt((int) h.getEntrySize());
|
||||||
|
|
||||||
|
shdr_elf_offset += 0x28;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.setMessage("Create new ELF header");
|
||||||
|
|
||||||
|
buffer = Utils.checkAndGrowByteBuffer(buffer, 36);
|
||||||
|
|
||||||
|
buffer.position(0);
|
||||||
|
buffer.put(RPX_MAGIC);
|
||||||
|
buffer.position(0x10);
|
||||||
|
buffer.putShort(ElfConstants.ET_EXEC); // e.e_type());
|
||||||
|
buffer.putShort(elfFile.e_machine());
|
||||||
|
buffer.putInt(elfFile.e_version());
|
||||||
|
buffer.putInt((int) elfFile.e_entry());
|
||||||
|
buffer.putInt((int) elfFile.e_phoff());
|
||||||
|
buffer.putInt(elfFile.e_ehsize()); // e.e_shoff());
|
||||||
|
buffer.putInt(elfFile.e_flags());
|
||||||
|
buffer.putShort(elfFile.e_ehsize());
|
||||||
|
buffer.putShort(elfFile.e_phentsize());
|
||||||
|
buffer.putShort(elfFile.e_phnum());
|
||||||
|
buffer.putShort(elfFile.e_shentsize());
|
||||||
|
buffer.putShort(elfFile.e_shnum());
|
||||||
|
buffer.putShort(elfFile.e_shstrndx());
|
||||||
|
|
||||||
|
return buffer.array();
|
||||||
|
}
|
||||||
|
}
|
79
src/main/java/ghidra/app/util/opinion/RPXLoader.java
Normal file
79
src/main/java/ghidra/app/util/opinion/RPXLoader.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
|
||||||
|
import de.mas.ghidra.wiiu.RPXUtils;
|
||||||
|
import generic.continues.GenericFactory;
|
||||||
|
import ghidra.app.util.Option;
|
||||||
|
import ghidra.app.util.bin.ByteArrayProvider;
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfException;
|
||||||
|
import ghidra.app.util.bin.format.elf.ElfHeader;
|
||||||
|
import ghidra.app.util.importer.MemoryConflictHandler;
|
||||||
|
import ghidra.app.util.importer.MessageLog;
|
||||||
|
import ghidra.app.util.importer.MessageLogContinuesFactory;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class RPXLoader extends ElfLoader {
|
||||||
|
@Override
|
||||||
|
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||||
|
byte[] header = new byte[] { 0x7F, 0x45, 0x4C, 0x46, 0x01, 0x02, 0x01, (byte) 0xCA, (byte) 0xFE };
|
||||||
|
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||||
|
|
||||||
|
if (Arrays.equals(provider.readBytes(0, header.length), header)) {
|
||||||
|
List<QueryResult> results = QueryOpinionService.query(getName(), "0", null);
|
||||||
|
|
||||||
|
for (QueryResult result : results) {
|
||||||
|
loadSpecs.add(new LoadSpec(this, 0, result));
|
||||||
|
}
|
||||||
|
if (loadSpecs.isEmpty()) {
|
||||||
|
loadSpecs.add(new LoadSpec(this, 0, true));
|
||||||
|
}
|
||||||
|
return loadSpecs;
|
||||||
|
|
||||||
|
}
|
||||||
|
return loadSpecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Wii U Executable (RPX/RPL)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoaderTier getTier() {
|
||||||
|
return LoaderTier.SPECIALIZED_TARGET_LOADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTierPriority() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(ByteProvider provider, LoadSpec loadSpec, List<Option> options, Program program,
|
||||||
|
MemoryConflictHandler handler, TaskMonitor monitor, MessageLog log) throws IOException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
GenericFactory factory = MessageLogContinuesFactory.create(log);
|
||||||
|
byte[] data = RPXUtils.convertRPX(provider, monitor);
|
||||||
|
ElfHeader elf = ElfHeader.createElfHeader(factory, new ByteArrayProvider(data));
|
||||||
|
ElfProgramBuilder.loadElf(elf, program, options, log, handler, monitor);
|
||||||
|
} catch (ElfException e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
} catch (CancelledException e) {
|
||||||
|
// TODO: Caller should properly handle CancelledException instead
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,337 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* Copyright (C) 2019 Maschell
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
****************************************************************************/
|
|
||||||
package rpx;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.zip.DataFormatException;
|
|
||||||
import java.util.zip.Inflater;
|
|
||||||
|
|
||||||
import org.apache.commons.collections4.map.HashedMap;
|
|
||||||
|
|
||||||
import generic.continues.RethrowContinuesFactory;
|
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
|
||||||
import ghidra.app.util.bin.format.elf.ElfConstants;
|
|
||||||
import ghidra.app.util.bin.format.elf.ElfException;
|
|
||||||
import ghidra.app.util.bin.format.elf.ElfHeader;
|
|
||||||
import ghidra.app.util.bin.format.elf.ElfSectionHeader;
|
|
||||||
import ghidra.app.util.bin.format.elf.ElfSectionHeaderConstants;
|
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
|
||||||
import ghidra.formats.gfilesystem.FSRLRoot;
|
|
||||||
import ghidra.formats.gfilesystem.FSUtilities;
|
|
||||||
import ghidra.formats.gfilesystem.FileSystemIndexHelper;
|
|
||||||
import ghidra.formats.gfilesystem.FileSystemRefManager;
|
|
||||||
import ghidra.formats.gfilesystem.FileSystemService;
|
|
||||||
import ghidra.formats.gfilesystem.GFile;
|
|
||||||
import ghidra.formats.gfilesystem.GFileSystem;
|
|
||||||
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
|
|
||||||
import ghidra.formats.gfilesystem.factory.GFileSystemFactoryFull;
|
|
||||||
import ghidra.formats.gfilesystem.factory.GFileSystemProbeFull;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@FileSystemInfo(type = "rpx", description = "RPX", factory = RPXFileSystem.RPXFileSystemFactory.class, priority = FileSystemInfo.PRIORITY_HIGH)
|
|
||||||
public class RPXFileSystem implements GFileSystem {
|
|
||||||
|
|
||||||
private static byte[] RPX_MAGIC = new byte[] { 0x7F, 0x45, 0x4C, 0x46, 0x01, 0x02, 0x01, (byte) 0xCA, (byte) 0xFE };
|
|
||||||
public static final int SHF_RPL_ZLIB = 0x08000000;
|
|
||||||
public static final int SHT_NOBITS = 0x00000008;
|
|
||||||
|
|
||||||
public static final int SHT_RPL_EXPORTS = 0x80000001;
|
|
||||||
public static final int SHT_RPL_IMPORTS = 0x80000002;
|
|
||||||
public static final int SHT_RPL_CRCS = 0x80000003;
|
|
||||||
public static final int SHT_RPL_FILEINFO = 0x80000004;
|
|
||||||
|
|
||||||
private final FSRLRoot fsFSRL;
|
|
||||||
private FileSystemIndexHelper<ElfData> fsih;
|
|
||||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
|
||||||
|
|
||||||
private ByteProvider provider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File system constructor.
|
|
||||||
*
|
|
||||||
* @param fsFSRL The root {@link FSRL} of the file system.
|
|
||||||
* @param provider The file system provider.
|
|
||||||
*/
|
|
||||||
public RPXFileSystem(FSRLRoot fsFSRL, ByteProvider provider) {
|
|
||||||
this.fsFSRL = fsFSRL;
|
|
||||||
this.provider = provider;
|
|
||||||
this.fsih = new FileSystemIndexHelper<>(this, fsFSRL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mounts (opens) the file system.
|
|
||||||
*
|
|
||||||
* @param monitor A cancellable task monitor.
|
|
||||||
*/
|
|
||||||
public void mount(TaskMonitor monitor) {
|
|
||||||
monitor.setMessage("Opening " + RPXFileSystem.class.getSimpleName() + "...");
|
|
||||||
try {
|
|
||||||
ElfData data = convertRPX(provider, monitor);
|
|
||||||
fsih.storeFile("converted.elf", 0, false, data.elf_size, data);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Based on https://github.com/Relys/rpl2elf/blob/master/rpl2elf.c
|
|
||||||
*
|
|
||||||
* @param monitor
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* @throws CancelledException
|
|
||||||
*/
|
|
||||||
public static ElfData convertRPX(ByteProvider bProvider, TaskMonitor monitor)
|
|
||||||
throws ElfException, IOException, DataFormatException, CancelledException {
|
|
||||||
ElfHeader elfFile = ElfHeader.createElfHeader(RethrowContinuesFactory.INSTANCE, bProvider);
|
|
||||||
elfFile.parse();
|
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(0);
|
|
||||||
|
|
||||||
long shdr_elf_offset = elfFile.e_ehsize() & 0xFFFFFFFF;
|
|
||||||
long shdr_data_elf_offset = shdr_elf_offset + elfFile.e_shnum() * elfFile.e_shentsize();
|
|
||||||
|
|
||||||
for (ElfSectionHeader h : elfFile.getSections()) {
|
|
||||||
monitor.checkCanceled();
|
|
||||||
long curSize = h.getSize();
|
|
||||||
long flags = h.getFlags();
|
|
||||||
long offset = h.getOffset();
|
|
||||||
|
|
||||||
if (offset != 0) {
|
|
||||||
if ((flags & SHT_NOBITS) != SHT_NOBITS) {
|
|
||||||
byte[] data = h.getData();
|
|
||||||
|
|
||||||
if ((flags & SHF_RPL_ZLIB) == SHF_RPL_ZLIB) {
|
|
||||||
monitor.setMessage("Decompressing section " + h.getTypeAsString());
|
|
||||||
long section_size_inflated = ByteBuffer.wrap(Arrays.copyOf(data, 4)).getInt() & 0xFFFFFFFF;
|
|
||||||
Inflater inflater = new Inflater();
|
|
||||||
inflater.setInput(data, 4, (int) h.getSize() - 4); // the first byte is the size
|
|
||||||
|
|
||||||
byte[] decompressed = new byte[(int) section_size_inflated];
|
|
||||||
|
|
||||||
inflater.inflate(decompressed);
|
|
||||||
|
|
||||||
inflater.end();
|
|
||||||
|
|
||||||
// Is this alignment really necessary?
|
|
||||||
curSize = (section_size_inflated + 0x03) & ~0x3;
|
|
||||||
flags &= ~SHF_RPL_ZLIB;
|
|
||||||
data = decompressed;
|
|
||||||
}
|
|
||||||
long newEnd = shdr_data_elf_offset + curSize;
|
|
||||||
|
|
||||||
buffer = checkBuffer(buffer, newEnd);
|
|
||||||
buffer.position((int) shdr_data_elf_offset);
|
|
||||||
buffer.put(data);
|
|
||||||
offset = shdr_data_elf_offset;
|
|
||||||
shdr_data_elf_offset += curSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hacky way to fix import relocations
|
|
||||||
if (h.getType() == ElfSectionHeaderConstants.SHT_SYMTAB) {
|
|
||||||
monitor.setMessage("Fix import relocations " + h.getTypeAsString());
|
|
||||||
int symbolCount = (int) ((int) (curSize) / h.getEntrySize());
|
|
||||||
long entryPos = 0;
|
|
||||||
for (int i = 0; i < symbolCount; i++) {
|
|
||||||
monitor.checkCanceled();
|
|
||||||
long test_offset = (int) (offset + entryPos + 4);
|
|
||||||
buffer.position((int) test_offset);
|
|
||||||
int val = buffer.getInt();
|
|
||||||
|
|
||||||
if ((val & 0xF0000000L) == 0xC0000000L) {
|
|
||||||
long fixedAddress = val - 0xC0000000L + 0x01000000L;
|
|
||||||
buffer.position((int) test_offset);
|
|
||||||
buffer.putInt((int) fixedAddress);
|
|
||||||
}
|
|
||||||
entryPos += h.getEntrySize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = checkBuffer(buffer, shdr_elf_offset + 0x28);
|
|
||||||
|
|
||||||
monitor.setMessage("Converting section " + h.getTypeAsString());
|
|
||||||
|
|
||||||
buffer.position((int) shdr_elf_offset);
|
|
||||||
System.out.println("Write header " + String.format("%08X", shdr_elf_offset));
|
|
||||||
buffer.putInt(h.getName());
|
|
||||||
if (h.getType() == SHT_RPL_CRCS || h.getType() == SHT_RPL_FILEINFO || h.getType() == SHT_RPL_EXPORTS
|
|
||||||
|| h.getType() == SHT_RPL_IMPORTS) {
|
|
||||||
buffer.putInt(ElfSectionHeaderConstants.SHT_NULL);
|
|
||||||
} else {
|
|
||||||
buffer.putInt(h.getType());
|
|
||||||
}
|
|
||||||
buffer.putInt((int) flags);
|
|
||||||
|
|
||||||
// Hacky way to fix import relocations
|
|
||||||
if ((h.getAddress() & 0xF0000000L) == 0xC0000000L) {
|
|
||||||
long fixedAddress = h.getAddress() - 0xC0000000L + 0x01000000L;
|
|
||||||
buffer.putInt((int) fixedAddress);
|
|
||||||
} else {
|
|
||||||
buffer.putInt((int) h.getAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.putInt((int) offset);
|
|
||||||
buffer.putInt((int) curSize);
|
|
||||||
buffer.putInt(h.getLink());
|
|
||||||
buffer.putInt(h.getInfo());
|
|
||||||
buffer.putInt((int) h.getAddressAlignment());
|
|
||||||
buffer.putInt((int) h.getEntrySize());
|
|
||||||
|
|
||||||
shdr_elf_offset += 0x28;
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor.setMessage("Create new ELF header");
|
|
||||||
|
|
||||||
buffer.position(0);
|
|
||||||
buffer.put(RPX_MAGIC);
|
|
||||||
buffer.position(0x10);
|
|
||||||
buffer.putShort(ElfConstants.ET_EXEC); // e.e_type());
|
|
||||||
buffer.putShort(elfFile.e_machine());
|
|
||||||
buffer.putInt(elfFile.e_version());
|
|
||||||
buffer.putInt((int) elfFile.e_entry());
|
|
||||||
buffer.putInt((int) elfFile.e_phoff());
|
|
||||||
buffer.putInt(elfFile.e_ehsize()); // e.e_shoff());
|
|
||||||
buffer.putInt(elfFile.e_flags());
|
|
||||||
buffer.putShort(elfFile.e_ehsize());
|
|
||||||
buffer.putShort(elfFile.e_phentsize());
|
|
||||||
buffer.putShort(elfFile.e_phnum());
|
|
||||||
buffer.putShort(elfFile.e_shentsize());
|
|
||||||
buffer.putShort(elfFile.e_shnum());
|
|
||||||
buffer.putShort(elfFile.e_shstrndx());
|
|
||||||
|
|
||||||
byte[] dataArray = buffer.array();
|
|
||||||
|
|
||||||
return new ElfData(dataArray, (int) bProvider.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ByteBuffer checkBuffer(ByteBuffer buffer, long newEnd) {
|
|
||||||
// This probably the worst way to do this.
|
|
||||||
if (buffer.remaining() < newEnd) {
|
|
||||||
ByteBuffer newBuffer = ByteBuffer.allocate((int) (buffer.capacity() + newEnd - buffer.remaining()));
|
|
||||||
newBuffer.put(buffer.array());
|
|
||||||
return newBuffer;
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
refManager.onClose();
|
|
||||||
if (provider != null) {
|
|
||||||
provider.close();
|
|
||||||
provider = null;
|
|
||||||
}
|
|
||||||
fsih.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return fsFSRL.getContainer().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FSRLRoot getFSRL() {
|
|
||||||
return fsFSRL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isClosed() {
|
|
||||||
return provider == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getFileCount() {
|
|
||||||
return fsih.getFileCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileSystemRefManager getRefManager() {
|
|
||||||
return refManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GFile lookup(String path) throws IOException {
|
|
||||||
return fsih.lookup(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream(GFile file, TaskMonitor monitor) throws IOException, CancelledException {
|
|
||||||
ElfData metadata = fsih.getMetadata(file);
|
|
||||||
return (metadata != null) ? new ByteArrayInputStream(metadata.data) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<GFile> getListing(GFile directory) throws IOException {
|
|
||||||
return fsih.getListing(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getInfo(GFile file, TaskMonitor monitor) throws IOException {
|
|
||||||
ElfData metadata = fsih.getMetadata(file);
|
|
||||||
return (metadata == null) ? null : FSUtilities.infoMapToString(getInfoMap(metadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> getInfoMap(ElfData metadata) {
|
|
||||||
Map<String, String> infos = new HashedMap<>();
|
|
||||||
infos.put("elf_size", Integer.toString(metadata.elf_size));
|
|
||||||
infos.put("rpx_size", Integer.toString(metadata.rpx_size));
|
|
||||||
return infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RPXFileSystemFactory implements GFileSystemFactoryFull<RPXFileSystem>, GFileSystemProbeFull {
|
|
||||||
@Override
|
|
||||||
public RPXFileSystem create(FSRL containerFSRL, FSRLRoot targetFSRL, ByteProvider byteProvider,
|
|
||||||
File containerFile, FileSystemService fsService, TaskMonitor monitor)
|
|
||||||
throws IOException, CancelledException {
|
|
||||||
|
|
||||||
RPXFileSystem fs = new RPXFileSystem(targetFSRL, byteProvider);
|
|
||||||
fs.mount(monitor);
|
|
||||||
return fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean probe(FSRL containerFSRL, ByteProvider byteProvider, File containerFile,
|
|
||||||
FileSystemService fsService, TaskMonitor monitor) throws IOException, CancelledException {
|
|
||||||
byte[] header = byteProvider.readBytes(0, RPX_MAGIC.length);
|
|
||||||
return Arrays.equals(header, RPX_MAGIC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ElfData {
|
|
||||||
private byte[] data;
|
|
||||||
private int elf_size;
|
|
||||||
private int rpx_size;
|
|
||||||
|
|
||||||
public ElfData(byte[] data, int rpx_size) {
|
|
||||||
this.data = data;
|
|
||||||
this.elf_size = data.length;
|
|
||||||
this.rpx_size = rpx_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user