-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Now rpx/rpl files can be imported directly.
- Loading branch information
Showing
6 changed files
with
267 additions
and
338 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<opinions> | ||
<constraint loader="Wii U Executable (RPX/RPL)" compilerSpecID="default"> | ||
<constraint primary="0" processor="PowerPC" endian="big" size="32" /> | ||
</constraint> | ||
</opinions> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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()); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.