diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs index 60b19c0..21e86a5 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM.cs @@ -13,9 +13,9 @@ namespace Il2CppInspector { internal class Il2CppBinaryARM : Il2CppBinary { - public Il2CppBinaryARM(IFileFormatReader stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } + public Il2CppBinaryARM(IFileFormatStream stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } - public Il2CppBinaryARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler statusCallback = null) + public Il2CppBinaryARM(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler 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 sweepThumbForAddressLoads(List func, uint baseAddress, IFileFormatReader image) { + private Dictionary sweepThumbForAddressLoads(List func, uint baseAddress, IFileFormatStream image) { // List of registers and addresses loaded into them var regs = new Dictionary(); @@ -200,7 +200,7 @@ namespace Il2CppInspector } // Get a Thumb function that ends in B.W - private List getThumbFunctionAtFileOffset(IFileFormatReader image, uint loc, uint maxLength) { + private List 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 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(pCgr, 10); // 7 instructions + 3 pointers + var insts = image.ReadArray(pCgr, 10); // 7 instructions + 3 pointers var ldrOffsets = new uint[3]; var pointers = new uint[3]; diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs index cca4186..a5d0934 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryARM64.cs @@ -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 statusCallback = null) : base(stream, statusCallback) { } + public Il2CppBinaryARM64(IFileFormatStream stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } - public Il2CppBinaryARM64(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler statusCallback = null) + public Il2CppBinaryARM64(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler 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 sweepForAddressLoads(List func, ulong baseAddress, IFileFormatReader image) { + private Dictionary sweepForAddressLoads(List func, ulong baseAddress, IFileFormatStream image) { // List of registers and addresses loaded into them var regs = new Dictionary(); @@ -118,7 +118,7 @@ namespace Il2CppInspector return regs; } - private List getFunctionAtFileOffset(IFileFormatReader image, uint loc, uint maxLength) { + private List 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 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); diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs index 7874dd2..5b2cf3e 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryX64.cs @@ -13,9 +13,9 @@ namespace Il2CppInspector { internal class Il2CppBinaryX64 : Il2CppBinary { - public Il2CppBinaryX64(IFileFormatReader stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } + public Il2CppBinaryX64(IFileFormatStream stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } - public Il2CppBinaryX64(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler statusCallback = null) + public Il2CppBinaryX64(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler 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 diff --git a/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs b/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs index e5c5393..1529966 100644 --- a/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs +++ b/Il2CppInspector.Common/Architectures/Il2CppBinaryX86.cs @@ -11,12 +11,12 @@ namespace Il2CppInspector { internal class Il2CppBinaryX86 : Il2CppBinary { - public Il2CppBinaryX86(IFileFormatReader stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } + public Il2CppBinaryX86(IFileFormatStream stream, EventHandler statusCallback = null) : base(stream, statusCallback) { } - public Il2CppBinaryX86(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler statusCallback = null) + public Il2CppBinaryX86(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler 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; diff --git a/Il2CppInspector.Common/Cpp/CppCompilerType.cs b/Il2CppInspector.Common/Cpp/CppCompilerType.cs index bbab1ed..ec301af 100644 --- a/Il2CppInspector.Common/Cpp/CppCompilerType.cs +++ b/Il2CppInspector.Common/Cpp/CppCompilerType.cs @@ -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); } } diff --git a/Il2CppInspector.Common/FileFormatReaders/AABReader.cs b/Il2CppInspector.Common/FileFormatStreams/AABReader.cs similarity index 88% rename from Il2CppInspector.Common/FileFormatReaders/AABReader.cs rename to Il2CppInspector.Common/FileFormatStreams/AABReader.cs index c863f5b..ed22e29 100644 --- a/Il2CppInspector.Common/FileFormatReaders/AABReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/AABReader.cs @@ -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 + internal class AABReader : FileFormatStream { 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(); diff --git a/Il2CppInspector.Common/FileFormatReaders/APKReader.cs b/Il2CppInspector.Common/FileFormatStreams/APKReader.cs similarity index 88% rename from Il2CppInspector.Common/FileFormatReaders/APKReader.cs rename to Il2CppInspector.Common/FileFormatStreams/APKReader.cs index 4093e6f..4869ff9 100644 --- a/Il2CppInspector.Common/FileFormatReaders/APKReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/APKReader.cs @@ -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 + internal class APKReader : FileFormatStream { 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(); diff --git a/Il2CppInspector.Common/FileFormatReaders/ElfReader.cs b/Il2CppInspector.Common/FileFormatStreams/ElfReader.cs similarity index 96% rename from Il2CppInspector.Common/FileFormatReaders/ElfReader.cs rename to Il2CppInspector.Common/FileFormatStreams/ElfReader.cs index 034a22c..4b6a435 100644 --- a/Il2CppInspector.Common/FileFormatReaders/ElfReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/ElfReader.cs @@ -17,7 +17,7 @@ namespace Il2CppInspector { internal class ElfReader32 : ElfReader { - 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 { - 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 : FileFormatReader, IElfReader + internal abstract class ElfReader : FileFormatStream, IElfReader where TWord : struct where TPHdr : Ielf_phdr, new() where TSym : Ielf_sym, new() where TConvert : IWordConverter, new() - where TReader : FileFormatReader + where TReader : FileFormatStream { 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>(); @@ -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(conv.Long(elf_header.e_phoff), elf_header.e_phnum); section_header_table = ReadArray>(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() { diff --git a/Il2CppInspector.Common/FileFormatReaders/Export.cs b/Il2CppInspector.Common/FileFormatStreams/Export.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/Export.cs rename to Il2CppInspector.Common/FileFormatStreams/Export.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs similarity index 83% rename from Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs rename to Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs index f7cc9f8..f4a8208 100644 --- a/Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FileFormatStream.cs @@ -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 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 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 TryNextLoadStrategy(); // Some images can be loaded multiple ways, eg. default, packed + IEnumerable TryNextLoadStrategy(); // Some images can be loaded multiple ways, eg. default, packed Dictionary GetSymbolTable(); uint[] GetFunctionTable(); IEnumerable GetExports(); @@ -92,24 +91,52 @@ namespace Il2CppInspector long[] ReadMappedWordArray(ulong uiAddr, int count); List ReadMappedObjectPointerArray(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(long addr, T obj); + void WriteObject(T obj); + void WriteArray(long addr, T[] array); + void WriteArray(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 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 statusCallback = null) + public static IFileFormatStream Load(string filename, LoadOptions loadOptions = null, EventHandler statusCallback = null) => Load(new FileStream(filename, FileMode.Open, FileAccess.Read), loadOptions, statusCallback); - public static IFileFormatReader Load(Stream stream, LoadOptions loadOptions = null, EventHandler statusCallback = null) { + public static IFileFormatStream Load(Stream stream, LoadOptions loadOptions = null, EventHandler 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)}, 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 : BinaryObjectReader, IFileFormatReader where T : FileFormatReader + public abstract class FileFormatStream : BinaryObjectStream, IFileFormatStream where T : FileFormatStream { - 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 Images { + public IEnumerable 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 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 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 TryNextLoadStrategy() { yield return this; } + public virtual IEnumerable TryNextLoadStrategy() { yield return this; } // Find search locations in the symbol table for Il2Cpp data public virtual Dictionary GetSymbolTable() => new Dictionary(); diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Elf.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs rename to Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Elf.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/MachO.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs rename to Il2CppInspector.Common/FileFormatStreams/FormatLayouts/MachO.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/PE.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/PE.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/FormatLayouts/PE.cs rename to Il2CppInspector.Common/FileFormatStreams/FormatLayouts/PE.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/SElf.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/SElf.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/FormatLayouts/SElf.cs rename to Il2CppInspector.Common/FileFormatStreams/FormatLayouts/SElf.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/UB.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/UB.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/FormatLayouts/UB.cs rename to Il2CppInspector.Common/FileFormatStreams/FormatLayouts/UB.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/LoadOptions.cs b/Il2CppInspector.Common/FileFormatStreams/LoadOptions.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/LoadOptions.cs rename to Il2CppInspector.Common/FileFormatStreams/LoadOptions.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/MachOReader.cs b/Il2CppInspector.Common/FileFormatStreams/MachOReader.cs similarity index 97% rename from Il2CppInspector.Common/FileFormatReaders/MachOReader.cs rename to Il2CppInspector.Common/FileFormatStreams/MachOReader.cs index de95eca..9f78c42 100644 --- a/Il2CppInspector.Common/FileFormatReaders/MachOReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/MachOReader.cs @@ -15,8 +15,6 @@ namespace Il2CppInspector { internal class MachOReader32 : MachOReader { - 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 { - 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 : FileFormatReader + internal abstract class MachOReader : FileFormatStream where TWord : struct - where TReader : FileFormatReader + where TReader : FileFormatStream where TConvert : IWordConverter, new() { private readonly TConvert conv = new TConvert(); @@ -75,8 +71,6 @@ namespace Il2CppInspector private List exports = new List(); - protected MachOReader(Stream stream) : base(stream) { } - public override string DefaultFilename => "app"; public override string Format => "Mach-O " + (Bits == 32 ? "32-bit" : "64-bit"); diff --git a/Il2CppInspector.Common/FileFormatReaders/PEReader.cs b/Il2CppInspector.Common/FileFormatStreams/PEReader.cs similarity index 95% rename from Il2CppInspector.Common/FileFormatReaders/PEReader.cs rename to Il2CppInspector.Common/FileFormatStreams/PEReader.cs index 51b588c..bc07128 100644 --- a/Il2CppInspector.Common/FileFormatReaders/PEReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/PEReader.cs @@ -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 + internal class PEReader : FileFormatStream { [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 TryNextLoadStrategy() { + public override IEnumerable TryNextLoadStrategy() { // First load strategy: the regular file yield return this; diff --git a/Il2CppInspector.Common/FileFormatReaders/ProcessMapReader.cs b/Il2CppInspector.Common/FileFormatStreams/ProcessMapReader.cs similarity index 93% rename from Il2CppInspector.Common/FileFormatReaders/ProcessMapReader.cs rename to Il2CppInspector.Common/FileFormatStreams/ProcessMapReader.cs index 8854a24..81d6775 100644 --- a/Il2CppInspector.Common/FileFormatReaders/ProcessMapReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/ProcessMapReader.cs @@ -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 + internal class ProcessMapReader : FileFormatStream { 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); } } } diff --git a/Il2CppInspector.Common/FileFormatReaders/SElfReader.cs b/Il2CppInspector.Common/FileFormatStreams/SElfReader.cs similarity index 97% rename from Il2CppInspector.Common/FileFormatReaders/SElfReader.cs rename to Il2CppInspector.Common/FileFormatStreams/SElfReader.cs index 70a3ce3..52fee43 100644 --- a/Il2CppInspector.Common/FileFormatReaders/SElfReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/SElfReader.cs @@ -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 + internal class SElfReader : FileFormatStream { - public SElfReader(Stream stream) : base(stream) { } - public override string DefaultFilename => "libil2cpp.so"; public override string Format => sceData.ProductType == (ulong) SElfExInfoTypes.PTYPE_FAKE? "FSELF" : "SELF"; diff --git a/Il2CppInspector.Common/FileFormatReaders/Section.cs b/Il2CppInspector.Common/FileFormatStreams/Section.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/Section.cs rename to Il2CppInspector.Common/FileFormatStreams/Section.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/Symbol.cs b/Il2CppInspector.Common/FileFormatStreams/Symbol.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/Symbol.cs rename to Il2CppInspector.Common/FileFormatStreams/Symbol.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/UBReader.cs b/Il2CppInspector.Common/FileFormatStreams/UBReader.cs similarity index 82% rename from Il2CppInspector.Common/FileFormatReaders/UBReader.cs rename to Il2CppInspector.Common/FileFormatStreams/UBReader.cs index 17d91cd..027687f 100644 --- a/Il2CppInspector.Common/FileFormatReaders/UBReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/UBReader.cs @@ -9,12 +9,10 @@ using NoisyCowStudios.Bin2Object; namespace Il2CppInspector { - internal class UBReader : FileFormatReader + internal class UBReader : FileFormatStream { 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); } } } diff --git a/Il2CppInspector.Common/FileFormatReaders/WordConversions.cs b/Il2CppInspector.Common/FileFormatStreams/WordConversions.cs similarity index 100% rename from Il2CppInspector.Common/FileFormatReaders/WordConversions.cs rename to Il2CppInspector.Common/FileFormatStreams/WordConversions.cs diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs index 6698f2d..c08ac77 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs @@ -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 APIExports { get; } = new Dictionary(); @@ -92,7 +92,7 @@ namespace Il2CppInspector private bool isModified = false; public bool IsModified => Image.IsModified || isModified; - protected Il2CppBinary(IFileFormatReader stream, EventHandler statusCallback = null) { + protected Il2CppBinary(IFileFormatStream stream, EventHandler statusCallback = null) { Image = stream; OnStatusUpdate = statusCallback; @@ -100,7 +100,7 @@ namespace Il2CppInspector DiscoverAPIExports(); } - protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration, EventHandler statusCallback = null) { + protected Il2CppBinary(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler 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 statusCallback) { + private static Il2CppBinary LoadImpl(IFileFormatStream stream, EventHandler 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 statusCallback = null) { + public static Il2CppBinary Load(IFileFormatStream stream, double metadataVersion, EventHandler 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 statusCallback = null) { + public static Il2CppBinary Load(IFileFormatStream stream, Metadata metadata, EventHandler 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) { diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index 7960f93..b65ee5a 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -66,7 +66,7 @@ namespace Il2CppInspector public Dictionary 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(); 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); diff --git a/Il2CppInspector.Common/IL2CPP/Metadata.cs b/Il2CppInspector.Common/IL2CPP/Metadata.cs index f4e2317..8040dda 100644 --- a/Il2CppInspector.Common/IL2CPP/Metadata.cs +++ b/Il2CppInspector.Common/IL2CPP/Metadata.cs @@ -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 Strings { get; } = new Dictionary(); // 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 statusCallback = null) : base(stream) + // Status update callback + private EventHandler 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 statusCallback = null) { + var metadata = new Metadata(statusCallback); + stream.Position = 0; + stream.CopyTo(metadata); + metadata.Position = 0; + metadata.Initialize(); + return metadata; + } + + private Metadata(EventHandler 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; - foreach (var str in Strings.OrderBy(s => s.Key)) - sw.WriteNullTerminatedString(str.Value); - sw.Flush(); - } + Position = Header.stringOffset; + foreach (var str in Strings.OrderBy(s => s.Key)) + 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); diff --git a/Il2CppInspector.Common/IL2CPP/ReconstructMetadata.cs b/Il2CppInspector.Common/IL2CPP/ReconstructMetadata.cs index 7673808..069c12a 100644 --- a/Il2CppInspector.Common/IL2CPP/ReconstructMetadata.cs +++ b/Il2CppInspector.Common/IL2CPP/ReconstructMetadata.cs @@ -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"); diff --git a/Il2CppInspector.Common/Model/AppModel.cs b/Il2CppInspector.Common/Model/AppModel.cs index 886bcc4..70e46ee 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -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; diff --git a/Il2CppInspector.Common/ULEB128.cs b/Il2CppInspector.Common/ULEB128.cs index 2d0da25..1d61d1d 100644 --- a/Il2CppInspector.Common/ULEB128.cs +++ b/Il2CppInspector.Common/ULEB128.cs @@ -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) { diff --git a/Il2CppInspector.GUI/App.xaml.cs b/Il2CppInspector.GUI/App.xaml.cs index c78e7d3..47b1da6 100644 --- a/Il2CppInspector.GUI/App.xaml.cs +++ b/Il2CppInspector.GUI/App.xaml.cs @@ -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"); }