commit 65c4c9333bda6812b8e531f2e8c5c583b8b44830 Author: orboditilt <45944072+orboditilt@users.noreply.github.com> Date: Mon Feb 25 10:08:43 2019 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8360da6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.settings/ +.classpath +.project +target/ diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..9ba120f --- /dev/null +++ b/CREDITS @@ -0,0 +1 @@ +CODE BASED ON https://github.com/odnoklassniki/one-elf \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5c304d1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4aeb212 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# RPXParserLib - A Java RPX/RPL parser library + +A library to parse the executables of the the Wii U. + +Example usages: +``` +// Load RPX/RPL +RPXFile rpxFile = new RPXFile(new File("test.rpx")); + +// Get all function symbols from the .text section +boolean hasSymbols = rpxFile.hasSymbols(); // Check if the file is not stripped. +List functionSymbols = rpxFile.getFunctionSymbolsText(); +// Get the data for a given ElfSymbol. +Optional functionData = rpxFile.getFunctionData(symbol); + +// Get all imports +Map> imports = rpxFile.getImports(); + +// Get all exports +List exports = rpxFile.getImports(); +``` + +# Use in projects +This library can be easily used via [jitpack.io](https://jitpack.io/) . +In the following the usage with maven will be explained. +Please take a look at the jitpack website for information on usage with other tools. + +Add the jitpack repository to the `pom.xml` +``` + + + jitpack.io + https://jitpack.io + + +``` +Then add the library as an dependency. +``` + + com.github.wiiu-env + RPXParserLib + + +``` +As the version, you can use any tag or hash of this repository. + +# Credits +Based on: https://github.com/odnoklassniki/one-elf +References: +- https://github.com/decaf-emu/ida_rpl_loader +- https://github.com/aerosoul94/ida_gel/tree/master/src/wiiu +- https://github.com/Relys/rpl2elf \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8e6e15d --- /dev/null +++ b/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + + de.orb + rpxparser + 0.0.1-SNAPSHOT + jar + + rpxparser + + + UTF-8 + 11 + + + + + org.projectlombok + lombok + 1.18.4 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 11 + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.0 + + + attach-javadocs + + jar + + + + + + + diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfException.java b/src/main/java/de/orb/wiiu/rpxparser/ElfException.java new file mode 100644 index 0000000..73e335a --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.io.IOException; + +public class ElfException extends IOException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public ElfException() { + } + + public ElfException(String message) { + super(message); + } + + public ElfException(String message, Throwable cause) { + super(message, cause); + } + + public ElfException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfExport.java b/src/main/java/de/orb/wiiu/rpxparser/ElfExport.java new file mode 100644 index 0000000..d0f546e --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfExport.java @@ -0,0 +1,46 @@ +package de.orb.wiiu.rpxparser; + +import java.nio.ByteBuffer; + +public class ElfExport { + + private final long offset; + private final int nameIndex; + private final ByteBuffer buf; + private final boolean isData; + + // Export SHT_RPL_EXPORTS name TLS flag + private static final int EXN_RPL_TLS = 0x80000000; + + public ElfExport(ByteBuffer buf, int offset, boolean isData) { + this.buf = buf; + this.offset = buf.getInt(offset + 0) & 0xFFFFFFFFL; + this.nameIndex = buf.getInt(offset + 4) & ~EXN_RPL_TLS; + this.isData = isData; + } + + public String name() { + int pos = nameIndex; + StringBuilder result = new StringBuilder(); + + for (byte b; (b = buf.get(pos)) != 0; pos++) { + result.append((char) b); + } + + return result.toString(); + } + + public long offset() { + return offset; + } + + public boolean isData() { + return isData; + } + + @Override + public String toString() { + return String.format("%s@%08X(%s)", name(), offset(), (isData() ? "data" : "function")); + } + +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfExportsTable.java b/src/main/java/de/orb/wiiu/rpxparser/ElfExportsTable.java new file mode 100644 index 0000000..7df4107 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfExportsTable.java @@ -0,0 +1,49 @@ +package de.orb.wiiu.rpxparser; + +import java.util.Iterator; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ElfExportsTable extends ElfSection implements Iterable { + + ElfExportsTable(ElfReader reader, int offset) { + super(reader, offset); + } + + private ElfExport export(int index) { + return new ElfExport(getSectionBuffer(), 8 + index * 8, name().startsWith(".d")); + } + + @Override + public int count() { + return getSectionBuffer().getInt(0); + } + + public Stream stream() { + return IntStream.range(0, count()).mapToObj(i -> export(i)); + } + + @Override + public Iterator iterator() { + return new Iterator() { + private final int count = count(); + private int index = 0; + + @Override + public boolean hasNext() { + return index < count; + } + + @Override + public ElfExport next() { + return export(index++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfImportsTable.java b/src/main/java/de/orb/wiiu/rpxparser/ElfImportsTable.java new file mode 100644 index 0000000..bc5b552 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfImportsTable.java @@ -0,0 +1,23 @@ +package de.orb.wiiu.rpxparser; + +import java.nio.ByteBuffer; + +public class ElfImportsTable extends ElfRelocationTable { + + ElfImportsTable(ElfReader reader, int offset) { + super(reader, offset); + } + + public String rplname() { + ByteBuffer buf = getSectionBuffer(); + int pos = (int) 8; + + StringBuilder result = new StringBuilder(); + + for (byte b; (b = buf.get(pos)) != 0; pos++) { + result.append((char) b); + } + return result.toString(); + } + +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfReader.java b/src/main/java/de/orb/wiiu/rpxparser/ElfReader.java new file mode 100644 index 0000000..c3969a2 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfReader.java @@ -0,0 +1,186 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Stream; + +public class ElfReader { + public static final short ET_NONE = 0; + public static final short ET_REL = 1; + public static final short ET_EXEC = 2; + public static final short ET_DYN = 3; + public static final short ET_CORE = 4; + public static final short ET_NUM = 5; + public static final short ET_LOOS = (short) 0xfe00; + public static final short ET_HIOS = (short) 0xfeff; + public static final short ET_LOPROC = (short) 0xff00; + public static final short ET_HIPROC = (short) 0xffff; + + public static final short EM_NONE = 0; + public static final short EM_SPARC = 2; + public static final short EM_386 = 3; + public static final short EM_PPC = 20; + public static final short EM_PPC64 = 21; + public static final short EM_ARM = 40; + public static final short EM_SPARCV9 = 43; + public static final short EM_IA_64 = 50; + public static final short EM_X86_64 = 62; + public static final short EM_AARCH64 = 183; + + public static final byte ELFOSABI_SYSV = 0; + public static final byte ELFOSABI_HPUX = 1; + public static final byte ELFOSABI_NETBSD = 2; + public static final byte ELFOSABI_GNU = 3; + public static final byte ELFOSABI_SOLARIS = 6; + public static final byte ELFOSABI_AIX = 7; + public static final byte ELFOSABI_IRIX = 8; + public static final byte ELFOSABI_FREEBSD = 9; + public static final byte ELFOSABI_TRU64 = 10; + public static final byte ELFOSABI_MODESTO = 11; + public static final byte ELFOSABI_OPENBSD = 12; + public static final byte ELFOSABI_ARM_AEABI = 64; + public static final byte ELFOSABI_ARM = 97; + public static final byte ELFOSABI_STANDALONE = -1; + + final ByteBuffer buf; + final byte abi; + final byte abiVersion; + final short type; + final short machine; + final int version; + final int flags; + final long entry; + final ElfSection[] sections; + final ElfStringTable strtab; + + public static ElfReader create(String fileName) throws IOException { + return ElfReader.create(new File(fileName)); + } + + public static ElfReader create(File file) throws IOException { + RandomAccessFile raf = new RandomAccessFile(file, "r"); + try { + return new ElfReader(raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, raf.length())); + } finally { + raf.close(); + } + } + + public ElfReader(ByteBuffer buf) throws IOException { + this.buf = buf; + byte[] ident = new byte[16]; + buf.get(ident); + if (ident[0] != 0x7f || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F') { + throw new ElfException("Invalid ELF signature"); + } + + switch (ident[4]) { + case 1: + break; + case 2: + throw new ElfException("ELF64 not supported"); + default: + throw new ElfException("Invalid ELF class"); + } + + switch (ident[5]) { + case 1: + buf.order(ByteOrder.LITTLE_ENDIAN); + break; + case 2: + buf.order(ByteOrder.BIG_ENDIAN); + break; + default: + throw new ElfException("Invalid ELF endian"); + } + + if (ident[6] != 1) { + throw new ElfException("Invalid ELF version"); + } + + this.abi = ident[7]; + this.abiVersion = ident[8]; + + this.type = buf.getShort(16); + this.machine = buf.getShort(18); + this.version = buf.getInt(20); + + this.entry = buf.getInt(24) & 0xffffffffL; + this.flags = buf.getInt(36); + this.sections = readSections(buf.getInt(32), buf.getShort(46) & 0xffff, buf.getShort(48) & 0xffff); + this.strtab = (ElfStringTable) sections[buf.getShort(50) & 0xffff]; + if (strtab == null) { + throw new ElfException(".strtab section was null"); + } + } + + private ElfSection[] readSections(int start, int entrySize, int entries) { + ElfSection[] sections = new ElfSection[entries]; + for (int i = 0; i < entries; i++) { + sections[i] = ElfSection.read(this, start + i * entrySize); + } + return sections; + } + + protected Stream sections() { + return Arrays.stream(sections).filter(s -> s != null); + } + + public Optional section(String name) { + return sections().filter(section -> section.name().equals(name)).findFirst(); + } + + public ByteOrder endian() { + return buf.order(); + } + + public byte abi() { + return abi; + } + + public byte abiVersion() { + return abiVersion; + } + + public short type() { + return type; + } + + public short machine() { + return machine; + } + + public int version() { + return version; + } + + public int flags() { + return flags; + } + + public long entry() { + return entry; + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfRelocation.java b/src/main/java/de/orb/wiiu/rpxparser/ElfRelocation.java new file mode 100644 index 0000000..be02f95 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfRelocation.java @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.nio.ByteBuffer; +import java.util.Optional; + +public class ElfRelocation { + public static final int R_PPC_NONE = 0; + + final Optional symtab; + final long offset; + final long info; + final long addend; + + ElfRelocation(ByteBuffer buf, ElfRelocationTable rel, int offset, ElfReader reader) { + this.symtab = rel.link().map(e -> (ElfSymbolTable) e); + + this.offset = buf.getInt(offset) & 0xffffffffL; + this.info = buf.getInt(offset + 4) & 0xffffffffL; + this.addend = rel.entrySize >= 12 ? buf.getInt(offset + 8) : 0; + } + + public long offset() { + return offset; + } + + public Optional symbol() { + if (type() == R_PPC_NONE) { + return Optional.empty(); + } + return symtab.map(tab -> tab.symbol((int) ((info) >> 8))); + } + + public int type() { + return (int) info & 0xFF; + } + + public long addend() { + return addend; + } + + @Override + public String toString() { + return symbol()// + .map(s -> s.name().orElse("EMPTY_NAME") + '(' + type() + ')' + addend)// + .orElse("NULL" + '(' + type() + ')' + addend); + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfRelocationTable.java b/src/main/java/de/orb/wiiu/rpxparser/ElfRelocationTable.java new file mode 100644 index 0000000..7d60317 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfRelocationTable.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.util.Iterator; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ElfRelocationTable extends ElfSection implements Iterable { + + ElfRelocationTable(ElfReader reader, int offset) { + super(reader, offset); + } + + public ElfRelocation relocation(int index) { + return new ElfRelocation(getSectionBuffer(), this, (int) (index * entrySize), reader); + } + + public Stream stream() { + return IntStream.range(0, count()).mapToObj(i -> relocation(i)); + } + + @Override + public Iterator iterator() { + return new Iterator() { + final int count = count(); + int index = 0; + + @Override + public boolean hasNext() { + return index < count; + } + + @Override + public ElfRelocation next() { + return relocation(index++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfSection.java b/src/main/java/de/orb/wiiu/rpxparser/ElfSection.java new file mode 100644 index 0000000..5a9fea8 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfSection.java @@ -0,0 +1,189 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +public class ElfSection { + public static final int SHT_NULL = 0; + public static final int SHT_PROGBITS = 1; + public static final int SHT_SYMTAB = 2; + public static final int SHT_STRTAB = 3; + public static final int SHT_RELA = 4; + public static final int SHT_HASH = 5; + public static final int SHT_DYNAMIC = 6; + public static final int SHT_NOTE = 7; + public static final int SHT_NOBITS = 8; + public static final int SHT_REL = 9; + public static final int SHT_SHLIB = 10; + public static final int SHT_DYNSYM = 11; + public static final int SHT_LOPROC = 0x70000000; + public static final int SHT_HIPROC = 0x7fffffff; + public static final int SHT_LOUSER = 0x80000000; + public static final int SHT_RPL_EXPORTS = 0x80000001; + public static final int SHT_RPL_IMPORTS = 0x80000002; + public static final int SHT_HIUSER = 0xffffffff; + + final ElfReader reader; + final int nameIndex; + final int type; + final long flags; + final long address; + final long offset; + final long orgSize; // the inside the .elf. may be the size of the compressed section + final long size; // the true size of the section. + final int linkIndex; + final int info; + final long align; + final long entrySize; + + ElfSection(ElfReader reader, int offset) { + ByteBuffer buf = reader.buf; + this.reader = reader; + this.nameIndex = buf.getInt(offset); + this.type = buf.getInt(offset + 4); + + this.flags = buf.getInt(offset + 8) & 0xffffffffL; + this.address = buf.getInt(offset + 12) & 0xffffffffL; + this.offset = buf.getInt(offset + 16) & 0xffffffffL; + this.orgSize = buf.getInt(offset + 20) & 0xffffffffL; + this.linkIndex = buf.getInt(offset + 24); + this.info = buf.getInt(offset + 28); + this.align = buf.getInt(offset + 32); + this.entrySize = buf.getInt(offset + 36); + + long tmp_size = orgSize; + // Fix the size of section when its compressed. + if ((flags & RPX_SHDR_ZLIB_FLAG) == RPX_SHDR_ZLIB_FLAG) { + buf.position((int) this.offset); + tmp_size = buf.getInt(); + } + size = tmp_size; + } + + public String name() { + // we can be sure the strtab is NOT null. + return reader.strtab.string(nameIndex); + } + + public int type() { + return type; + } + + public long flags() { + return flags; + } + + public long address() { + return address; + } + + public long offset() { + return offset; + } + + public long size() { + return size; + } + + public Optional link() { + return Optional.ofNullable(reader.sections[linkIndex]); + } + + public int info() { + return info; + } + + public long align() { + return align; + } + + public long entrySize() { + return entrySize; + } + + public int count() { + return entrySize == 0 ? 0 : (int) (size / entrySize); + } + + @Override + public String toString() { + return getClass().getSimpleName() + '(' + name() + ')'; + } + + ByteBuffer curBuffer = null; + + public ByteBuffer getSectionBuffer() { + if (curBuffer != null) { + return curBuffer; + } + ByteBuffer buf = reader.buf; + buf.position((int) offset); + byte[] data = new byte[(int) orgSize]; + buf.get(data, 0, (int) orgSize); + + if ((flags & RPX_SHDR_ZLIB_FLAG) == RPX_SHDR_ZLIB_FLAG) { + long section_size_inflated = buf.getInt(0) & 0xFFFFFFFF; + + Inflater inflater = new Inflater(); + inflater.setInput(data, 4, (int) orgSize - 4); // the first byte is the size + + byte[] decompressed = new byte[(int) section_size_inflated]; + + try { + inflater.inflate(decompressed); + } catch (DataFormatException e) { + // TODO + e.printStackTrace(); + } + + inflater.end(); + data = decompressed; + } + + curBuffer = ByteBuffer.wrap(data).order(buf.order()); + return curBuffer; + } + + public static final int RPX_SHDR_ZLIB_FLAG = 0x08000000; + + static ElfSection read(ElfReader reader, int offset) { + int type = reader.buf.getInt(offset + 4); + + switch (type) { + case SHT_NULL: + return null; + case SHT_SYMTAB: + case SHT_DYNSYM: + return new ElfSymbolTable(reader, offset); + case SHT_STRTAB: + return new ElfStringTable(reader, offset); + case SHT_RELA: + case SHT_REL: + return new ElfRelocationTable(reader, offset); + case SHT_RPL_IMPORTS: + return new ElfImportsTable(reader, offset); + case SHT_RPL_EXPORTS: + return new ElfExportsTable(reader, offset); + default: + return new ElfSection(reader, offset); + } + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfStringTable.java b/src/main/java/de/orb/wiiu/rpxparser/ElfStringTable.java new file mode 100644 index 0000000..51279ee --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfStringTable.java @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.nio.ByteBuffer; + +public class ElfStringTable extends ElfSection { + + ElfStringTable(ElfReader reader, int offset) { + super(reader, offset); + } + + public String string(int index) { + ByteBuffer buf = getSectionBuffer(); + + int pos = (int) index; + + StringBuilder result = new StringBuilder(); + + for (byte b; (b = buf.get(pos)) != 0; pos++) { + result.append((char) b); + } + + return result.toString(); + } + +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfSymbol.java b/src/main/java/de/orb/wiiu/rpxparser/ElfSymbol.java new file mode 100644 index 0000000..31b7679 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfSymbol.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.nio.ByteBuffer; +import java.util.Optional; + +public class ElfSymbol { + public static final byte STB_LOCAL = 0; + public static final byte STB_GLOBAL = 1; + public static final byte STB_WEAK = 2; + public static final byte STB_LOPROC = 13; + public static final byte STB_HIPROC = 15; + + public static final byte STT_NOTYPE = 0; + public static final byte STT_OBJECT = 1; + public static final byte STT_FUNC = 2; + public static final byte STT_SECTION = 3; + public static final byte STT_FILE = 4; + public static final byte STT_LOPROC = 13; + public static final byte STT_HIPROC = 15; + public static final int SHN_LORESERVE = 0xff00; /* Start of reserved indices */ + public static final int SHN_HIRESERVE = 0xffff; /* End of reserved indices */ + + final ElfReader reader; + final Optional strtab; + final int nameIndex; + final int info; + final byte other; + final int sectionIndex; + final long value; + final long size; + + ElfSymbol(ByteBuffer buf, Optional strtab, int offset, ElfReader reader) { + this.reader = reader; + this.strtab = strtab; + + this.nameIndex = buf.getInt(offset); + + this.value = buf.getInt(offset + 4) & 0xffffffffL; + this.size = buf.getInt(offset + 8) & 0xffffffffL; + this.info = buf.get(offset + 12) & 0xff; + this.other = buf.get(offset + 13); + this.sectionIndex = buf.getShort(offset + 14) & 0xffff; + } + + public Optional name() { + return strtab.map(s -> s.string(nameIndex)); + } + + public long value() { + return value; + } + + public long size() { + return size; + } + + public byte bind() { + return (byte) (info >> 4); + } + + public byte type() { + return (byte) (info & 0xf); + } + + public byte other() { + return other; + } + + public Optional section() { + if (sectionIndex >= SHN_LORESERVE && sectionIndex <= SHN_HIRESERVE) { + return Optional.empty(); + } + return Optional.ofNullable(reader.sections[sectionIndex]); + } + + @Override + public String toString() { + return name().map(name -> + '@' + Long.toHexString(value)).orElse("EMPTY_NAME@" + Long.toHexString(value)); + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/ElfSymbolTable.java b/src/main/java/de/orb/wiiu/rpxparser/ElfSymbolTable.java new file mode 100644 index 0000000..9761384 --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/ElfSymbolTable.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group + * + * 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 de.orb.wiiu.rpxparser; + +import java.util.Iterator; +import java.util.Optional; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ElfSymbolTable extends ElfSection implements Iterable { + + ElfSymbolTable(ElfReader reader, int offset) { + super(reader, offset); + } + + public ElfSymbol symbol(int index) { + return new ElfSymbol(getSectionBuffer(), link().map(e -> (ElfStringTable) e), (int) (index * entrySize), reader); + } + + public Optional symbol(String name) { + return stream() // + .filter(n -> n.name().filter(s_name -> s_name.equals(name)).isPresent()) // + .findAny(); + } + + public Stream stream() { + return IntStream.range(0, count()).mapToObj(i -> symbol(i)); + } + + @Override + public Iterator iterator() { + return new Iterator() { + private final int count = count(); + private int index = 0; + + @Override + public boolean hasNext() { + return index < count; + } + + @Override + public ElfSymbol next() { + return symbol(index++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/RPLImport.java b/src/main/java/de/orb/wiiu/rpxparser/RPLImport.java new file mode 100644 index 0000000..e25830c --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/RPLImport.java @@ -0,0 +1,14 @@ +package de.orb.wiiu.rpxparser; + +import lombok.Data; + +@Data +public class RPLImport implements Comparable { + private final String name; + private final String rplName; + + @Override + public int compareTo(RPLImport o) { + return name.compareTo(o.name); + } +} diff --git a/src/main/java/de/orb/wiiu/rpxparser/RPXFile.java b/src/main/java/de/orb/wiiu/rpxparser/RPXFile.java new file mode 100644 index 0000000..5d56cef --- /dev/null +++ b/src/main/java/de/orb/wiiu/rpxparser/RPXFile.java @@ -0,0 +1,99 @@ +package de.orb.wiiu.rpxparser; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class RPXFile { + final ElfReader elf_reader; + public static int MAX_FUNCTION_LENGTH_TO_COPY = 0; + + private static final int MIN_SYMBOLS = 10; + + public RPXFile(File f) throws IOException { + this(Files.readAllBytes(f.toPath())); + } + + public RPXFile(byte[] data) throws IOException { + this(ByteBuffer.wrap(data)); + } + + public RPXFile(ByteBuffer buf) throws IOException { + buf.position(0); + elf_reader = new ElfReader(buf); + } + + public List getExports() { + return elf_reader.sections() // + .filter(section -> section instanceof ElfExportsTable) // + .flatMap(m -> ((ElfExportsTable) m).stream()) // + .collect(Collectors.toUnmodifiableList()); + } + + public Map> getImports() { + return elf_reader.sections() // + .filter(section -> section instanceof ElfRelocationTable) // We want to check ElfRelocationTable sections + .flatMap(section -> ((ElfRelocationTable) section).stream()) // Get all relocations + .flatMap(r -> r.symbol().stream()) // Get symbols of relocations if existing + .filter(symbol -> symbol.section().filter(s -> (s instanceof ElfImportsTable)).isPresent()) // Only keep symbols of ElfImportsTable section + .map(symbol -> new RPLImport(symbol.name().orElseThrow(), ((ElfImportsTable) symbol.section().get()).rplname())) // Map to RPLImport + .distinct() // + .collect(Collectors.collectingAndThen( // + Collectors.groupingBy(RPLImport::getRplName, Collectors.toList()), // Group by RPLName + Collections::unmodifiableMap)); + } + + public Optional getFunctionData(ElfSymbol symbol) { + return symbol.section().flatMap(section -> getFunctionData(section, symbol.value() - section.address(), (int) symbol.size())); + } + + public Optional getFunctionData(ElfSection section, long _offset, int length) { + if (_offset < section.address() || _offset > section.address() + section.size()) { + return Optional.empty(); + } + + long offsetInSection = _offset - section.address(); + ByteBuffer buf = section.getSectionBuffer(); + + buf.position((int) offsetInSection); + + byte[] data = new byte[(int) length]; + buf.get(data, 0, (int) length); + + return Optional.of(data); + } + + public Optional getSymbolTable() { + return elf_reader.section(".symtab").map(section -> (ElfSymbolTable) section); + } + + public Optional getTextSection() { + return elf_reader.section(".text"); + } + + public boolean hasSymbols() { + return getFunctionSymbolsTextStream().limit(MIN_SYMBOLS).count() == MIN_SYMBOLS; + } + + public Stream getSymbols() { + return getSymbolTable().map(st -> st.stream()).orElse(Stream.empty()); + } + + public Stream getFunctionSymbolsTextStream() { + return getSymbols().filter(s -> s.type() == ElfSymbol.STT_FUNC) // We are only interested in functions + .filter(s -> !s.name().isEmpty()) // Not interested in functions with an empty name + .filter(s -> s.section().filter(m -> ".text".equals(m.name())).isPresent()); // + } + + public List getFunctionSymbolsText() { + return getFunctionSymbolsTextStream().collect(Collectors.toUnmodifiableList()); + } + +}