IL2CPP: Change metadata and binary to derive from BinaryObjectStream
This commit is contained in:
@@ -13,9 +13,9 @@ namespace Il2CppInspector
|
||||
{
|
||||
internal class Il2CppBinaryARM : Il2CppBinary
|
||||
{
|
||||
public Il2CppBinaryARM(IFileFormatReader stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
public Il2CppBinaryARM(IFileFormatStream stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
|
||||
public Il2CppBinaryARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
public Il2CppBinaryARM(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
: base(stream, codeRegistration, metadataRegistration, statusCallback) { }
|
||||
|
||||
// ARMv7-A Architecture Reference Manual: https://static.docs.arm.com/ddi0406/c/DDI0406C_C_arm_architecture_reference_manual.pdf
|
||||
@@ -67,7 +67,7 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Section 3.1
|
||||
private uint getNextThumbInstruction(IFileFormatReader image) {
|
||||
private uint getNextThumbInstruction(IFileFormatStream image) {
|
||||
// Assume 16-bit
|
||||
uint inst = image.ReadUInt16();
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Il2CppInspector
|
||||
private bool isBW(uint inst) => inst.Bits(27, 5) == 0b11110 && inst.Bits(14, 2) == 0b10 && inst.Bits(12, 1) == 1;
|
||||
|
||||
// Sweep a Thumb function and return the register values at the end (register number => value)
|
||||
private Dictionary<uint, uint> sweepThumbForAddressLoads(List<uint> func, uint baseAddress, IFileFormatReader image) {
|
||||
private Dictionary<uint, uint> sweepThumbForAddressLoads(List<uint> func, uint baseAddress, IFileFormatStream image) {
|
||||
// List of registers and addresses loaded into them
|
||||
var regs = new Dictionary<uint, uint>();
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Get a Thumb function that ends in B.W
|
||||
private List<uint> getThumbFunctionAtFileOffset(IFileFormatReader image, uint loc, uint maxLength) {
|
||||
private List<uint> getThumbFunctionAtFileOffset(IFileFormatStream image, uint loc, uint maxLength) {
|
||||
// Read a function that ends in a hard branch (B.W) or exceeds maxLength instructions
|
||||
var func = new List<uint>();
|
||||
uint inst;
|
||||
@@ -215,7 +215,7 @@ namespace Il2CppInspector
|
||||
return func;
|
||||
}
|
||||
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc) {
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc) {
|
||||
// Assembly bytes to search for at start of each function
|
||||
ulong metadataRegistration, codeRegistration;
|
||||
|
||||
@@ -264,7 +264,7 @@ namespace Il2CppInspector
|
||||
// - B
|
||||
// R0 = CodeRegistration, R1 = MetadataRegistration, R2 = Il2CppCodeGenOptions
|
||||
|
||||
var insts = image.Stream.ReadArray<uint>(pCgr, 10); // 7 instructions + 3 pointers
|
||||
var insts = image.ReadArray<uint>(pCgr, 10); // 7 instructions + 3 pointers
|
||||
var ldrOffsets = new uint[3];
|
||||
var pointers = new uint[3];
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace Il2CppInspector
|
||||
// A64 ISA reference: https://static.docs.arm.com/ddi0596/a/DDI_0596_ARM_a64_instruction_set_architecture.pdf
|
||||
internal class Il2CppBinaryARM64 : Il2CppBinary
|
||||
{
|
||||
public Il2CppBinaryARM64(IFileFormatReader stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
public Il2CppBinaryARM64(IFileFormatStream stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
|
||||
public Il2CppBinaryARM64(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
public Il2CppBinaryARM64(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
: base(stream, codeRegistration, metadataRegistration, statusCallback) { }
|
||||
|
||||
private (uint reg, ulong page)? getAdrp(uint inst, ulong pc) {
|
||||
@@ -70,7 +70,7 @@ namespace Il2CppInspector
|
||||
|
||||
private bool isB(uint inst) => inst.Bits(26, 6) == 0b_000101;
|
||||
|
||||
private Dictionary<uint, ulong> sweepForAddressLoads(List<uint> func, ulong baseAddress, IFileFormatReader image) {
|
||||
private Dictionary<uint, ulong> sweepForAddressLoads(List<uint> func, ulong baseAddress, IFileFormatStream image) {
|
||||
// List of registers and addresses loaded into them
|
||||
var regs = new Dictionary<uint, ulong>();
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Il2CppInspector
|
||||
return regs;
|
||||
}
|
||||
|
||||
private List<uint> getFunctionAtFileOffset(IFileFormatReader image, uint loc, uint maxLength) {
|
||||
private List<uint> getFunctionAtFileOffset(IFileFormatStream image, uint loc, uint maxLength) {
|
||||
// Read a function that ends in a hard branch (B) or exceeds maxLength instructions
|
||||
var func = new List<uint>();
|
||||
uint inst;
|
||||
@@ -141,7 +141,7 @@ namespace Il2CppInspector
|
||||
// - Loads can be done either with ADRP+ADD (loads the address of the wanted struct) or ADRP+LDR (loads a pointer to the address which must be de-referenced)
|
||||
// - Loads do not need to be pairs of sequential instructions
|
||||
// - We need to sweep the whole function from the ADRP to the next B to find an ADD or LDR with a corresponding register
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc) {
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc) {
|
||||
// Load function into memory
|
||||
// In practice, the longest function length we need is not generally longer than 7 instructions (0x1C bytes)
|
||||
var func = getFunctionAtFileOffset(image, loc, 7);
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace Il2CppInspector
|
||||
{
|
||||
internal class Il2CppBinaryX64 : Il2CppBinary
|
||||
{
|
||||
public Il2CppBinaryX64(IFileFormatReader stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
public Il2CppBinaryX64(IFileFormatStream stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
|
||||
public Il2CppBinaryX64(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
public Il2CppBinaryX64(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
: base(stream, codeRegistration, metadataRegistration, statusCallback) { }
|
||||
|
||||
// Format of 64-bit LEA:
|
||||
@@ -94,7 +94,7 @@ namespace Il2CppInspector
|
||||
return ((buff[offset + 1] & 0b0011_1000) >> 3, buff[offset + 1] & 0b0000_0111);
|
||||
}
|
||||
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc) {
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc) {
|
||||
|
||||
// Setup
|
||||
var buffSize = 0x76; // minimum number of bytes to process the longest expected function
|
||||
|
||||
@@ -11,12 +11,12 @@ namespace Il2CppInspector
|
||||
{
|
||||
internal class Il2CppBinaryX86 : Il2CppBinary
|
||||
{
|
||||
public Il2CppBinaryX86(IFileFormatReader stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
public Il2CppBinaryX86(IFileFormatStream stream, EventHandler<string> statusCallback = null) : base(stream, statusCallback) { }
|
||||
|
||||
public Il2CppBinaryX86(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
public Il2CppBinaryX86(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null)
|
||||
: base(stream, codeRegistration, metadataRegistration, statusCallback) { }
|
||||
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc) {
|
||||
protected override (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc) {
|
||||
ulong metadata, code;
|
||||
long pCgr;
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace Il2CppInspector.Cpp
|
||||
public static class CppCompiler
|
||||
{
|
||||
// Attempt to guess the compiler used to build the binary via its file type
|
||||
public static CppCompilerType GuessFromImage(IFileFormatReader image) => (image is PEReader? CppCompilerType.MSVC : CppCompilerType.GCC);
|
||||
public static CppCompilerType GuessFromImage(IFileFormatStream image) => (image is PEReader? CppCompilerType.MSVC : CppCompilerType.GCC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,11 @@ using System.Linq;
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
// This is a wrapper for multiple binary files of different architectures within a single AAB
|
||||
internal class AABReader : FileFormatReader<AABReader>
|
||||
internal class AABReader : FileFormatStream<AABReader>
|
||||
{
|
||||
private ZipArchive zip;
|
||||
private ZipArchiveEntry[] binaryFiles;
|
||||
|
||||
public AABReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "Package.aab";
|
||||
|
||||
protected override bool Init() {
|
||||
@@ -32,7 +30,7 @@ namespace Il2CppInspector
|
||||
return false;
|
||||
|
||||
try {
|
||||
zip = new ZipArchive(BaseStream);
|
||||
zip = new ZipArchive(this);
|
||||
|
||||
// Get list of binary files
|
||||
binaryFiles = zip.Entries.Where(f => f.FullName.StartsWith("base/lib/") && f.Name == "libil2cpp.so").ToArray();
|
||||
@@ -51,10 +49,10 @@ namespace Il2CppInspector
|
||||
return true;
|
||||
}
|
||||
|
||||
public override IFileFormatReader this[uint index] {
|
||||
public override IFileFormatStream this[uint index] {
|
||||
get {
|
||||
Console.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
|
||||
IFileFormatReader loaded = null;
|
||||
IFileFormatStream loaded = null;
|
||||
|
||||
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format
|
||||
var binary = binaryFiles[index].Open();
|
||||
@@ -13,13 +13,11 @@ using System.Linq;
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
// This is a wrapper for multiple binary files of different architectures within a single APK
|
||||
internal class APKReader : FileFormatReader<APKReader>
|
||||
internal class APKReader : FileFormatStream<APKReader>
|
||||
{
|
||||
private ZipArchive zip;
|
||||
private ZipArchiveEntry[] binaryFiles;
|
||||
|
||||
public APKReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "Package.apk";
|
||||
|
||||
protected override bool Init() {
|
||||
@@ -32,7 +30,7 @@ namespace Il2CppInspector
|
||||
return false;
|
||||
|
||||
try {
|
||||
zip = new ZipArchive(BaseStream);
|
||||
zip = new ZipArchive(this);
|
||||
|
||||
// Get list of binary files
|
||||
binaryFiles = zip.Entries.Where(f => f.FullName.StartsWith("lib/") && f.Name == "libil2cpp.so").ToArray();
|
||||
@@ -51,10 +49,10 @@ namespace Il2CppInspector
|
||||
return true;
|
||||
}
|
||||
|
||||
public override IFileFormatReader this[uint index] {
|
||||
public override IFileFormatStream this[uint index] {
|
||||
get {
|
||||
Console.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
|
||||
IFileFormatReader loaded = null;
|
||||
IFileFormatStream loaded = null;
|
||||
|
||||
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format
|
||||
var binary = binaryFiles[index].Open();
|
||||
@@ -17,7 +17,7 @@ namespace Il2CppInspector
|
||||
{
|
||||
internal class ElfReader32 : ElfReader<uint, elf_32_phdr, elf_32_sym, ElfReader32, Convert32>
|
||||
{
|
||||
public ElfReader32(Stream stream) : base(stream) {
|
||||
public ElfReader32() : base() {
|
||||
ElfReloc.GetRelocType = info => (Elf) (info & 0xff);
|
||||
ElfReloc.GetSymbolIndex = info => info >> 8;
|
||||
}
|
||||
@@ -25,12 +25,12 @@ namespace Il2CppInspector
|
||||
public override int Bits => 32;
|
||||
protected override Elf ArchClass => Elf.ELFCLASS32;
|
||||
|
||||
protected override void Write(BinaryWriter writer, uint value) => writer.Write(value);
|
||||
protected override void WriteWord(uint value) => Write(value);
|
||||
}
|
||||
|
||||
internal class ElfReader64 : ElfReader<ulong, elf_64_phdr, elf_64_sym, ElfReader64, Convert64>
|
||||
{
|
||||
public ElfReader64(Stream stream) : base(stream) {
|
||||
public ElfReader64() : base() {
|
||||
ElfReloc.GetRelocType = info => (Elf) (info & 0xffff_ffff);
|
||||
ElfReloc.GetSymbolIndex = info => info >> 32;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace Il2CppInspector
|
||||
public override int Bits => 64;
|
||||
protected override Elf ArchClass => Elf.ELFCLASS64;
|
||||
|
||||
protected override void Write(BinaryWriter writer, ulong value) => writer.Write(value);
|
||||
protected override void WriteWord(ulong value) => Write(value);
|
||||
}
|
||||
|
||||
interface IElfReader
|
||||
@@ -46,12 +46,12 @@ namespace Il2CppInspector
|
||||
uint GetPLTAddress();
|
||||
}
|
||||
|
||||
internal abstract class ElfReader<TWord, TPHdr, TSym, TReader, TConvert> : FileFormatReader<TReader>, IElfReader
|
||||
internal abstract class ElfReader<TWord, TPHdr, TSym, TReader, TConvert> : FileFormatStream<TReader>, IElfReader
|
||||
where TWord : struct
|
||||
where TPHdr : Ielf_phdr<TWord>, new()
|
||||
where TSym : Ielf_sym<TWord>, new()
|
||||
where TConvert : IWordConverter<TWord>, new()
|
||||
where TReader : FileFormatReader<TReader>
|
||||
where TReader : FileFormatStream<TReader>
|
||||
{
|
||||
private readonly TConvert conv = new TConvert();
|
||||
|
||||
@@ -115,8 +115,6 @@ namespace Il2CppInspector
|
||||
private bool preferPHT = false;
|
||||
private bool isMemoryImage = false;
|
||||
|
||||
public ElfReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "libil2cpp.so";
|
||||
|
||||
public override string Format => Bits == 32 ? "ELF" : "ELF64";
|
||||
@@ -141,7 +139,7 @@ namespace Il2CppInspector
|
||||
|
||||
protected abstract Elf ArchClass { get; }
|
||||
|
||||
protected abstract void Write(BinaryWriter writer, TWord value);
|
||||
protected abstract void WriteWord(TWord value);
|
||||
|
||||
protected override bool Init() {
|
||||
elf_header = ReadObject<elf_header<TWord>>();
|
||||
@@ -154,10 +152,6 @@ namespace Il2CppInspector
|
||||
if ((Elf) elf_header.m_arch != ArchClass)
|
||||
return false;
|
||||
|
||||
// Relocations and rebasing will modify the stream - ensure it is non-destructive
|
||||
if (!(BaseStream is MemoryStream))
|
||||
throw new InvalidOperationException("Input stream to ElfReader must be a MemoryStream.");
|
||||
|
||||
// Get PHT and SHT
|
||||
program_header_table = ReadArray<TPHdr>(conv.Long(elf_header.e_phoff), elf_header.e_phnum);
|
||||
section_header_table = ReadArray<elf_shdr<TWord>>(conv.Long(elf_header.e_shoff), elf_header.e_shnum);
|
||||
@@ -283,7 +277,6 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Process relocations
|
||||
using var writer = new BinaryWriter(BaseStream, Encoding.Default, true);
|
||||
var relsz = Sizeof(typeof(TSym));
|
||||
|
||||
var currentRel = 0;
|
||||
@@ -334,7 +327,7 @@ namespace Il2CppInspector
|
||||
|
||||
if (result.recognized) {
|
||||
Position = MapVATR(conv.ULong(rel.Offset));
|
||||
Write(writer, result.newValue);
|
||||
WriteWord(result.newValue);
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"Processed {rels.Count} relocations");
|
||||
@@ -525,12 +518,9 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
private void xorRange(int offset, int length, byte xorValue) {
|
||||
using var writer = new BinaryWriter(BaseStream, Encoding.Default, true);
|
||||
|
||||
var bytes = ReadBytes(offset, length);
|
||||
bytes = bytes.Select(b => (byte) (b ^ xorValue)).ToArray();
|
||||
writer.Seek(offset, SeekOrigin.Begin);
|
||||
writer.Write(bytes);
|
||||
Write(offset, bytes);
|
||||
}
|
||||
|
||||
private void xorSection(string sectionName, byte xorValue, uint stripeSize) {
|
||||
@@ -571,8 +561,7 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Rewrite to stream
|
||||
using var writer = new BinaryObjectWriter(BaseStream, Endianness, true);
|
||||
writer.WriteArray(conv.Long(elf_header.e_phoff), program_header_table);
|
||||
WriteArray(conv.Long(elf_header.e_phoff), program_header_table);
|
||||
IsModified = true;
|
||||
|
||||
// Rebase dynamic table if it exists
|
||||
@@ -595,7 +584,7 @@ namespace Il2CppInspector
|
||||
section.d_un = conv.Add(section.d_un, imageBase);
|
||||
|
||||
// Rewrite to stream
|
||||
writer.WriteArray(conv.Long(PT_DYNAMIC.p_offset), dt);
|
||||
WriteArray(conv.Long(PT_DYNAMIC.p_offset), dt);
|
||||
}
|
||||
|
||||
private void processSymbols() {
|
||||
@@ -14,23 +14,22 @@ using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
public interface IFileFormatReader
|
||||
public interface IFileFormatStream
|
||||
{
|
||||
BinaryObjectReader Stream { get; }
|
||||
double Version { get; set; }
|
||||
long Length { get; }
|
||||
uint NumImages { get; }
|
||||
string DefaultFilename { get; }
|
||||
bool IsModified { get; }
|
||||
IEnumerable<IFileFormatReader> Images { get; } // Each child image of this object (eg. 32/64-bit versions in Fat MachO file)
|
||||
IFileFormatReader this[uint index] { get; } // With no additional override, one object = one file, this[0] == this
|
||||
IEnumerable<IFileFormatStream> Images { get; } // Each child image of this object (eg. 32/64-bit versions in Fat MachO file)
|
||||
IFileFormatStream this[uint index] { get; } // With no additional override, one object = one file, this[0] == this
|
||||
long Position { get; set; }
|
||||
string Format { get; }
|
||||
string Arch { get; }
|
||||
int Bits { get; }
|
||||
ulong GlobalOffset { get; } // The virtual address where the code section (.text) would be loaded in memory
|
||||
ulong ImageBase { get; } // The virtual address of where the image would be loaded in memory (same as GlobalOffset except for PE)
|
||||
IEnumerable<IFileFormatReader> TryNextLoadStrategy(); // Some images can be loaded multiple ways, eg. default, packed
|
||||
IEnumerable<IFileFormatStream> TryNextLoadStrategy(); // Some images can be loaded multiple ways, eg. default, packed
|
||||
Dictionary<string, Symbol> GetSymbolTable();
|
||||
uint[] GetFunctionTable();
|
||||
IEnumerable<Export> GetExports();
|
||||
@@ -92,24 +91,52 @@ namespace Il2CppInspector
|
||||
long[] ReadMappedWordArray(ulong uiAddr, int count);
|
||||
List<U> ReadMappedObjectPointerArray<U>(ulong uiAddr, int count) where U : new();
|
||||
|
||||
void WriteEndianBytes(byte[] bytes);
|
||||
void Write(long int64);
|
||||
void Write(ulong uint64);
|
||||
void Write(int int32);
|
||||
void Write(uint uint32);
|
||||
void Write(short int16);
|
||||
void Write(ushort uint16);
|
||||
void Write(long addr, byte[] bytes);
|
||||
void Write(long addr, long int64);
|
||||
void Write(long addr, ulong uint64);
|
||||
void Write(long addr, int int32);
|
||||
void Write(long addr, uint uint32);
|
||||
void Write(long addr, short int16);
|
||||
void Write(long addr, ushort uint16);
|
||||
void Write(long addr, byte value);
|
||||
void Write(long addr, bool value);
|
||||
void WriteObject<T>(long addr, T obj);
|
||||
void WriteObject<T>(T obj);
|
||||
void WriteArray<T>(long addr, T[] array);
|
||||
void WriteArray<T>(T[] array);
|
||||
void WriteNullTerminatedString(long addr, string str, Encoding encoding = null);
|
||||
void WriteNullTerminatedString(string str, Encoding encoding = null);
|
||||
void WriteFixedLengthString(long addr, string str, int size = -1, Encoding encoding = null);
|
||||
void WriteFixedLengthString(string str, int size = -1, Encoding encoding = null);
|
||||
|
||||
EventHandler<string> OnStatusUpdate { get; set; }
|
||||
|
||||
public void AddPrimitiveMapping(Type objType, Type streamType);
|
||||
public void CopyTo(Stream stream);
|
||||
}
|
||||
|
||||
public class FileFormatReader
|
||||
public class FileFormatStream
|
||||
{
|
||||
// Helper method to try all defined file formats when the contents of the binary is unknown
|
||||
public static IFileFormatReader Load(string filename, LoadOptions loadOptions = null, EventHandler<string> statusCallback = null)
|
||||
public static IFileFormatStream Load(string filename, LoadOptions loadOptions = null, EventHandler<string> statusCallback = null)
|
||||
=> Load(new FileStream(filename, FileMode.Open, FileAccess.Read), loadOptions, statusCallback);
|
||||
|
||||
public static IFileFormatReader Load(Stream stream, LoadOptions loadOptions = null, EventHandler<string> statusCallback = null) {
|
||||
public static IFileFormatStream Load(Stream stream, LoadOptions loadOptions = null, EventHandler<string> statusCallback = null) {
|
||||
var types = Assembly.GetExecutingAssembly().DefinedTypes
|
||||
.Where(x => x.ImplementedInterfaces.Contains(typeof(IFileFormatReader)) && !x.IsGenericTypeDefinition);
|
||||
.Where(x => x.ImplementedInterfaces.Contains(typeof(IFileFormatStream)) && !x.IsGenericTypeDefinition);
|
||||
|
||||
foreach (var type in types) {
|
||||
try {
|
||||
if (type.GetMethod("Load", BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public,
|
||||
null, new[] {typeof(Stream), typeof(LoadOptions), typeof(EventHandler<string>)}, null)
|
||||
.Invoke(null, new object[] {stream, loadOptions, statusCallback}) is IFileFormatReader loaded)
|
||||
.Invoke(null, new object[] {stream, loadOptions, statusCallback}) is IFileFormatStream loaded)
|
||||
return loaded;
|
||||
}
|
||||
catch (TargetInvocationException ex) {
|
||||
@@ -120,18 +147,12 @@ namespace Il2CppInspector
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class FileFormatReader<T> : BinaryObjectReader, IFileFormatReader where T : FileFormatReader<T>
|
||||
public abstract class FileFormatStream<T> : BinaryObjectStream, IFileFormatStream where T : FileFormatStream<T>
|
||||
{
|
||||
public FileFormatReader(Stream stream) : base(stream) { }
|
||||
|
||||
public BinaryObjectReader Stream => this;
|
||||
|
||||
public abstract string DefaultFilename { get; }
|
||||
|
||||
public bool IsModified { get; protected set; } = false;
|
||||
|
||||
public long Length => BaseStream.Length;
|
||||
|
||||
public uint NumImages { get; protected set; } = 1;
|
||||
|
||||
public ulong GlobalOffset { get; protected set; }
|
||||
@@ -151,7 +172,7 @@ namespace Il2CppInspector
|
||||
|
||||
protected void StatusUpdate(string status) => OnStatusUpdate?.Invoke(this, status);
|
||||
|
||||
public IEnumerable<IFileFormatReader> Images {
|
||||
public IEnumerable<IFileFormatStream> Images {
|
||||
get {
|
||||
for (uint i = 0; i < NumImages; i++)
|
||||
yield return this[i];
|
||||
@@ -165,15 +186,12 @@ namespace Il2CppInspector
|
||||
|
||||
public static T Load(Stream stream, LoadOptions loadOptions = null, EventHandler<string> statusCallback = null) {
|
||||
// Copy the original stream in case we modify it
|
||||
var ms = new MemoryStream();
|
||||
|
||||
if (stream.CanSeek)
|
||||
stream.Position = 0;
|
||||
stream.CopyTo(ms);
|
||||
|
||||
ms.Position = 0;
|
||||
var pe = (T) Activator.CreateInstance(typeof(T), ms);
|
||||
return pe.InitImpl(loadOptions, statusCallback) ? pe : null;
|
||||
var binary = (T) Activator.CreateInstance(typeof(T));
|
||||
stream.CopyTo(binary);
|
||||
binary.Position = 0;
|
||||
return binary.InitImpl(loadOptions, statusCallback) ? binary : null;
|
||||
}
|
||||
|
||||
private bool InitImpl(LoadOptions loadOptions, EventHandler<string> statusCallback) {
|
||||
@@ -186,10 +204,10 @@ namespace Il2CppInspector
|
||||
protected virtual bool Init() => throw new NotImplementedException();
|
||||
|
||||
// Choose an image within the file for multi-architecture binaries
|
||||
public virtual IFileFormatReader this[uint index] => (index == 0)? this : throw new IndexOutOfRangeException("Binary image index out of bounds");
|
||||
public virtual IFileFormatStream this[uint index] => (index == 0)? this : throw new IndexOutOfRangeException("Binary image index out of bounds");
|
||||
|
||||
// For images that can be loaded and then tested with Il2CppBinary in multiple ways, get the next possible version of the image
|
||||
public virtual IEnumerable<IFileFormatReader> TryNextLoadStrategy() { yield return this; }
|
||||
public virtual IEnumerable<IFileFormatStream> TryNextLoadStrategy() { yield return this; }
|
||||
|
||||
// Find search locations in the symbol table for Il2Cpp data
|
||||
public virtual Dictionary<string, Symbol> GetSymbolTable() => new Dictionary<string, Symbol>();
|
||||
@@ -15,8 +15,6 @@ namespace Il2CppInspector
|
||||
{
|
||||
internal class MachOReader32 : MachOReader<uint, MachOReader32, Convert32>
|
||||
{
|
||||
public MachOReader32(Stream stream) : base(stream) { }
|
||||
|
||||
public override int Bits => 32;
|
||||
|
||||
protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC;
|
||||
@@ -37,8 +35,6 @@ namespace Il2CppInspector
|
||||
|
||||
internal class MachOReader64 : MachOReader<ulong, MachOReader64, Convert64>
|
||||
{
|
||||
public MachOReader64(Stream stream) : base(stream) { }
|
||||
|
||||
public override int Bits => 64;
|
||||
|
||||
protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC_64;
|
||||
@@ -59,9 +55,9 @@ namespace Il2CppInspector
|
||||
|
||||
// We need this convoluted generic TReader declaration so that "static T FileFormatReader.Load(Stream)"
|
||||
// is inherited to MachOReader32/64 with a correct definition of T
|
||||
internal abstract class MachOReader<TWord, TReader, TConvert> : FileFormatReader<TReader>
|
||||
internal abstract class MachOReader<TWord, TReader, TConvert> : FileFormatStream<TReader>
|
||||
where TWord : struct
|
||||
where TReader : FileFormatReader<TReader>
|
||||
where TReader : FileFormatStream<TReader>
|
||||
where TConvert : IWordConverter<TWord>, new()
|
||||
{
|
||||
private readonly TConvert conv = new TConvert();
|
||||
@@ -75,8 +71,6 @@ namespace Il2CppInspector
|
||||
|
||||
private List<Export> exports = new List<Export>();
|
||||
|
||||
protected MachOReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "app";
|
||||
|
||||
public override string Format => "Mach-O " + (Bits == 32 ? "32-bit" : "64-bit");
|
||||
@@ -17,7 +17,7 @@ namespace Il2CppInspector
|
||||
// References:
|
||||
// PE Header file: https://github.com/dotnet/llilc/blob/master/include/clr/ntimage.h
|
||||
// PE format specification: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format?redirectedfrom=MSDN
|
||||
internal class PEReader : FileFormatReader<PEReader>
|
||||
internal class PEReader : FileFormatStream<PEReader>
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
private extern static IntPtr LoadLibrary(string lpLibFileName);
|
||||
@@ -38,8 +38,6 @@ namespace Il2CppInspector
|
||||
[PE.IMAGE_SCN_MEM_READ | PE.IMAGE_SCN_MEM_WRITE | PE.IMAGE_SCN_CNT_INITIALIZED_DATA] = ".data"
|
||||
};
|
||||
|
||||
public PEReader(Stream stream) : base(stream) {}
|
||||
|
||||
public override string DefaultFilename => "GameAssembly.dll";
|
||||
|
||||
|
||||
@@ -187,22 +185,21 @@ namespace Il2CppInspector
|
||||
|
||||
// Truncate memory stream at start of COFF header
|
||||
var endOfSignature = ReadUInt32(0x3C) + 4; // DOS header + 4-byte PE signature
|
||||
BaseStream.SetLength(endOfSignature);
|
||||
SetLength(endOfSignature);
|
||||
|
||||
// Re-write the stream (the headers are only necessary in case the user wants to save)
|
||||
using var writer = new BinaryObjectWriter(BaseStream, Endianness, true);
|
||||
writer.Position = endOfSignature;
|
||||
writer.WriteObject(coff);
|
||||
if (Bits == 32) writer.WriteObject((PEOptHeader32) pe);
|
||||
else writer.WriteObject((PEOptHeader64) pe);
|
||||
writer.WriteArray(sections);
|
||||
writer.Write(peBytes, (int) Position, peBytes.Length - (int) Position);
|
||||
Position = endOfSignature;
|
||||
WriteObject(coff);
|
||||
if (Bits == 32) WriteObject((PEOptHeader32) pe);
|
||||
else WriteObject((PEOptHeader64) pe);
|
||||
WriteArray(sections);
|
||||
Write(peBytes, (int) Position, peBytes.Length - (int) Position);
|
||||
|
||||
IsModified = true;
|
||||
}
|
||||
|
||||
// Raw file / unpacked file load strategies
|
||||
public override IEnumerable<IFileFormatReader> TryNextLoadStrategy() {
|
||||
public override IEnumerable<IFileFormatStream> TryNextLoadStrategy() {
|
||||
// First load strategy: the regular file
|
||||
yield return this;
|
||||
|
||||
@@ -15,12 +15,10 @@ namespace Il2CppInspector
|
||||
// This is a wrapper for a Linux memory dump
|
||||
// The supplied file is a text file containing the output of "cat /proc/["self"|process-id]/maps"
|
||||
// We re-construct libil2cpp.so from the *.bin files and return it as the first image
|
||||
internal class ProcessMapReader : FileFormatReader<ProcessMapReader>
|
||||
internal class ProcessMapReader : FileFormatStream<ProcessMapReader>
|
||||
{
|
||||
private MemoryStream il2cpp;
|
||||
|
||||
public ProcessMapReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "maps.txt";
|
||||
|
||||
protected override bool Init() {
|
||||
@@ -30,7 +28,7 @@ namespace Il2CppInspector
|
||||
return false;
|
||||
|
||||
// Get the entire stream as a string
|
||||
var text = System.Text.Encoding.ASCII.GetString((BaseStream as MemoryStream).ToArray());
|
||||
var text = System.Text.Encoding.ASCII.GetString(ToArray());
|
||||
|
||||
// Line format is: https://stackoverflow.com/questions/1401359/understanding-linux-proc-id-maps
|
||||
// xxxxxxxx-yyyyyyyy ffff zzzzzzzz aa:bb c [whitespace] [image path]
|
||||
@@ -105,10 +103,10 @@ namespace Il2CppInspector
|
||||
return true;
|
||||
}
|
||||
|
||||
public override IFileFormatReader this[uint index] {
|
||||
public override IFileFormatStream this[uint index] {
|
||||
get {
|
||||
// Get merged stream as ELF file
|
||||
return (IFileFormatReader) ElfReader32.Load(il2cpp, LoadOptions, OnStatusUpdate) ?? ElfReader64.Load(il2cpp, LoadOptions, OnStatusUpdate);
|
||||
return (IFileFormatStream) ElfReader32.Load(il2cpp, LoadOptions, OnStatusUpdate) ?? ElfReader64.Load(il2cpp, LoadOptions, OnStatusUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,8 @@ namespace Il2CppInspector
|
||||
// https://www.psxhax.com/threads/ps4-self-spkg-file-format-documentation-detailed-for-scene-devs.6636/
|
||||
// https://wiki.henkaku.xyz/vita/images/a/a2/Vita_SDK_specifications.pdf
|
||||
// https://www.psxhax.com/threads/make-fself-gui-for-flat_zs-make_fself-py-script-by-cfwprophet.3494/
|
||||
internal class SElfReader : FileFormatReader<SElfReader>
|
||||
internal class SElfReader : FileFormatStream<SElfReader>
|
||||
{
|
||||
public SElfReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "libil2cpp.so";
|
||||
|
||||
public override string Format => sceData.ProductType == (ulong) SElfExInfoTypes.PTYPE_FAKE? "FSELF" : "SELF";
|
||||
@@ -9,12 +9,10 @@ using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
internal class UBReader : FileFormatReader<UBReader>
|
||||
internal class UBReader : FileFormatStream<UBReader>
|
||||
{
|
||||
private FatHeader header;
|
||||
|
||||
public UBReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string DefaultFilename => "app";
|
||||
|
||||
protected override bool Init() {
|
||||
@@ -30,7 +28,7 @@ namespace Il2CppInspector
|
||||
return true;
|
||||
}
|
||||
|
||||
public override IFileFormatReader this[uint index] {
|
||||
public override IFileFormatStream this[uint index] {
|
||||
get {
|
||||
Position = 0x8 + 0x14 * index; // sizeof(FatHeader), sizeof(FatArch)
|
||||
Endianness = Endianness.Big;
|
||||
@@ -41,7 +39,7 @@ namespace Il2CppInspector
|
||||
Endianness = Endianness.Little;
|
||||
|
||||
using var s = new MemoryStream(ReadBytes((int) arch.Size));
|
||||
return (IFileFormatReader) MachOReader32.Load(s, LoadOptions, OnStatusUpdate) ?? MachOReader64.Load(s, LoadOptions, OnStatusUpdate);
|
||||
return (IFileFormatStream) MachOReader32.Load(s, LoadOptions, OnStatusUpdate) ?? MachOReader64.Load(s, LoadOptions, OnStatusUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Il2CppInspector
|
||||
{
|
||||
public abstract partial class Il2CppBinary
|
||||
{
|
||||
public IFileFormatReader Image { get; }
|
||||
public IFileFormatStream Image { get; }
|
||||
|
||||
// IL2CPP-only API exports with decrypted names
|
||||
public Dictionary<string, ulong> APIExports { get; } = new Dictionary<string, ulong>();
|
||||
@@ -92,7 +92,7 @@ namespace Il2CppInspector
|
||||
private bool isModified = false;
|
||||
public bool IsModified => Image.IsModified || isModified;
|
||||
|
||||
protected Il2CppBinary(IFileFormatReader stream, EventHandler<string> statusCallback = null) {
|
||||
protected Il2CppBinary(IFileFormatStream stream, EventHandler<string> statusCallback = null) {
|
||||
Image = stream;
|
||||
OnStatusUpdate = statusCallback;
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Il2CppInspector
|
||||
DiscoverAPIExports();
|
||||
}
|
||||
|
||||
protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null) {
|
||||
protected Il2CppBinary(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null) {
|
||||
Image = stream;
|
||||
OnStatusUpdate = statusCallback;
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Load and initialize a binary of any supported architecture
|
||||
private static Il2CppBinary LoadImpl(IFileFormatReader stream, EventHandler<string> statusCallback) {
|
||||
private static Il2CppBinary LoadImpl(IFileFormatStream stream, EventHandler<string> statusCallback) {
|
||||
// Get type from image architecture
|
||||
var type = Assembly.GetExecutingAssembly().GetType("Il2CppInspector.Il2CppBinary" + stream.Arch.ToUpper());
|
||||
if (type == null)
|
||||
@@ -118,15 +118,15 @@ namespace Il2CppInspector
|
||||
|
||||
// Set width of long (convert to sizeof(int) for 32-bit files)
|
||||
if (stream[0].Bits == 32) {
|
||||
stream[0].Stream.PrimitiveMappings.Add(typeof(long), typeof(int));
|
||||
stream[0].Stream.PrimitiveMappings.Add(typeof(ulong), typeof(uint));
|
||||
stream[0].AddPrimitiveMapping(typeof(long), typeof(int));
|
||||
stream[0].AddPrimitiveMapping(typeof(ulong), typeof(uint));
|
||||
}
|
||||
|
||||
return (Il2CppBinary) Activator.CreateInstance(type, stream[0], statusCallback);
|
||||
}
|
||||
|
||||
// Load binary without a global-metadata.dat available
|
||||
public static Il2CppBinary Load(IFileFormatReader stream, double metadataVersion, EventHandler<string> statusCallback = null) {
|
||||
public static Il2CppBinary Load(IFileFormatStream stream, double metadataVersion, EventHandler<string> statusCallback = null) {
|
||||
foreach (var loadedImage in stream.TryNextLoadStrategy()) {
|
||||
var inst = LoadImpl(stream, statusCallback);
|
||||
if (inst.FindRegistrationStructs(metadataVersion))
|
||||
@@ -140,7 +140,7 @@ namespace Il2CppInspector
|
||||
// If it is specified and both symbol table and function scanning fail,
|
||||
// Metadata will be used to try to find the required structures with data analysis
|
||||
// If it is not specified, data analysis will not be performed
|
||||
public static Il2CppBinary Load(IFileFormatReader stream, Metadata metadata, EventHandler<string> statusCallback = null) {
|
||||
public static Il2CppBinary Load(IFileFormatStream stream, Metadata metadata, EventHandler<string> statusCallback = null) {
|
||||
foreach (var loadedImage in stream.TryNextLoadStrategy()) {
|
||||
var inst = LoadImpl(stream, statusCallback);
|
||||
if (inst.FindRegistrationStructs(metadata))
|
||||
@@ -154,7 +154,7 @@ namespace Il2CppInspector
|
||||
public void SaveToFile(string pathname) {
|
||||
Image.Position = 0;
|
||||
using (var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write))
|
||||
Image.Stream.BaseStream.CopyTo(outFile);
|
||||
Image.CopyTo(outFile);
|
||||
}
|
||||
|
||||
// Initialize binary without a global-metadata.dat available
|
||||
@@ -244,7 +244,7 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Architecture-specific search function
|
||||
protected abstract (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc);
|
||||
protected abstract (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc);
|
||||
|
||||
// Load all of the discovered metadata in the binary
|
||||
private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration, Metadata metadata = null) {
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Il2CppInspector
|
||||
public Dictionary<Il2CppMethodSpec, int> GenericMethodInvokerIndices => Binary.GenericMethodInvokerIndices;
|
||||
|
||||
// TODO: Finish all file access in the constructor and eliminate the need for this
|
||||
public IFileFormatReader BinaryImage => Binary.Image;
|
||||
public IFileFormatStream BinaryImage => Binary.Image;
|
||||
|
||||
private (ulong MetadataAddress, object Value)? getDefaultValue(int typeIndex, int dataIndex) {
|
||||
// No default
|
||||
@@ -543,7 +543,7 @@ namespace Il2CppInspector
|
||||
// Load the metadata file
|
||||
Metadata metadata;
|
||||
try {
|
||||
metadata = new Metadata(metadataStream, statusCallback);
|
||||
metadata = Metadata.FromStream(metadataStream, statusCallback);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Console.Error.WriteLine(ex.Message);
|
||||
@@ -554,9 +554,9 @@ namespace Il2CppInspector
|
||||
Console.WriteLine("Detected metadata version " + metadata.Version);
|
||||
|
||||
// Load the il2cpp code file (try all available file formats)
|
||||
IFileFormatReader stream;
|
||||
IFileFormatStream stream;
|
||||
try {
|
||||
stream = FileFormatReader.Load(binaryStream, loadOptions, statusCallback);
|
||||
stream = FileFormatStream.Load(binaryStream, loadOptions, statusCallback);
|
||||
|
||||
if (stream == null)
|
||||
throw new InvalidOperationException("Unsupported executable file format");
|
||||
@@ -571,7 +571,7 @@ namespace Il2CppInspector
|
||||
var processors = new List<Il2CppInspector>();
|
||||
foreach (var image in stream.Images) {
|
||||
Console.WriteLine("Container format: " + image.Format);
|
||||
Console.WriteLine("Container endianness: " + ((BinaryObjectReader) image).Endianness);
|
||||
Console.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
|
||||
Console.WriteLine("Architecture word size: {0}-bit", image.Bits);
|
||||
Console.WriteLine("Instruction set: " + image.Arch);
|
||||
Console.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
|
||||
|
||||
@@ -14,44 +14,60 @@ using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
public class Metadata : BinaryObjectReader
|
||||
public class Metadata : BinaryObjectStream
|
||||
{
|
||||
public Il2CppGlobalMetadataHeader Header { get; set; }
|
||||
|
||||
public Il2CppAssemblyDefinition[] Assemblies { get; }
|
||||
public Il2CppImageDefinition[] Images { get; }
|
||||
public Il2CppTypeDefinition[] Types { get; }
|
||||
public Il2CppMethodDefinition[] Methods { get; }
|
||||
public Il2CppParameterDefinition[] Params { get; }
|
||||
public Il2CppFieldDefinition[] Fields { get; }
|
||||
public Il2CppFieldDefaultValue[] FieldDefaultValues { get; }
|
||||
public Il2CppParameterDefaultValue[] ParameterDefaultValues { get; }
|
||||
public Il2CppPropertyDefinition[] Properties { get; }
|
||||
public Il2CppEventDefinition[] Events { get; }
|
||||
public Il2CppGenericContainer[] GenericContainers { get; }
|
||||
public Il2CppGenericParameter[] GenericParameters { get; }
|
||||
public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; }
|
||||
public Il2CppInterfaceOffsetPair[] InterfaceOffsets { get; }
|
||||
public Il2CppMetadataUsageList[] MetadataUsageLists { get; }
|
||||
public Il2CppMetadataUsagePair[] MetadataUsagePairs { get; }
|
||||
public Il2CppFieldRef[] FieldRefs { get; }
|
||||
public Il2CppAssemblyDefinition[] Assemblies { get; set; }
|
||||
public Il2CppImageDefinition[] Images { get; set; }
|
||||
public Il2CppTypeDefinition[] Types { get; set; }
|
||||
public Il2CppMethodDefinition[] Methods { get; set; }
|
||||
public Il2CppParameterDefinition[] Params { get; set; }
|
||||
public Il2CppFieldDefinition[] Fields { get; set; }
|
||||
public Il2CppFieldDefaultValue[] FieldDefaultValues { get; set; }
|
||||
public Il2CppParameterDefaultValue[] ParameterDefaultValues { get; set; }
|
||||
public Il2CppPropertyDefinition[] Properties { get; set; }
|
||||
public Il2CppEventDefinition[] Events { get; set; }
|
||||
public Il2CppGenericContainer[] GenericContainers { get; set; }
|
||||
public Il2CppGenericParameter[] GenericParameters { get; set; }
|
||||
public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; set; }
|
||||
public Il2CppInterfaceOffsetPair[] InterfaceOffsets { get; set; }
|
||||
public Il2CppMetadataUsageList[] MetadataUsageLists { get; set; }
|
||||
public Il2CppMetadataUsagePair[] MetadataUsagePairs { get; set; }
|
||||
public Il2CppFieldRef[] FieldRefs { get; set; }
|
||||
|
||||
public int[] InterfaceUsageIndices { get; }
|
||||
public int[] NestedTypeIndices { get; }
|
||||
public int[] AttributeTypeIndices { get; }
|
||||
public int[] GenericConstraintIndices { get; }
|
||||
public uint[] VTableMethodIndices { get; }
|
||||
public string[] StringLiterals { get; }
|
||||
public int[] InterfaceUsageIndices { get; set; }
|
||||
public int[] NestedTypeIndices { get; set; }
|
||||
public int[] AttributeTypeIndices { get; set; }
|
||||
public int[] GenericConstraintIndices { get; set; }
|
||||
public uint[] VTableMethodIndices { get; set; }
|
||||
public string[] StringLiterals { get; set; }
|
||||
|
||||
public Dictionary<int, string> Strings { get; } = new Dictionary<int, string>();
|
||||
|
||||
// Set if something in the metadata has been modified / decrypted
|
||||
public bool IsModified { get; } = false;
|
||||
public bool IsModified { get; private set; } = false;
|
||||
|
||||
public Metadata(MemoryStream stream, EventHandler<string> statusCallback = null) : base(stream)
|
||||
// Status update callback
|
||||
private EventHandler<string> OnStatusUpdate { get; set; }
|
||||
private void StatusUpdate(string status) => OnStatusUpdate?.Invoke(this, status);
|
||||
|
||||
// Initialize metadata object from a stream
|
||||
public static Metadata FromStream(MemoryStream stream, EventHandler<string> statusCallback = null) {
|
||||
var metadata = new Metadata(statusCallback);
|
||||
stream.Position = 0;
|
||||
stream.CopyTo(metadata);
|
||||
metadata.Position = 0;
|
||||
metadata.Initialize();
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private Metadata(EventHandler<string> statusCallback = null) : base() => OnStatusUpdate = statusCallback;
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// Pre-processing hook
|
||||
var pluginResult = PluginHooks.PreProcessMetadata(stream);
|
||||
var pluginResult = PluginHooks.PreProcessMetadata(this);
|
||||
IsModified = pluginResult.IsStreamModified;
|
||||
|
||||
// Read metadata header
|
||||
@@ -185,7 +201,7 @@ namespace Il2CppInspector
|
||||
if (stringOffsets.Except(Strings.Keys).Any()) {
|
||||
|
||||
Console.WriteLine("Decrypting strings...");
|
||||
statusCallback?.Invoke(this, "Decrypting strings");
|
||||
StatusUpdate("Decrypting strings");
|
||||
|
||||
// There may be zero-padding at the end of the last string since counts seem to be word-aligned
|
||||
// Find the true location one byte after the final character of the final string
|
||||
@@ -209,13 +225,10 @@ namespace Il2CppInspector
|
||||
}
|
||||
|
||||
// Write changes back in case the user wants to save the metadata file
|
||||
using (var sw = new BinaryObjectWriter(BaseStream, Endianness, leaveOpen: true)) {
|
||||
sw.Version = Version;
|
||||
sw.Position = Header.stringOffset;
|
||||
Position = Header.stringOffset;
|
||||
foreach (var str in Strings.OrderBy(s => s.Key))
|
||||
sw.WriteNullTerminatedString(str.Value);
|
||||
sw.Flush();
|
||||
}
|
||||
WriteNullTerminatedString(str.Value);
|
||||
Flush();
|
||||
|
||||
IsModified = true;
|
||||
}
|
||||
@@ -235,7 +248,7 @@ namespace Il2CppInspector
|
||||
public void SaveToFile(string pathname) {
|
||||
Position = 0;
|
||||
using (var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write))
|
||||
BaseStream.CopyTo(outFile);
|
||||
CopyTo(outFile);
|
||||
}
|
||||
|
||||
internal int Sizeof(Type type) => Sizeof(type, Version);
|
||||
|
||||
@@ -801,17 +801,9 @@ namespace Il2CppInspector
|
||||
MetadataRegistration.methodReferences = 0;
|
||||
|
||||
// Write changes to stream
|
||||
using var sw = new BinaryObjectWriter(Image.Stream.BaseStream, Image.Stream.Endianness, true);
|
||||
sw.Version = Image.Version;
|
||||
|
||||
// Set width of long (convert to sizeof(int) for 32-bit files)
|
||||
if (Image.Bits == 32) {
|
||||
sw.PrimitiveMappings.Add(typeof(long), typeof(int));
|
||||
sw.PrimitiveMappings.Add(typeof(ulong), typeof(uint));
|
||||
}
|
||||
|
||||
sw.WriteObject(Image.MapVATR(CodeRegistrationPointer), CodeRegistration);
|
||||
sw.WriteObject(Image.MapVATR(MetadataRegistrationPointer), MetadataRegistration);
|
||||
Image.WriteObject(Image.MapVATR(CodeRegistrationPointer), CodeRegistration);
|
||||
Image.WriteObject(Image.MapVATR(MetadataRegistrationPointer), MetadataRegistration);
|
||||
isModified = true;
|
||||
|
||||
StatusUpdate("Analyzing IL2CPP image");
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Il2CppInspector.Model
|
||||
public int WordSizeBytes => WordSizeBits / 8;
|
||||
|
||||
// The binary image
|
||||
public IFileFormatReader Image => Package.BinaryImage;
|
||||
public IFileFormatStream Image => Package.BinaryImage;
|
||||
|
||||
// The IL2CPP package for this application
|
||||
public Il2CppInspector Package => TypeModel.Package;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Il2CppInspector
|
||||
{
|
||||
internal class ULEB128
|
||||
{
|
||||
public static ulong Decode(IFileFormatReader next) {
|
||||
public static ulong Decode(IFileFormatStream next) {
|
||||
ulong uleb = 0;
|
||||
byte b = 0x80;
|
||||
for (var shift = 0; b >> 7 == 1; shift += 7) {
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Il2CppInspectorGUI
|
||||
try {
|
||||
OnStatusUpdate?.Invoke(this, "Processing metadata");
|
||||
|
||||
metadata = new Metadata(metadataStream, StatusUpdate);
|
||||
metadata = Metadata.FromStream(metadataStream, StatusUpdate);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@@ -150,7 +150,7 @@ namespace Il2CppInspectorGUI
|
||||
OnStatusUpdate?.Invoke(this, "Processing binary");
|
||||
|
||||
// This may throw other exceptions from the individual loaders as well
|
||||
IFileFormatReader stream = FileFormatReader.Load(binaryStream, LoadOptions, StatusUpdate);
|
||||
IFileFormatStream stream = FileFormatStream.Load(binaryStream, LoadOptions, StatusUpdate);
|
||||
if (stream == null) {
|
||||
throw new InvalidOperationException("Could not determine the binary file format");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user