diff --git a/Il2CppDumper/Il2CppDumper.cs b/Il2CppDumper/Il2CppDumper.cs index 8e46e1e..da11110 100644 --- a/Il2CppDumper/Il2CppDumper.cs +++ b/Il2CppDumper/Il2CppDumper.cs @@ -8,9 +8,9 @@ namespace Il2CppInspector { public class Il2CppDumper { - private readonly Il2CppProcessor il2cpp; + private readonly Il2CppInspector il2cpp; - public Il2CppDumper(Il2CppProcessor proc) { + public Il2CppDumper(Il2CppInspector proc) { il2cpp = proc; } @@ -100,7 +100,7 @@ namespace Il2CppInspector } if (methodDef.methodIndex >= 0) writer.Write("); // {0:x} - {1}\n", - il2cpp.Code.PtrCodeRegistration.methodPointers[methodDef.methodIndex], + il2cpp.Binary.MethodPointers[methodDef.methodIndex], methodDef.methodIndex); else writer.Write("); // 0 - -1\n"); diff --git a/Il2CppDumper/Program.cs b/Il2CppDumper/Program.cs index 144f2b4..c34f702 100644 --- a/Il2CppDumper/Program.cs +++ b/Il2CppDumper/Program.cs @@ -40,13 +40,13 @@ namespace Il2CppInspector } // Analyze data - var il2cppProcessors = Il2CppProcessor.LoadFromFile(imageFile, metaFile); - if (il2cppProcessors == null) + var il2cppInspectors = Il2CppInspector.LoadFromFile(imageFile, metaFile); + if (il2cppInspectors == null) Environment.Exit(1); // Write output file int i = 0; - foreach (var il2cpp in il2cppProcessors) + foreach (var il2cpp in il2cppInspectors) new Il2CppDumper(il2cpp).WriteFile(outFile + (i++ > 0 ? "-" + (i-1) : "")); } } diff --git a/Il2CppInspector/FileFormatReader.cs b/Il2CppInspector/FileFormatReader.cs index 2e71032..6883040 100644 --- a/Il2CppInspector/FileFormatReader.cs +++ b/Il2CppInspector/FileFormatReader.cs @@ -21,11 +21,11 @@ namespace Il2CppInspector long Position { get; set; } string Arch { get; } uint GlobalOffset { get; } - uint[] GetSearchLocations(); + uint[] GetFunctionTable(); U ReadMappedObject(uint uiAddr) where U : new(); U[] ReadMappedArray(uint uiAddr, int count) where U : new(); uint MapVATR(uint uiAddr); - void FinalizeInit(Il2CppReader il2cpp); + void FinalizeInit(Il2CppBinary il2cpp); byte[] ReadBytes(int count); ulong ReadUInt64(); @@ -77,7 +77,7 @@ namespace Il2CppInspector } // Find search locations in the machine code for Il2Cpp data - public virtual uint[] GetSearchLocations() => throw new NotImplementedException(); + public virtual uint[] GetFunctionTable() => throw new NotImplementedException(); // Map an RVA to an offset into the file image // No mapping by default @@ -93,6 +93,6 @@ namespace Il2CppInspector } // Perform file format-based post-load manipulations to the IL2Cpp data - public virtual void FinalizeInit(Il2CppReader il2cpp) { } + public virtual void FinalizeInit(Il2CppBinary il2cpp) { } } } \ No newline at end of file diff --git a/Il2CppInspector/FileFormatReaders/ElfReader.cs b/Il2CppInspector/FileFormatReaders/ElfReader.cs index c0af446..94b918a 100644 --- a/Il2CppInspector/FileFormatReaders/ElfReader.cs +++ b/Il2CppInspector/FileFormatReaders/ElfReader.cs @@ -47,7 +47,7 @@ namespace Il2CppInspector return true; } - public override uint[] GetSearchLocations() { + public override uint[] GetFunctionTable() { // Find dynamic section var dynamic = new elf_32_shdr(); var PT_DYNAMIC = program_table_element.First(x => x.p_type == 2u); diff --git a/Il2CppInspector/FileFormatReaders/MachOReader.cs b/Il2CppInspector/FileFormatReaders/MachOReader.cs index bad7863..aeb8016 100644 --- a/Il2CppInspector/FileFormatReaders/MachOReader.cs +++ b/Il2CppInspector/FileFormatReaders/MachOReader.cs @@ -113,7 +113,7 @@ namespace Il2CppInspector return true; } - public override uint[] GetSearchLocations() { + public override uint[] GetFunctionTable() { Position = pFuncTable; var functionPointers = new List(); @@ -139,10 +139,9 @@ namespace Il2CppInspector return functionPointers.ToArray(); } - public override void FinalizeInit(Il2CppReader il2cpp) { + public override void FinalizeInit(Il2CppBinary il2cpp) { // Mach-O function pointers have an annoying habit of being 1-off - il2cpp.PtrCodeRegistration.methodPointers = - il2cpp.PtrCodeRegistration.methodPointers.Select(x => x - 1).ToArray(); + il2cpp.MethodPointers = il2cpp.MethodPointers.Select(x => x - 1).ToArray(); } public override uint MapVATR(uint uiAddr) { diff --git a/Il2CppInspector/FileFormatReaders/PEReader.cs b/Il2CppInspector/FileFormatReaders/PEReader.cs index f820bce..dd51a52 100644 --- a/Il2CppInspector/FileFormatReaders/PEReader.cs +++ b/Il2CppInspector/FileFormatReaders/PEReader.cs @@ -80,7 +80,7 @@ namespace Il2CppInspector return true; } - public override uint[] GetSearchLocations() { + public override uint[] GetFunctionTable() { Position = pFuncTable; var addrs = new List(); uint addr; diff --git a/Il2CppInspector/Il2CppBinary.cs b/Il2CppInspector/Il2CppBinary.cs new file mode 100644 index 0000000..a15d219 --- /dev/null +++ b/Il2CppInspector/Il2CppBinary.cs @@ -0,0 +1,62 @@ +/* + Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper + Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + + All rights reserved. +*/ + +using System.Collections.Generic; + +namespace Il2CppInspector +{ + public abstract class Il2CppBinary + { + public IFileFormatReader Image { get; } + + public Il2CppCodeRegistration CodeRegistration { get; protected set; } + public Il2CppMetadataRegistration MetadataRegistration { get; protected set; } + + public uint[] MethodPointers { get; set; } + public int[] FieldOffsets { get; private set; } + public List Types { get; } = new List(); + + protected Il2CppBinary(IFileFormatReader stream) { + Image = stream; + } + + protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) { + Image = stream; + Configure(Image, codeRegistration, metadataRegistration); + } + + // Architecture-specific search function + protected abstract (uint, uint) ConsiderCode(uint loc, uint globalOffset); + + // Check all search locations + public bool Initialize(int version, uint imageIndex = 0) { + var subImage = Image[imageIndex]; + Image.Stream.Version = version; + var addrs = subImage.GetFunctionTable(); + foreach (var loc in addrs) + if (loc != 0) { + var (code, metadata) = ConsiderCode(loc, Image.GlobalOffset); + if (code != 0) { + Configure(subImage, code, metadata); + subImage.FinalizeInit(this); + return true; + } + } + return false; + } + + private void Configure(IFileFormatReader image, uint codeRegistration, uint metadataRegistration) { + CodeRegistration = image.ReadMappedObject(codeRegistration); + MetadataRegistration = image.ReadMappedObject(metadataRegistration); + MethodPointers = image.ReadMappedArray(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount); + FieldOffsets = image.ReadMappedArray(MetadataRegistration.pfieldOffsets, MetadataRegistration.fieldOffsetsCount); + var types = image.ReadMappedArray(MetadataRegistration.ptypes, MetadataRegistration.typesCount); + for (int i = 0; i < MetadataRegistration.typesCount; i++) + Types.Add(image.ReadMappedObject(types[i])); + } + } +} diff --git a/Il2CppInspector/Il2CppReaderARM.cs b/Il2CppInspector/Il2CppBinaryARM.cs similarity index 93% rename from Il2CppInspector/Il2CppReaderARM.cs rename to Il2CppInspector/Il2CppBinaryARM.cs index 909252f..484215f 100644 --- a/Il2CppInspector/Il2CppReaderARM.cs +++ b/Il2CppInspector/Il2CppBinaryARM.cs @@ -9,13 +9,13 @@ using System.Linq; namespace Il2CppInspector { - internal class Il2CppReaderARM : Il2CppReader + internal class Il2CppBinaryARM : Il2CppBinary { - public Il2CppReaderARM(IFileFormatReader stream) : base(stream) { } + public Il2CppBinaryARM(IFileFormatReader stream) : base(stream) { } - public Il2CppReaderARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { } + public Il2CppBinaryARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { } - protected override (uint, uint) Search(uint loc, uint globalOffset) { + protected override (uint, uint) ConsiderCode(uint loc, uint globalOffset) { // Assembly bytes to search for at start of each function uint metadataRegistration, codeRegistration; diff --git a/Il2CppInspector/Il2CppClasses.cs b/Il2CppInspector/Il2CppBinaryClasses.cs similarity index 73% rename from Il2CppInspector/Il2CppClasses.cs rename to Il2CppInspector/Il2CppBinaryClasses.cs index 51b74ef..62edc7d 100644 --- a/Il2CppInspector/Il2CppClasses.cs +++ b/Il2CppInspector/Il2CppBinaryClasses.cs @@ -49,12 +49,6 @@ namespace Il2CppInspector public uint interopDataCount; [Version(Min = 23)] public uint interopData; - - // Properties - public uint[] methodPointers - { - get; set; - } } #pragma warning disable CS0649 @@ -78,16 +72,6 @@ namespace Il2CppInspector public uint typeDefinitionsSizes; public uint metadataUsagesCount; public uint metadataUsages; - - public int[] fieldOffsets - { - get; set; - } - - public Il2CppType[] types - { - get; set; - } } #pragma warning restore CS0649 @@ -136,68 +120,13 @@ namespace Il2CppInspector public class Il2CppType { public uint datapoint; - public Anonymous data { get; set; } public uint bits; - public uint attrs { get; set; } - public Il2CppTypeEnum type { get; set; } - public uint num_mods { get; set; } - public uint byref { get; set; } - public uint pinned { get; set; } - public void Init() - { - var str = Convert.ToString(bits, 2); - if (str.Length != 32) - { - str = new string(Enumerable.Repeat('0', 32 - str.Length).Concat(str.ToCharArray()).ToArray()); - } - attrs = Convert.ToUInt32(str.Substring(16, 16), 2); - type = (Il2CppTypeEnum)Convert.ToInt32(str.Substring(8, 8), 2); - num_mods = Convert.ToUInt32(str.Substring(2, 6), 2); - byref = Convert.ToUInt32(str.Substring(1, 1), 2); - pinned = Convert.ToUInt32(str.Substring(0, 1), 2); - data = new Anonymous() { dummy = datapoint }; - } - - public class Anonymous - { - public uint dummy; - public int klassIndex - { - get - { - return (int)dummy; - } - } - public uint type - { - get - { - return dummy; - } - } - public uint array - { - get - { - return dummy; - } - } - public int genericParameterIndex - { - get - { - return (int)dummy; - } - } - public uint generic_class - { - get - { - return dummy; - } - } - } + public uint attrs => bits & 0xffff; + public Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff); + public uint num_mods => (bits >> 24) & 0x3f; + public bool byref => ((bits >> 30) & 1) == 1; + public bool pinned => (bits >> 31) == 1; } public class Il2CppGenericClass diff --git a/Il2CppInspector/Il2CppReaderX86.cs b/Il2CppInspector/Il2CppBinaryX86.cs similarity index 90% rename from Il2CppInspector/Il2CppReaderX86.cs rename to Il2CppInspector/Il2CppBinaryX86.cs index b6d0409..6a49747 100644 --- a/Il2CppInspector/Il2CppReaderX86.cs +++ b/Il2CppInspector/Il2CppBinaryX86.cs @@ -8,11 +8,11 @@ using System.Linq; namespace Il2CppInspector { - internal class Il2CppReaderX86 : Il2CppReader + internal class Il2CppBinaryX86 : Il2CppBinary { - public Il2CppReaderX86(IFileFormatReader stream) : base(stream) { } - public Il2CppReaderX86(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { } - protected override (uint, uint) Search(uint loc, uint globalOffset) { + public Il2CppBinaryX86(IFileFormatReader stream) : base(stream) { } + public Il2CppBinaryX86(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { } + protected override (uint, uint) ConsiderCode(uint loc, uint globalOffset) { uint funcPtr, metadata, code; ushort opcode; diff --git a/Il2CppInspector/Il2CppProcessor.cs b/Il2CppInspector/Il2CppInspector.cs similarity index 78% rename from Il2CppInspector/Il2CppProcessor.cs rename to Il2CppInspector/Il2CppInspector.cs index b250092..dcc0ce3 100644 --- a/Il2CppInspector/Il2CppProcessor.cs +++ b/Il2CppInspector/Il2CppInspector.cs @@ -12,17 +12,17 @@ using System.Text; namespace Il2CppInspector { - public class Il2CppProcessor + public class Il2CppInspector { - public Il2CppReader Code { get; } + public Il2CppBinary Binary { get; } public Metadata Metadata { get; } - public Il2CppProcessor(Il2CppReader code, Metadata metadata) { - Code = code; + public Il2CppInspector(Il2CppBinary binary, Metadata metadata) { + Binary = binary; Metadata = metadata; } - public static List LoadFromFile(string codeFile, string metadataFile) { + public static List LoadFromFile(string codeFile, string metadataFile) { // Load the metadata file Metadata metadata; try { @@ -33,7 +33,7 @@ namespace Il2CppInspector return null; } - // Load the il2cpp code file (try ELF and PE) + // Load the il2cpp code file (try ELF, PE, Mach-O and Universal Binary) var memoryStream = new MemoryStream(File.ReadAllBytes(codeFile)); IFileFormatReader stream = (((IFileFormatReader) ElfReader.Load(memoryStream) ?? @@ -45,17 +45,18 @@ namespace Il2CppInspector return null; } - var processors = new List(); + // Multi-image binaries may contain more than one Il2Cpp image + var processors = new List(); foreach (var image in stream.Images) { - Il2CppReader il2cpp; + Il2CppBinary binary; // We are currently supporting x86 and ARM architectures switch (image.Arch) { case "x86": - il2cpp = new Il2CppReaderX86(image); + binary = new Il2CppBinaryX86(image); break; case "ARM": - il2cpp = new Il2CppReaderARM(image); + binary = new Il2CppBinaryARM(image); break; default: Console.Error.WriteLine("Unsupported architecture"); @@ -63,11 +64,11 @@ namespace Il2CppInspector } // Find code and metadata regions - if (!il2cpp.Load(metadata.Version)) { + if (!binary.Initialize(metadata.Version)) { Console.Error.WriteLine("Could not process IL2CPP image"); } else { - processors.Add(new Il2CppProcessor(il2cpp, metadata)); + processors.Add(new Il2CppInspector(binary, metadata)); } } return processors; @@ -76,32 +77,29 @@ namespace Il2CppInspector public string GetTypeName(Il2CppType pType) { string ret; if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS || pType.type == Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) { - Il2CppTypeDefinition klass = Metadata.Types[pType.data.klassIndex]; + Il2CppTypeDefinition klass = Metadata.Types[pType.datapoint]; ret = Metadata.Strings[klass.nameIndex]; } else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { - Il2CppGenericClass generic_class = Code.Image.ReadMappedObject(pType.data.generic_class); + Il2CppGenericClass generic_class = Binary.Image.ReadMappedObject(pType.datapoint); Il2CppTypeDefinition pMainDef = Metadata.Types[generic_class.typeDefinitionIndex]; ret = Metadata.Strings[pMainDef.nameIndex]; var typeNames = new List(); - Il2CppGenericInst pInst = Code.Image.ReadMappedObject(generic_class.context.class_inst); - var pointers = Code.Image.ReadMappedArray(pInst.type_argv, (int)pInst.type_argc); + Il2CppGenericInst pInst = Binary.Image.ReadMappedObject(generic_class.context.class_inst); + var pointers = Binary.Image.ReadMappedArray(pInst.type_argv, (int)pInst.type_argc); for (int i = 0; i < pInst.type_argc; ++i) { - var pOriType = Code.Image.ReadMappedObject(pointers[i]); - pOriType.Init(); + var pOriType = Binary.Image.ReadMappedObject(pointers[i]); typeNames.Add(GetTypeName(pOriType)); } ret += $"<{string.Join(", ", typeNames)}>"; } else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) { - Il2CppArrayType arrayType = Code.Image.ReadMappedObject(pType.data.array); - var type = Code.Image.ReadMappedObject(arrayType.etype); - type.Init(); + Il2CppArrayType arrayType = Binary.Image.ReadMappedObject(pType.datapoint); + var type = Binary.Image.ReadMappedObject(arrayType.etype); ret = $"{GetTypeName(type)}[]"; } else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { - var type = Code.Image.ReadMappedObject(pType.data.type); - type.Init(); + var type = Binary.Image.ReadMappedObject(pType.datapoint); ret = $"{GetTypeName(type)}[]"; } else { @@ -114,7 +112,7 @@ namespace Il2CppInspector } public Il2CppType GetTypeFromTypeIndex(int idx) { - return Code.PtrMetadataRegistration.types[idx]; + return Binary.Types[idx]; } public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) { @@ -123,19 +121,19 @@ namespace Il2CppInspector // Some variants of 21 also use an array of pointers if (Metadata.Version == 21) { - var f = Code.PtrMetadataRegistration.fieldOffsets; + var f = Binary.FieldOffsets; fieldOffsetsArePointers = (f[0] == 0 && f[1] == 0 && f[2] == 0 && f[3] == 0 && f[4] == 0 && f[5] > 0); } // All older versions use values directly in the array if (!fieldOffsetsArePointers) { var typeDef = Metadata.Types[typeIndex]; - return Code.PtrMetadataRegistration.fieldOffsets[typeDef.fieldStart + fieldIndexInType]; + return Binary.FieldOffsets[typeDef.fieldStart + fieldIndexInType]; } - var ptr = Code.PtrMetadataRegistration.fieldOffsets[typeIndex]; - Code.Image.Stream.Position = Code.Image.MapVATR((uint)ptr) + 4 * fieldIndexInType; - return Code.Image.Stream.ReadInt32(); + var ptr = Binary.FieldOffsets[typeIndex]; + Binary.Image.Stream.Position = Binary.Image.MapVATR((uint)ptr) + 4 * fieldIndexInType; + return Binary.Image.Stream.ReadInt32(); } diff --git a/Il2CppInspector/Il2CppReader.cs b/Il2CppInspector/Il2CppReader.cs deleted file mode 100644 index e014d1a..0000000 --- a/Il2CppInspector/Il2CppReader.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper - Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com - - All rights reserved. -*/ - -namespace Il2CppInspector -{ - public abstract class Il2CppReader - { - public IFileFormatReader Image { get; } - - protected Il2CppReader(IFileFormatReader stream) { - Image = stream; - } - - protected Il2CppReader(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) { - Image = stream; - Configure(Image, codeRegistration, metadataRegistration); - } - - public Il2CppCodeRegistration PtrCodeRegistration { get; protected set; } - public Il2CppMetadataRegistration PtrMetadataRegistration { get; protected set; } - - // Architecture-specific search function - protected abstract (uint, uint) Search(uint loc, uint globalOffset); - - // Check all search locations - public bool Load(int version, uint imageIndex = 0) { - var subImage = Image[imageIndex]; - Image.Stream.Version = version; - var addrs = subImage.GetSearchLocations(); - foreach (var loc in addrs) - if (loc != 0) { - var (code, metadata) = Search(loc, Image.GlobalOffset); - if (code != 0) { - Configure(subImage, code, metadata); - subImage.FinalizeInit(this); - return true; - } - } - return false; - } - - private void Configure(IFileFormatReader image, uint codeRegistration, uint metadataRegistration) { - PtrCodeRegistration = image.ReadMappedObject(codeRegistration); - PtrMetadataRegistration = image.ReadMappedObject(metadataRegistration); - PtrCodeRegistration.methodPointers = image.ReadMappedArray(PtrCodeRegistration.pmethodPointers, - (int) PtrCodeRegistration.methodPointersCount); - PtrMetadataRegistration.fieldOffsets = image.ReadMappedArray(PtrMetadataRegistration.pfieldOffsets, - PtrMetadataRegistration.fieldOffsetsCount); - var types = image.ReadMappedArray(PtrMetadataRegistration.ptypes, PtrMetadataRegistration.typesCount); - PtrMetadataRegistration.types = new Il2CppType[PtrMetadataRegistration.typesCount]; - for (int i = 0; i < PtrMetadataRegistration.typesCount; ++i) { - PtrMetadataRegistration.types[i] = image.ReadMappedObject(types[i]); - PtrMetadataRegistration.types[i].Init(); - } - } - } -}