/* Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com All rights reserved. */ using System; using System.Collections.Generic; using System.IO; using System.IO.IsolatedStorage; using System.Linq; using System.Reflection; using NoisyCowStudios.Bin2Object; namespace Il2CppInspector { public interface IFileFormatReader { BinaryObjectReader Stream { get; } uint NumImages { get; } IEnumerable Images { get; } IFileFormatReader this[uint index] { get; } long Position { get; set; } string Format { get; } string Arch { get; } int Bits { get; } uint GlobalOffset { get; } Dictionary GetSymbolTable(); uint[] GetFunctionTable(); U ReadMappedObject(uint uiAddr) where U : new(); U[] ReadMappedArray(uint uiAddr, int count) where U : new(); uint MapVATR(uint uiAddr); byte[] ReadBytes(int count); ulong ReadUInt64(); uint ReadUInt32(); ushort ReadUInt16(); byte ReadByte(); string ReadMappedNullTerminatedString(uint uiAddr); List ReadMappedObjectPointerArray(uint uiAddr, int count) where U : new(); } internal class FileFormatReader { // Helper method to try all defined file formats when the contents of the binary is unknown public static IFileFormatReader Load(string filename) => Load(new FileStream(filename, FileMode.Open, FileAccess.Read)); public static IFileFormatReader Load(Stream stream) { var types = Assembly.GetExecutingAssembly().DefinedTypes .Where(x => x.ImplementedInterfaces.Contains(typeof(IFileFormatReader)) && !x.IsGenericTypeDefinition); foreach (var type in types) { if (type.BaseType.GetMethod("Load", new [] {typeof(Stream)}) .Invoke(null, new object[] { stream }) is IFileFormatReader loaded) return loaded; } return null; } } internal class FileFormatReader : BinaryObjectReader, IFileFormatReader where T : FileFormatReader { public FileFormatReader(Stream stream) : base(stream) { } public BinaryObjectReader Stream => this; public uint NumImages { get; protected set; } = 1; public uint GlobalOffset { get; protected set; } public virtual string Format => throw new NotImplementedException(); public virtual string Arch => throw new NotImplementedException(); public virtual int Bits => throw new NotImplementedException(); public IEnumerable Images { get { for (uint i = 0; i < NumImages; i++) yield return this[i]; } } public static T Load(string filename) { using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read)) return Load(stream); } public static T Load(Stream stream) { // Copy the original stream in case we modify it var ms = new MemoryStream(); stream.Position = 0; stream.CopyTo(ms); ms.Position = 0; var pe = (T) Activator.CreateInstance(typeof(T), ms); return pe.Init() ? pe : null; } // Confirm file is valid and set up RVA mappings protected virtual bool Init() => throw new NotImplementedException(); // Choose a sub-binary within the image for multi-architecture binaries public virtual IFileFormatReader this[uint index] { get { if (index == 0) return this; throw new IndexOutOfRangeException("Binary image index out of bounds"); } } // Find search locations in the symbol table for Il2Cpp data public virtual Dictionary GetSymbolTable() => null; // Find search locations in the machine code for Il2Cpp data public virtual uint[] GetFunctionTable() => throw new NotImplementedException(); // Map an RVA to an offset into the file image // No mapping by default public virtual uint MapVATR(uint uiAddr) => uiAddr; // Retrieve object(s) from specified RVA(s) public U ReadMappedObject(uint uiAddr) where U : new() { return ReadObject(MapVATR(uiAddr)); } public U[] ReadMappedArray(uint uiAddr, int count) where U : new() { return ReadArray(MapVATR(uiAddr), count); } public string ReadMappedNullTerminatedString(uint uiAddr) { return ReadNullTerminatedString(MapVATR(uiAddr)); } // Reads a list of pointers, then reads each object pointed to public List ReadMappedObjectPointerArray(uint uiAddr, int count) where U : new() { var pointers = ReadMappedArray(uiAddr, count); var array = new List(); for (int i = 0; i < count; i++) array.Add(ReadMappedObject(pointers[i])); return array; } } }