diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ee39077
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "Bin2Object"]
+ path = Bin2Object
+ url = https://github.com/djkaty/Bin2Object
diff --git a/Bin2Object b/Bin2Object
new file mode 160000
index 0000000..8cb9cc9
--- /dev/null
+++ b/Bin2Object
@@ -0,0 +1 @@
+Subproject commit 8cb9cc95df417f09e460dc027ff39e24f8284ee3
diff --git a/Il2CppDumper/Il2CppDumper.cs b/Il2CppDumper/Il2CppDumper.cs
new file mode 100644
index 0000000..397af3d
--- /dev/null
+++ b/Il2CppDumper/Il2CppDumper.cs
@@ -0,0 +1,161 @@
+// Copyright (c) 2017 Katy Coe - https://www.djkaty.com - https://github.com/djlaty
+// All rights reserved
+
+using System.IO;
+using System.Text;
+
+namespace Il2CppInspector
+{
+ public class Il2CppDumper
+ {
+ private readonly Il2CppProcessor il2cpp;
+
+ public Il2CppDumper(Il2CppProcessor proc) {
+ il2cpp = proc;
+ }
+
+ public void WriteFile(string outFile) {
+ using (var writer = new StreamWriter(new FileStream(outFile, FileMode.Create))) {
+ var metadata = il2cpp.Metadata;
+
+ for (int imageIndex = 0; imageIndex < metadata.Images.Length; imageIndex++) {
+ var imageDef = metadata.Images[imageIndex];
+ writer.Write($"// Image {imageIndex}: {metadata.GetImageName(imageDef)} - {imageDef.typeStart}\n");
+ }
+ for (int idx = 0; idx < metadata.Types.Length; ++idx) {
+ var typeDef = metadata.Types[idx];
+ writer.Write($"// Namespace: {metadata.GetTypeNamespace(typeDef)}\n");
+ if ((typeDef.flags & DefineConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0)
+ writer.Write("[Serializable]\n");
+ if ((typeDef.flags & DefineConstants.TYPE_ATTRIBUTE_VISIBILITY_MASK) ==
+ DefineConstants.TYPE_ATTRIBUTE_PUBLIC)
+ writer.Write("public ");
+ if ((typeDef.flags & DefineConstants.TYPE_ATTRIBUTE_ABSTRACT) != 0)
+ writer.Write("abstract ");
+ if ((typeDef.flags & DefineConstants.TYPE_ATTRIBUTE_SEALED) != 0)
+ writer.Write("sealed ");
+ if ((typeDef.flags & DefineConstants.TYPE_ATTRIBUTE_INTERFACE) != 0)
+ writer.Write("interface ");
+ else
+ writer.Write("class ");
+ writer.Write($"{metadata.GetTypeName(typeDef)} // TypeDefIndex: {idx}\n{{\n");
+ writer.Write("\t// Fields\n");
+ var fieldEnd = typeDef.fieldStart + typeDef.field_count;
+ for (int i = typeDef.fieldStart; i < fieldEnd; ++i) {
+ var pField = metadata.Fields[i];
+ var pType = il2cpp.Code.GetTypeFromTypeIndex(pField.typeIndex);
+ var pDefault = metadata.GetFieldDefaultFromIndex(i);
+ writer.Write("\t");
+ if ((pType.attrs & DefineConstants.FIELD_ATTRIBUTE_PRIVATE) ==
+ DefineConstants.FIELD_ATTRIBUTE_PRIVATE)
+ writer.Write("private ");
+ if ((pType.attrs & DefineConstants.FIELD_ATTRIBUTE_PUBLIC) ==
+ DefineConstants.FIELD_ATTRIBUTE_PUBLIC)
+ writer.Write("public ");
+ if ((pType.attrs & DefineConstants.FIELD_ATTRIBUTE_STATIC) != 0)
+ writer.Write("static ");
+ if ((pType.attrs & DefineConstants.FIELD_ATTRIBUTE_INIT_ONLY) != 0)
+ writer.Write("readonly ");
+ writer.Write($"{il2cpp.GetTypeName(pType)} {metadata.GetString(pField.nameIndex)}");
+ if (pDefault != null && pDefault.dataIndex != -1) {
+ var pointer = metadata.GetDefaultValueFromIndex(pDefault.dataIndex);
+ Il2CppType pTypeToUse = il2cpp.Code.GetTypeFromTypeIndex(pDefault.typeIndex);
+ if (pointer > 0) {
+ metadata.Position = pointer;
+ object multi = null;
+ switch (pTypeToUse.type) {
+ case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
+ multi = metadata.ReadBoolean();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_U1:
+ case Il2CppTypeEnum.IL2CPP_TYPE_I1:
+ multi = metadata.ReadByte();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
+ multi = metadata.ReadChar();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_U2:
+ multi = metadata.ReadUInt16();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_I2:
+ multi = metadata.ReadInt16();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_U4:
+ multi = metadata.ReadUInt32();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_I4:
+ multi = metadata.ReadInt32();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_U8:
+ multi = metadata.ReadUInt64();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_I8:
+ multi = metadata.ReadInt64();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_R4:
+ multi = metadata.ReadSingle();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_R8:
+ multi = metadata.ReadDouble();
+ break;
+ case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
+ var uiLen = metadata.ReadInt32();
+ multi = Encoding.UTF8.GetString(metadata.ReadBytes(uiLen));
+ break;
+ }
+ if (multi is string)
+ writer.Write($" = \"{multi}\"");
+ else if (multi != null)
+ writer.Write($" = {multi}");
+ }
+ }
+ writer.Write("; // 0x{0:x}\n",
+ il2cpp.Code.GetFieldOffsetFromIndex(idx, i - typeDef.fieldStart));
+ }
+ writer.Write("\t// Methods\n");
+ var methodEnd = typeDef.methodStart + typeDef.method_count;
+ for (int i = typeDef.methodStart; i < methodEnd; ++i) {
+ var methodDef = metadata.Methods[i];
+ writer.Write("\t");
+ Il2CppType pReturnType = il2cpp.Code.GetTypeFromTypeIndex(methodDef.returnType);
+ if ((methodDef.flags & DefineConstants.METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) ==
+ DefineConstants.METHOD_ATTRIBUTE_PRIVATE)
+ writer.Write("private ");
+ if ((methodDef.flags & DefineConstants.METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) ==
+ DefineConstants.METHOD_ATTRIBUTE_PUBLIC)
+ writer.Write("public ");
+ if ((methodDef.flags & DefineConstants.METHOD_ATTRIBUTE_VIRTUAL) != 0)
+ writer.Write("virtual ");
+ if ((methodDef.flags & DefineConstants.METHOD_ATTRIBUTE_STATIC) != 0)
+ writer.Write("static ");
+
+ writer.Write($"{il2cpp.GetTypeName(pReturnType)} {metadata.GetString(methodDef.nameIndex)}(");
+ for (int j = 0; j < methodDef.parameterCount; ++j) {
+ Il2CppParameterDefinition pParam = metadata.parameterDefs[methodDef.parameterStart + j];
+ string szParamName = metadata.GetString(pParam.nameIndex);
+ Il2CppType pType = il2cpp.Code.GetTypeFromTypeIndex(pParam.typeIndex);
+ string szTypeName = il2cpp.GetTypeName(pType);
+ if ((pType.attrs & DefineConstants.PARAM_ATTRIBUTE_OPTIONAL) != 0)
+ writer.Write("optional ");
+ if ((pType.attrs & DefineConstants.PARAM_ATTRIBUTE_OUT) != 0)
+ writer.Write("out ");
+ if (j != methodDef.parameterCount - 1) {
+ writer.Write($"{szTypeName} {szParamName}, ");
+ }
+ else {
+ writer.Write($"{szTypeName} {szParamName}");
+ }
+ }
+ if (methodDef.methodIndex >= 0)
+ writer.Write("); // {0:x} - {1}\n",
+ il2cpp.Code.PtrCodeRegistration.methodPointers[methodDef.methodIndex],
+ methodDef.methodIndex);
+ else
+ writer.Write("); // 0 - -1\n");
+ }
+ writer.Write("}\n");
+ }
+ }
+ }
+ }
+}
diff --git a/Il2CppDumper/Il2CppDumper.csproj b/Il2CppDumper/Il2CppDumper.csproj
new file mode 100644
index 0000000..40bbc13
--- /dev/null
+++ b/Il2CppDumper/Il2CppDumper.csproj
@@ -0,0 +1,12 @@
+
+
+
+ Exe
+ netcoreapp1.1
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Il2CppDumper/Program.cs b/Il2CppDumper/Program.cs
new file mode 100644
index 0000000..b9d2b17
--- /dev/null
+++ b/Il2CppDumper/Program.cs
@@ -0,0 +1,52 @@
+// Copyright (c) 2017 Katy Coe - https://www.djkaty.com - https://github.com/djlaty
+// All rights reserved
+
+using System;
+using System.IO;
+
+namespace Il2CppInspector
+{
+ public class App
+ {
+ static void Main(string[] args) {
+
+ // Command-line usage: dotnet run [ [ []]]
+ // Defaults to libil2cpp.so or GameAssembly.dll if binary file not specified
+ string imageFile = "libil2cpp.so";
+ string metaFile = "global-metadata.dat";
+ string outFile = "types.cs";
+
+ if (args.Length == 0)
+ if (!File.Exists(imageFile))
+ imageFile = "GameAssembly.dll";
+
+ if (args.Length >= 1)
+ imageFile = args[0];
+
+ if (args.Length >= 2)
+ metaFile = args[1];
+
+ if (args.Length >= 3)
+ outFile = args[2];
+
+ // Check files
+ if (!File.Exists(imageFile)) {
+ Console.Error.WriteLine($"File {imageFile} does not exist");
+ Environment.Exit(1);
+ }
+ if (!File.Exists(metaFile)) {
+ Console.Error.WriteLine($"File {metaFile} does not exist");
+ Environment.Exit(1);
+ }
+
+ // Analyze data
+ var il2cpp = Il2CppProcessor.LoadFromFile(imageFile, metaFile);
+ if (il2cpp == null)
+ Environment.Exit(1);
+
+ // Write output file
+ var dumper = new Il2CppDumper(il2cpp);
+ dumper.WriteFile(outFile);
+ }
+ }
+}
diff --git a/Il2CppInspector.sln b/Il2CppInspector.sln
new file mode 100644
index 0000000..c3236dd
--- /dev/null
+++ b/Il2CppInspector.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26228.9
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bin2Object", "Bin2Object\Bin2Object\Bin2Object.csproj", "{55382D6C-01B6-4AFD-850C-7A79DAB6F270}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppInspector", "Il2CppInspector\Il2CppInspector.csproj", "{E4721466-CC6F-47EB-AD48-F4DE70D77E5C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Il2CppDumper", "Il2CppDumper\Il2CppDumper.csproj", "{EA4C27DF-4640-48DF-8CAF-5587884CAF30}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {55382D6C-01B6-4AFD-850C-7A79DAB6F270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55382D6C-01B6-4AFD-850C-7A79DAB6F270}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55382D6C-01B6-4AFD-850C-7A79DAB6F270}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55382D6C-01B6-4AFD-850C-7A79DAB6F270}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4721466-CC6F-47EB-AD48-F4DE70D77E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4721466-CC6F-47EB-AD48-F4DE70D77E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4721466-CC6F-47EB-AD48-F4DE70D77E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4721466-CC6F-47EB-AD48-F4DE70D77E5C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EA4C27DF-4640-48DF-8CAF-5587884CAF30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EA4C27DF-4640-48DF-8CAF-5587884CAF30}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EA4C27DF-4640-48DF-8CAF-5587884CAF30}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EA4C27DF-4640-48DF-8CAF-5587884CAF30}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Il2CppInspector/DefineConstants.cs b/Il2CppInspector/DefineConstants.cs
new file mode 100644
index 0000000..01bea56
--- /dev/null
+++ b/Il2CppInspector/DefineConstants.cs
@@ -0,0 +1,20 @@
+public static class DefineConstants
+{
+ public const int FIELD_ATTRIBUTE_PRIVATE = 0x0001;
+ public const int FIELD_ATTRIBUTE_PUBLIC = 0x0006;
+ public const int FIELD_ATTRIBUTE_STATIC = 0x0010;
+ public const int FIELD_ATTRIBUTE_INIT_ONLY = 0x0020;
+ public const int METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK = 0x0007;
+ public const int METHOD_ATTRIBUTE_PRIVATE = 0x0001;
+ public const int METHOD_ATTRIBUTE_PUBLIC = 0x0006;
+ public const int METHOD_ATTRIBUTE_STATIC = 0x0010;
+ public const int METHOD_ATTRIBUTE_VIRTUAL = 0x0040;
+ public const int TYPE_ATTRIBUTE_VISIBILITY_MASK = 0x00000007;
+ public const int TYPE_ATTRIBUTE_PUBLIC = 0x00000001;
+ public const int TYPE_ATTRIBUTE_INTERFACE = 0x00000020;
+ public const int TYPE_ATTRIBUTE_ABSTRACT = 0x00000080;
+ public const int TYPE_ATTRIBUTE_SEALED = 0x00000100;
+ public const int TYPE_ATTRIBUTE_SERIALIZABLE = 0x00002000;
+ public const int PARAM_ATTRIBUTE_OUT = 0x0002;
+ public const int PARAM_ATTRIBUTE_OPTIONAL = 0x0010;
+}
\ No newline at end of file
diff --git a/Il2CppInspector/ElfHeaders.cs b/Il2CppInspector/ElfHeaders.cs
new file mode 100644
index 0000000..e0f50a4
--- /dev/null
+++ b/Il2CppInspector/ElfHeaders.cs
@@ -0,0 +1,89 @@
+/*
+ Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
+ Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
+
+ All rights reserved.
+*/
+
+using NoisyCowStudios.Bin2Object;
+
+namespace Il2CppInspector
+{
+#pragma warning disable CS0649
+ internal class elf_header
+ {
+ // 0x7f followed by ELF in ascii
+ public uint m_dwFormat;
+
+ // 1 - 32 bit
+ // 2 - 64 bit
+ public byte m_arch;
+
+ // 1 - little endian
+ // 2 - big endian
+ public byte m_endian;
+
+ // 1 is original elf format
+ public byte m_version;
+
+ // set based on OS, refer to OSABI enum
+ public byte m_osabi;
+
+ // refer to elf documentation
+ public byte m_osabi_ver;
+
+ // unused
+ [ArrayLength(FixedSize=7)]
+ public byte[] e_pad;//byte[7]
+
+ // 1 - relocatable
+ // 2 - executable
+ // 3 - shared
+ // 4 - core
+ public ushort e_type;
+
+ // refer to isa enum
+ public ushort e_machine;
+
+ public uint e_version;
+
+ public uint e_entry;
+ public uint e_phoff;
+ public uint e_shoff;
+ public uint e_flags;
+ public ushort e_ehsize;
+ public ushort e_phentsize;
+ public ushort e_phnum;
+ public ushort e_shentsize;
+ public ushort e_shnum;
+ public ushort e_shtrndx;
+ }
+
+ internal class program_header_table
+ {
+ public uint p_type;
+ public uint p_offset;
+ public uint p_vaddr;
+ public uint p_paddr;
+ public uint p_filesz;
+ public uint p_memsz;
+ public uint p_flags;
+ public uint p_align;
+ //public byte[] p_data;忽略
+ }
+
+ internal class elf_32_shdr
+ {
+ public uint sh_name;
+ public uint sh_type;
+ public uint sh_flags;
+ public uint sh_addr;
+ public uint sh_offset;
+ public uint sh_size;
+ public uint sh_link;
+ public uint sh_info;
+ public uint sh_addralign;
+ public uint sh_entsize;
+ }
+#pragma warning restore CS0649
+}
diff --git a/Il2CppInspector/ElfReader.cs b/Il2CppInspector/ElfReader.cs
new file mode 100644
index 0000000..c0af446
--- /dev/null
+++ b/Il2CppInspector/ElfReader.cs
@@ -0,0 +1,93 @@
+/*
+ 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;
+using System.IO;
+using System.Linq;
+
+namespace Il2CppInspector
+{
+ internal class ElfReader : FileFormatReader
+ {
+ private program_header_table[] program_table_element;
+ private elf_header elf_header;
+
+ public ElfReader(Stream stream) : base(stream) { }
+
+ public override string Arch {
+ get {
+ switch (elf_header.e_machine) {
+ case 0x03:
+ return "x86";
+ case 0x28:
+ return "ARM";
+ default:
+ return "Unsupported";
+ }
+ }
+ }
+
+ protected override bool Init() {
+ elf_header = ReadObject();
+
+ if (elf_header.m_dwFormat != 0x464c457f) {
+ // Not an ELF file
+ return false;
+ }
+ if (elf_header.m_arch == 2)//64
+ {
+ // 64-bit not supported
+ return false;
+ }
+ program_table_element = ReadArray(elf_header.e_phoff, elf_header.e_phnum);
+ return true;
+ }
+
+ public override uint[] GetSearchLocations() {
+ // Find dynamic section
+ var dynamic = new elf_32_shdr();
+ var PT_DYNAMIC = program_table_element.First(x => x.p_type == 2u);
+ dynamic.sh_offset = PT_DYNAMIC.p_offset;
+ dynamic.sh_size = PT_DYNAMIC.p_filesz;
+
+ // We need GOT, INIT_ARRAY and INIT_ARRAYSZ
+ uint _GLOBAL_OFFSET_TABLE_ = 0;
+ var init_array = new elf_32_shdr();
+ Position = dynamic.sh_offset;
+ var dynamicend = dynamic.sh_offset + dynamic.sh_size;
+ while (Position < dynamicend) {
+ var tag = ReadInt32();
+ if (tag == 3) //DT_PLTGOT
+ {
+ _GLOBAL_OFFSET_TABLE_ = ReadUInt32();
+ continue;
+ }
+ else if (tag == 25) //DT_INIT_ARRAY
+ {
+ init_array.sh_offset = MapVATR(ReadUInt32());
+ continue;
+ }
+ else if (tag == 27) //DT_INIT_ARRAYSZ
+ {
+ init_array.sh_size = ReadUInt32();
+ continue;
+ }
+ Position += 4;
+ }
+ if (_GLOBAL_OFFSET_TABLE_ == 0)
+ throw new InvalidOperationException("Unable to get GLOBAL_OFFSET_TABLE from PT_DYNAMIC");
+ GlobalOffset = _GLOBAL_OFFSET_TABLE_;
+ return ReadArray(init_array.sh_offset, (int) init_array.sh_size / 4);
+ }
+
+ public override uint MapVATR(uint uiAddr)
+ {
+ var program_header_table = program_table_element.First(x => uiAddr >= x.p_vaddr && uiAddr <= (x.p_vaddr + x.p_memsz));
+ return uiAddr - (program_header_table.p_vaddr - program_header_table.p_offset);
+ }
+ }
+}
diff --git a/Il2CppInspector/FileFormatReader.cs b/Il2CppInspector/FileFormatReader.cs
new file mode 100644
index 0000000..02a524b
--- /dev/null
+++ b/Il2CppInspector/FileFormatReader.cs
@@ -0,0 +1,70 @@
+/*
+ Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
+
+ All rights reserved.
+*/
+
+using System;
+using System.IO;
+using NoisyCowStudios.Bin2Object;
+
+namespace Il2CppInspector
+{
+ public interface IFileFormatReader
+ {
+ BinaryObjectReader Stream { get; }
+ long Position { get; set; }
+ string Arch { get; }
+ uint GlobalOffset { get; }
+ uint[] GetSearchLocations();
+ 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();
+ }
+
+ internal class FileFormatReader : BinaryObjectReader, IFileFormatReader where T : FileFormatReader
+ {
+ public FileFormatReader(Stream stream) : base(stream) { }
+
+ public BinaryObjectReader Stream => this;
+
+ public uint GlobalOffset { get; protected set; }
+
+ public virtual string Arch => throw new NotImplementedException();
+
+ public static T Load(string filename) {
+ using (var stream = new FileStream(filename, FileMode.Open))
+ return Load(stream);
+ }
+
+ public static T Load(Stream stream) {
+ stream.Position = 0;
+ var pe = (T) Activator.CreateInstance(typeof(T), stream);
+ return pe.Init() ? pe : null;
+ }
+
+ // Confirm file is valid and set up RVA mappings
+ protected virtual bool Init() => throw new NotImplementedException();
+
+ // Find search locations in the machine code for Il2Cpp data
+ public virtual uint[] GetSearchLocations() => throw new NotImplementedException();
+
+ // Map an RVA to an offset into the file image
+ public virtual uint MapVATR(uint uiAddr) => throw new NotImplementedException();
+
+ // 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Il2CppInspector/Il2CppClasses.cs b/Il2CppInspector/Il2CppClasses.cs
new file mode 100644
index 0000000..575f643
--- /dev/null
+++ b/Il2CppInspector/Il2CppClasses.cs
@@ -0,0 +1,207 @@
+using System;
+using System.Linq;
+
+namespace Il2CppInspector
+{
+ public class Il2CppCodeRegistration
+ {
+ public uint methodPointersCount;
+ public uint pmethodPointers;
+ public uint delegateWrappersFromNativeToManagedCount;
+ public uint delegateWrappersFromNativeToManaged; // note the double indirection to handle different calling conventions
+ public uint delegateWrappersFromManagedToNativeCount;
+ public uint delegateWrappersFromManagedToNative;
+ public uint marshalingFunctionsCount;
+ public uint marshalingFunctions;
+ public uint ccwMarshalingFunctionsCount;
+ public uint ccwMarshalingFunctions;
+ public uint genericMethodPointersCount;
+ public uint genericMethodPointers;
+ public uint invokerPointersCount;
+ public uint invokerPointers;
+ public int customAttributeCount;
+ public uint customAttributeGenerators;
+ public int guidCount;
+ public uint guids; // Il2CppGuid
+
+ public uint[] methodPointers
+ {
+ get; set;
+ }
+ }
+
+#pragma warning disable CS0649
+ public class Il2CppMetadataRegistration
+ {
+ public int genericClassesCount;
+ public uint genericClasses;
+ public int genericInstsCount;
+ public uint genericInsts;
+ public int genericMethodTableCount;
+ public uint genericMethodTable; // Il2CppGenericMethodFunctionsDefinitions
+ public int typesCount;
+ public uint ptypes;
+ public int methodSpecsCount;
+ public uint methodSpecs;
+
+ public int fieldOffsetsCount;
+ public uint pfieldOffsets;
+
+ public int typeDefinitionsSizesCount;
+ public uint typeDefinitionsSizes;
+ public uint metadataUsagesCount;
+ public uint metadataUsages;
+
+ public int[] fieldOffsets
+ {
+ get; set;
+ }
+
+ public Il2CppType[] types
+ {
+ get; set;
+ }
+ }
+#pragma warning restore CS0649
+
+ public enum Il2CppTypeEnum
+ {
+ IL2CPP_TYPE_END = 0x00, /* End of List */
+ IL2CPP_TYPE_VOID = 0x01,
+ IL2CPP_TYPE_BOOLEAN = 0x02,
+ IL2CPP_TYPE_CHAR = 0x03,
+ IL2CPP_TYPE_I1 = 0x04,
+ IL2CPP_TYPE_U1 = 0x05,
+ IL2CPP_TYPE_I2 = 0x06,
+ IL2CPP_TYPE_U2 = 0x07,
+ IL2CPP_TYPE_I4 = 0x08,
+ IL2CPP_TYPE_U4 = 0x09,
+ IL2CPP_TYPE_I8 = 0x0a,
+ IL2CPP_TYPE_U8 = 0x0b,
+ IL2CPP_TYPE_R4 = 0x0c,
+ IL2CPP_TYPE_R8 = 0x0d,
+ IL2CPP_TYPE_STRING = 0x0e,
+ IL2CPP_TYPE_PTR = 0x0f, /* arg: token */
+ IL2CPP_TYPE_BYREF = 0x10, /* arg: token */
+ IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: token */
+ IL2CPP_TYPE_CLASS = 0x12, /* arg: token */
+ IL2CPP_TYPE_VAR = 0x13, /* Generic parameter in a generic type definition, represented as number (compressed unsigned integer) number */
+ IL2CPP_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */
+ IL2CPP_TYPE_GENERICINST = 0x15, /* \x{2026} */
+ IL2CPP_TYPE_TYPEDBYREF = 0x16,
+ IL2CPP_TYPE_I = 0x18,
+ IL2CPP_TYPE_U = 0x19,
+ IL2CPP_TYPE_FNPTR = 0x1b, /* arg: full method signature */
+ IL2CPP_TYPE_OBJECT = 0x1c,
+ IL2CPP_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */
+ IL2CPP_TYPE_MVAR = 0x1e, /* Generic parameter in a generic method definition, represented as number (compressed unsigned integer) */
+ IL2CPP_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */
+ IL2CPP_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */
+ IL2CPP_TYPE_INTERNAL = 0x21, /* CLR internal type */
+
+ IL2CPP_TYPE_MODIFIER = 0x40, /* Or with the following types */
+ IL2CPP_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */
+ IL2CPP_TYPE_PINNED = 0x45, /* Local var that points to pinned object */
+
+ IL2CPP_TYPE_ENUM = 0x55 /* an enumeration */
+ }
+
+ 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 class Il2CppGenericClass
+ {
+ public int typeDefinitionIndex; /* the generic type definition */
+ public Il2CppGenericContext context; /* a context that contains the type instantiation doesn't contain any method instantiation */
+ public uint cached_class; /* if present, the Il2CppClass corresponding to the instantiation. */
+ }
+
+ public class Il2CppGenericContext
+ {
+ /* The instantiation corresponding to the class generic parameters */
+ public uint class_inst;
+ /* The instantiation corresponding to the method generic parameters */
+ public uint method_inst;
+ }
+
+
+ public class Il2CppGenericInst
+ {
+ public uint type_argc;
+ public uint type_argv;
+ }
+
+ public class Il2CppArrayType
+ {
+ public uint etype;
+ public byte rank;
+ public byte numsizes;
+ public byte numlobounds;
+ public uint sizes;
+ public uint lobounds;
+ }
+}
diff --git a/Il2CppInspector/Il2CppInspector.csproj b/Il2CppInspector/Il2CppInspector.csproj
new file mode 100644
index 0000000..0690e34
--- /dev/null
+++ b/Il2CppInspector/Il2CppInspector.csproj
@@ -0,0 +1,15 @@
+
+
+
+ netstandard1.5
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Il2CppInspector/Il2CppProcessor.cs b/Il2CppInspector/Il2CppProcessor.cs
new file mode 100644
index 0000000..eab48f4
--- /dev/null
+++ b/Il2CppInspector/Il2CppProcessor.cs
@@ -0,0 +1,137 @@
+/*
+ Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
+
+ All rights reserved.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Il2CppInspector
+{
+ public class Il2CppProcessor
+ {
+ public Il2CppReader Code { get; }
+ public Metadata Metadata { get; }
+
+ public Il2CppProcessor(Il2CppReader code, Metadata metadata) {
+ Code = code;
+ Metadata = metadata;
+ }
+
+ public static Il2CppProcessor LoadFromFile(string codeFile, string metadataFile) {
+ // Load the metadata file
+ var metadata = new Metadata(new MemoryStream(File.ReadAllBytes(metadataFile)));
+
+ // Load the il2cpp code file (try ELF and PE)
+ var memoryStream = new MemoryStream(File.ReadAllBytes(codeFile));
+ IFileFormatReader stream = (IFileFormatReader) ElfReader.Load(memoryStream) ?? PEReader.Load(memoryStream);
+ if (stream == null) {
+ Console.Error.WriteLine("Unsupported executable file format");
+ return null;
+ }
+
+ Il2CppReader il2cpp;
+
+ // We are currently supporting x86 and ARM architectures
+ switch (stream.Arch) {
+ case "x86":
+ il2cpp = new Il2CppReaderX86(stream);
+ break;
+ case "ARM":
+ il2cpp = new Il2CppReaderARM(stream);
+ break;
+ default:
+ Console.Error.WriteLine("Unsupported architecture");
+ return null;
+ }
+
+ // Find code and metadata regions
+ if (!il2cpp.Load()) {
+ Console.Error.WriteLine("Could not process IL2CPP image");
+ return null;
+ }
+
+ return new Il2CppProcessor(il2cpp, metadata);
+ }
+
+ 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];
+ ret = Metadata.GetString(klass.nameIndex);
+ }
+ else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) {
+ Il2CppGenericClass generic_class = Code.Image.ReadMappedObject(pType.data.generic_class);
+ Il2CppTypeDefinition pMainDef = Metadata.Types[generic_class.typeDefinitionIndex];
+ ret = Metadata.GetString(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);
+ for (int i = 0; i < pInst.type_argc; ++i) {
+ var pOriType = Code.Image.ReadMappedObject(pointers[i]);
+ pOriType.Init();
+ 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();
+ ret = $"{GetTypeName(type)}[]";
+ }
+ else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) {
+ var type = Code.Image.ReadMappedObject(pType.data.type);
+ type.Init();
+ ret = $"{GetTypeName(type)}[]";
+ }
+ else {
+ if ((int)pType.type >= szTypeString.Length)
+ ret = "unknow";
+ else
+ ret = szTypeString[(int)pType.type];
+ }
+ return ret;
+ }
+
+ private readonly string[] szTypeString =
+ {
+ "END",
+ "void",
+ "bool",
+ "char",
+ "sbyte",
+ "byte",
+ "short",
+ "ushort",
+ "int",
+ "uint",
+ "long",
+ "ulong",
+ "float",
+ "double",
+ "string",
+ "PTR",//eg. void*
+ "BYREF",
+ "VALUETYPE",
+ "CLASS",
+ "T",
+ "ARRAY",
+ "GENERICINST",
+ "TYPEDBYREF",
+ "None",
+ "IntPtr",
+ "UIntPtr",
+ "None",
+ "FNPTR",
+ "object",
+ "SZARRAY",
+ "T",
+ "CMOD_REQD",
+ "CMOD_OPT",
+ "INTERNAL",
+ };
+ }
+}
diff --git a/Il2CppInspector/Il2CppReader.cs b/Il2CppInspector/Il2CppReader.cs
new file mode 100644
index 0000000..ee2c1db
--- /dev/null
+++ b/Il2CppInspector/Il2CppReader.cs
@@ -0,0 +1,68 @@
+/*
+ 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(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() {
+ var addrs = Image.GetSearchLocations();
+ foreach (var loc in addrs)
+ if (loc != 0) {
+ var (code, metadata) = Search(loc, Image.GlobalOffset);
+ if (code != 0) {
+ Configure(code, metadata);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void Configure(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();
+ }
+ }
+
+ public Il2CppType GetTypeFromTypeIndex(int idx) {
+ return PtrMetadataRegistration.types[idx];
+ }
+
+ public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType) {
+ var ptr = PtrMetadataRegistration.fieldOffsets[typeIndex];
+ Image.Stream.Position = Image.MapVATR((uint) ptr) + 4 * fieldIndexInType;
+ return Image.Stream.ReadInt32();
+ }
+ }
+}
diff --git a/Il2CppInspector/Il2CppReaderARM.cs b/Il2CppInspector/Il2CppReaderARM.cs
new file mode 100644
index 0000000..196b3f4
--- /dev/null
+++ b/Il2CppInspector/Il2CppReaderARM.cs
@@ -0,0 +1,64 @@
+/*
+ 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.Linq;
+
+namespace Il2CppInspector
+{
+ internal class Il2CppReaderARM : Il2CppReader
+ {
+ public Il2CppReaderARM(IFileFormatReader stream) : base(stream) { }
+
+ public Il2CppReaderARM(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) : base(stream, codeRegistration, metadataRegistration) { }
+
+ protected override (uint, uint) Search(uint loc, uint globalOffset) {
+ // Assembly bytes to search for at start of each function
+ uint metadataRegistration, codeRegistration;
+
+ // ARM
+ var bytes = new byte[] { 0x1c, 0x0, 0x9f, 0xe5, 0x1c, 0x10, 0x9f, 0xe5, 0x1c, 0x20, 0x9f, 0xe5 };
+ Image.Position = loc;
+ var buff = Image.ReadBytes(12);
+ if (bytes.SequenceEqual(buff)) {
+ Image.Position = loc + 0x2c;
+ var subaddr = Image.ReadUInt32() + globalOffset;
+ Image.Position = subaddr + 0x28;
+ codeRegistration = Image.ReadUInt32() + globalOffset;
+ Image.Position = subaddr + 0x2C;
+ var ptr = Image.ReadUInt32() + globalOffset;
+ Image.Position = Image.MapVATR(ptr);
+ metadataRegistration = Image.ReadUInt32();
+ return (codeRegistration, metadataRegistration);
+ }
+
+ // ARMv7 Thumb (T1)
+ // http://liris.cnrs.fr/~mmrissa/lib/exe/fetch.php?media=armv7-a-r-manual.pdf - A8.8.106
+ // http://armconverter.com/hextoarm/
+ bytes = new byte[] { 0x2d, 0xe9, 0x00, 0x48, 0xeb, 0x46 };
+ Image.Position = loc;
+ buff = Image.ReadBytes(6);
+ if (!bytes.SequenceEqual(buff))
+ return (0, 0);
+ bytes = new byte[] { 0x00, 0x23, 0x00, 0x22, 0xbd, 0xe8, 0x00, 0x48 };
+ Image.Position += 0x10;
+ buff = Image.ReadBytes(8);
+ if (!bytes.SequenceEqual(buff))
+ return (0, 0);
+ Image.Position = loc + 6;
+ Image.Position = (Image.MapVATR(decodeMovImm32(Image.ReadBytes(8))) & 0xfffffffc) + 0x0e;
+ metadataRegistration = decodeMovImm32(Image.ReadBytes(8));
+ codeRegistration = decodeMovImm32(Image.ReadBytes(8));
+ return (codeRegistration, metadataRegistration);
+ }
+
+ private uint decodeMovImm32(byte[] asm) {
+ ushort low = (ushort) (asm[2] + ((asm[3] & 0x70) << 4) + ((asm[1] & 0x04) << 9) + ((asm[0] & 0x0f) << 12));
+ ushort high = (ushort) (asm[6] + ((asm[7] & 0x70) << 4) + ((asm[5] & 0x04) << 9) + ((asm[4] & 0x0f) << 12));
+ return (uint) ((high << 16) + low);
+ }
+ }
+}
diff --git a/Il2CppInspector/Il2CppReaderX86.cs b/Il2CppInspector/Il2CppReaderX86.cs
new file mode 100644
index 0000000..cc5848b
--- /dev/null
+++ b/Il2CppInspector/Il2CppReaderX86.cs
@@ -0,0 +1,58 @@
+/*
+ Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
+
+ All rights reserved.
+*/
+
+using System.Linq;
+
+namespace Il2CppInspector
+{
+ internal class Il2CppReaderX86 : Il2CppReader
+ {
+ 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) {
+ uint funcPtr, metadata, code;
+
+ // Variant 1
+
+ // Assembly bytes to search for at start of each function
+ var bytes = new byte[] { 0x6A, 0x00, 0x6A, 0x00, 0x68 };
+ Image.Position = loc;
+ var buff = Image.ReadBytes(5);
+ if (bytes.SequenceEqual(buff)) {
+ // Next 4 bytes are the function pointer being pushed onto the stack
+ funcPtr = Image.ReadUInt32();
+
+ // Start of next instruction
+ if (Image.ReadByte() != 0xB9)
+ return (0, 0);
+
+ // Jump to Il2CppCodegenRegistration
+ Image.Position = Image.MapVATR(funcPtr) + 6;
+ metadata = Image.ReadUInt32();
+ Image.Position = Image.MapVATR(funcPtr) + 11;
+ code = Image.ReadUInt32();
+ return (code, metadata);
+ }
+
+ // Variant 2
+ bytes = new byte[] { 0x55, 0x89, 0xE5, 0x53, 0x83, 0xE4, 0xF0, 0x83, 0xEC, 0x20, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5B };
+ Image.Position = loc;
+ buff = Image.ReadBytes(16);
+ if (!bytes.SequenceEqual(buff))
+ return (0, 0);
+
+ Image.Position += 8;
+ funcPtr = Image.MapVATR(Image.ReadUInt32() + globalOffset);
+ if (funcPtr > Image.Stream.BaseStream.Length)
+ return (0, 0);
+ Image.Position = funcPtr + 0x22;
+ metadata = Image.ReadUInt32() + globalOffset;
+ Image.Position = funcPtr + 0x2C;
+ code = Image.ReadUInt32() + globalOffset;
+ return (code, metadata);
+ }
+ }
+}
diff --git a/Il2CppInspector/Metadata.cs b/Il2CppInspector/Metadata.cs
new file mode 100644
index 0000000..2c81591
--- /dev/null
+++ b/Il2CppInspector/Metadata.cs
@@ -0,0 +1,99 @@
+/*
+ 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;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using NoisyCowStudios.Bin2Object;
+
+namespace Il2CppInspector
+{
+ public class Metadata : BinaryObjectReader
+ {
+ private Il2CppGlobalMetadataHeader pMetadataHdr;
+
+ public Il2CppImageDefinition[] Images { get; }
+ public Il2CppTypeDefinition[] Types { get; }
+ public Il2CppMethodDefinition[] Methods { get; }
+ public Il2CppParameterDefinition[] parameterDefs;
+ public Il2CppFieldDefinition[] Fields { get; }
+ public Il2CppFieldDefaultValue[] fieldDefaultValues;
+
+ public string GetImageName(Il2CppImageDefinition image) => GetString(image.nameIndex);
+ public string GetTypeNamespace(Il2CppTypeDefinition type) => GetString(type.namespaceIndex);
+ public string GetTypeName(Il2CppTypeDefinition type) => GetString(type.nameIndex);
+
+
+
+ public Metadata(Stream stream) : base(stream)
+ {
+ pMetadataHdr = ReadObject();
+ if (pMetadataHdr.sanity != 0xFAB11BAF)
+ {
+ throw new Exception("ERROR: Metadata file supplied is not valid metadata file.");
+ }
+ if (pMetadataHdr.version != 21 && pMetadataHdr.version != 22)
+ {
+ throw new Exception($"ERROR: Metadata file supplied is not a supported version[{pMetadataHdr.version}].");
+ }
+ var uiImageCount = pMetadataHdr.imagesCount / MySizeOf(typeof(Il2CppImageDefinition));
+ var uiNumTypes = pMetadataHdr.typeDefinitionsCount / MySizeOf(typeof(Il2CppTypeDefinition));
+ Images = ReadArray(pMetadataHdr.imagesOffset, uiImageCount);
+ //GetTypeDefFromIndex
+ Types = ReadArray(pMetadataHdr.typeDefinitionsOffset, uiNumTypes);
+ //GetMethodDefinition
+ Methods = ReadArray(pMetadataHdr.methodsOffset, pMetadataHdr.methodsCount / MySizeOf(typeof(Il2CppMethodDefinition)));
+ //GetParameterFromIndex
+ parameterDefs = ReadArray(pMetadataHdr.parametersOffset, pMetadataHdr.parametersCount / MySizeOf(typeof(Il2CppParameterDefinition)));
+ //GetFieldDefFromIndex
+ Fields = ReadArray(pMetadataHdr.fieldsOffset, pMetadataHdr.fieldsCount / MySizeOf(typeof(Il2CppFieldDefinition)));
+ //GetFieldDefaultFromIndex
+ fieldDefaultValues = ReadArray(pMetadataHdr.fieldDefaultValuesOffset, pMetadataHdr.fieldDefaultValuesCount / MySizeOf(typeof(Il2CppFieldDefaultValue)));
+ }
+
+ public Il2CppFieldDefaultValue GetFieldDefaultFromIndex(int idx)
+ {
+ return fieldDefaultValues.FirstOrDefault(x => x.fieldIndex == idx);
+ }
+
+ public int GetDefaultValueFromIndex(int idx)
+ {
+ return pMetadataHdr.fieldAndParameterDefaultValueDataOffset + idx;
+ }
+
+ public string GetString(int idx)
+ {
+ return ReadNullTerminatedString(pMetadataHdr.stringOffset + idx);
+ }
+
+ private int MySizeOf(Type type)
+ {
+ int size = 0;
+ foreach (var i in type.GetTypeInfo().GetFields())
+ {
+ if (i.FieldType == typeof(int))
+ {
+ size += 4;
+ }
+ else if (i.FieldType == typeof(uint))
+ {
+ size += 4;
+ }
+ else if (i.FieldType == typeof(short))
+ {
+ size += 2;
+ }
+ else if (i.FieldType == typeof(ushort))
+ {
+ size += 2;
+ }
+ }
+ return size;
+ }
+ }
+}
diff --git a/Il2CppInspector/MetadataClass.cs b/Il2CppInspector/MetadataClass.cs
new file mode 100644
index 0000000..a840f92
--- /dev/null
+++ b/Il2CppInspector/MetadataClass.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Il2CppInspector
+{
+#pragma warning disable CS0649
+ public class Il2CppGlobalMetadataHeader
+ {
+ public uint sanity;
+ public int version;
+ public int stringLiteralOffset; // string data for managed code
+ public int stringLiteralCount;
+ public int stringLiteralDataOffset;
+ public int stringLiteralDataCount;
+ public int stringOffset; // string data for metadata
+ public int stringCount;
+ public int eventsOffset; // Il2CppEventDefinition
+ public int eventsCount;
+ public int propertiesOffset; // Il2CppPropertyDefinition
+ public int propertiesCount;
+ public int methodsOffset; // Il2CppMethodDefinition
+ public int methodsCount;
+ public int parameterDefaultValuesOffset; // Il2CppParameterDefaultValue
+ public int parameterDefaultValuesCount;
+ public int fieldDefaultValuesOffset; // Il2CppFieldDefaultValue
+ public int fieldDefaultValuesCount;
+ public int fieldAndParameterDefaultValueDataOffset; // uint8_t
+ public int fieldAndParameterDefaultValueDataCount;
+ public int fieldMarshaledSizesOffset; // Il2CppFieldMarshaledSize
+ public int fieldMarshaledSizesCount;
+ public int parametersOffset; // Il2CppParameterDefinition
+ public int parametersCount;
+ public int fieldsOffset; // Il2CppFieldDefinition
+ public int fieldsCount;
+ public int genericParametersOffset; // Il2CppGenericParameter
+ public int genericParametersCount;
+ public int genericParameterConstraintsOffset; // TypeIndex
+ public int genericParameterConstraintsCount;
+ public int genericContainersOffset; // Il2CppGenericContainer
+ public int genericContainersCount;
+ public int nestedTypesOffset; // TypeDefinitionIndex
+ public int nestedTypesCount;
+ public int interfacesOffset; // TypeIndex
+ public int interfacesCount;
+ public int vtableMethodsOffset; // EncodedMethodIndex
+ public int vtableMethodsCount;
+ public int interfaceOffsetsOffset; // Il2CppInterfaceOffsetPair
+ public int interfaceOffsetsCount;
+ public int typeDefinitionsOffset; // Il2CppTypeDefinition
+ public int typeDefinitionsCount;
+ public int rgctxEntriesOffset; // Il2CppRGCTXDefinition
+ public int rgctxEntriesCount;
+ public int imagesOffset; // Il2CppImageDefinition
+ public int imagesCount;
+ public int assembliesOffset; // Il2CppAssemblyDefinition
+ public int assembliesCount;
+ public int metadataUsageListsOffset; // Il2CppMetadataUsageList
+ public int metadataUsageListsCount;
+ public int metadataUsagePairsOffset; // Il2CppMetadataUsagePair
+ public int metadataUsagePairsCount;
+ public int fieldRefsOffset; // Il2CppFieldRef
+ public int fieldRefsCount;
+ public int referencedAssembliesOffset; // int
+ public int referencedAssembliesCount;
+ public int attributesInfoOffset; // Il2CppCustomAttributeTypeRange
+ public int attributesInfoCount;
+ public int attributeTypesOffset; // TypeIndex
+ public int attributeTypesCount;
+ }
+
+ public class Il2CppImageDefinition
+ {
+ public int nameIndex;
+ public int assemblyIndex;
+
+ public int typeStart;
+ public uint typeCount;
+
+ public int entryPointIndex;
+ public uint token;
+ }
+#pragma warning restore CS0649
+
+ public class Il2CppTypeDefinition
+ {
+ public int nameIndex;
+ public int namespaceIndex;
+ public int customAttributeIndex;
+ public int byvalTypeIndex;
+ public int byrefTypeIndex;
+
+ public int declaringTypeIndex;
+ public int parentIndex;
+ public int elementTypeIndex; // we can probably remove this one. Only used for enums
+
+ public int rgctxStartIndex;
+ public int rgctxCount;
+
+ public int genericContainerIndex;
+
+ public int delegateWrapperFromManagedToNativeIndex;
+ public int marshalingFunctionsIndex;
+ public int ccwFunctionIndex;
+ public int guidIndex;
+
+ public uint flags;
+
+ public int fieldStart;
+ public int methodStart;
+ public int eventStart;
+ public int propertyStart;
+ public int nestedTypesStart;
+ public int interfacesStart;
+ public int vtableStart;
+ public int interfaceOffsetsStart;
+
+ public ushort method_count;
+ public ushort property_count;
+ public ushort field_count;
+ public ushort event_count;
+ public ushort nested_type_count;
+ public ushort vtable_count;
+ public ushort interfaces_count;
+ public ushort interface_offsets_count;
+
+ // bitfield to portably encode boolean values as single bits
+ // 01 - valuetype;
+ // 02 - enumtype;
+ // 03 - has_finalize;
+ // 04 - has_cctor;
+ // 05 - is_blittable;
+ // 06 - is_import;
+ // 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128)
+ public uint bitfield;
+ public uint token;
+ }
+
+ public class Il2CppMethodDefinition
+ {
+ public int nameIndex;
+ public int declaringType;
+ public int returnType;
+ public int parameterStart;
+ public int customAttributeIndex;
+ public int genericContainerIndex;
+ public int methodIndex;
+ public int invokerIndex;
+ public int delegateWrapperIndex;
+ public int rgctxStartIndex;
+ public int rgctxCount;
+ public uint token;
+ public ushort flags;
+ public ushort iflags;
+ public ushort slot;
+ public ushort parameterCount;
+ }
+
+ public class Il2CppParameterDefinition
+ {
+ public int nameIndex;
+ public uint token;
+ public int customAttributeIndex;
+ public int typeIndex;
+ }
+
+ public class Il2CppFieldDefinition
+ {
+ public int nameIndex;
+ public int typeIndex;
+ public int customAttributeIndex;
+ public uint token;
+ }
+
+ public class Il2CppFieldDefaultValue
+ {
+ public int fieldIndex;
+ public int typeIndex;
+ public int dataIndex;
+ }
+}
diff --git a/Il2CppInspector/PEHeaders.cs b/Il2CppInspector/PEHeaders.cs
new file mode 100644
index 0000000..06adcc5
--- /dev/null
+++ b/Il2CppInspector/PEHeaders.cs
@@ -0,0 +1,78 @@
+/*
+ Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
+
+ All rights reserved.
+*/
+
+using NoisyCowStudios.Bin2Object;
+
+namespace Il2CppInspector
+{
+#pragma warning disable CS0649
+ internal class COFFHeader
+ {
+ public ushort Machine;
+ public ushort NumberOfSections;
+ public uint TimeDateStamp;
+ public uint PointerToSymbolTable;
+ public uint NumberOfSymbols;
+ public ushort SizeOfOptionalHeader;
+ public ushort Characteristics;
+ }
+
+ internal class PEOptHeader
+ {
+ public ushort signature;
+ public byte MajorLinkerVersion;
+ public byte MinorLinkerVersion;
+ public uint SizeOfCode;
+ public uint SizeOfInitializedData;
+ public uint SizeOfUninitializedData;
+ public uint AddressOfEntryPoint;
+ public uint BaseOfCode;
+ public uint BaseOfData;
+ public uint ImageBase;
+ public uint SectionAlignment;
+ public uint FileAlignment;
+ public ushort MajorOSVersion;
+ public ushort MinorOSVersion;
+ public ushort MajorImageVersion;
+ public ushort MinorImageVersion;
+ public ushort MajorSubsystemVersion;
+ public ushort MinorSubsystemVersion;
+ public uint Win32VersionValue;
+ public uint SizeOfImage;
+ public uint SizeOfHeaders;
+ public uint Checksum;
+ public ushort Subsystem;
+ public ushort DLLCharacteristics;
+ public uint SizeOfStackReserve;
+ public uint SizeOfStackCommit;
+ public uint SizeOfHeapReserve;
+ public uint SizeOfHeapCommit;
+ public uint LoaderFlags;
+ public uint NumberOfRvaAndSizes;
+ [ArrayLength(FieldName = "NumberOfRvaAndSizes")]
+ public RvaEntry[] DataDirectory;
+ }
+
+ internal class RvaEntry
+ {
+ public uint VirtualAddress;
+ public uint Size;
+ }
+
+ internal class PESection
+ {
+ [String(FixedSize=8)]
+ public string Name;
+ public uint SizeMemory;
+ public uint BaseMemory; // RVA
+ public uint SizeImage; // Size in file
+ public uint BaseImage; // Base address in file
+ [ArrayLength(FixedSize=12)]
+ public byte[] Reserved;
+ public uint Flags;
+ }
+#pragma warning restore CS0649
+}
diff --git a/Il2CppInspector/PEReader.cs b/Il2CppInspector/PEReader.cs
new file mode 100644
index 0000000..f820bce
--- /dev/null
+++ b/Il2CppInspector/PEReader.cs
@@ -0,0 +1,101 @@
+/*
+ Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
+
+ All rights reserved.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Il2CppInspector
+{
+ internal class PEReader : FileFormatReader
+ {
+ private COFFHeader coff;
+ private PEOptHeader pe;
+ private PESection[] sections;
+ private uint pFuncTable;
+
+ public PEReader(Stream stream) : base(stream) {}
+
+ public override string Arch {
+ get {
+ switch (coff.Machine) {
+ case 0x14C:
+ return "x86";
+ case 0x1C0: // ARMv7
+ case 0x1C4: // ARMv7 Thumb (T1)
+ return "ARM";
+ default:
+ return "Unsupported";
+ }
+ }
+ }
+
+ protected override bool Init() {
+ // Check for MZ signature "MZ"
+ if (ReadUInt16() != 0x5A4D)
+ return false;
+
+ // Get offset to PE header from DOS header
+ Position = 0x3C;
+ Position = ReadUInt32();
+
+ // Check PE signature "PE\0\0"
+ if (ReadUInt32() != 0x00004550)
+ return false;
+
+ // Read COFF Header
+ coff = ReadObject();
+
+ // Ensure presence of PE Optional header
+ // Size will always be 0x60 + (0x10 ' 0x8) for 16 RVA entries @ 8 bytes each
+ if (coff.SizeOfOptionalHeader != 0xE0)
+ return false;
+
+ // Read PE optional header
+ pe = ReadObject();
+
+ // Ensure IMAGE_NT_OPTIONAL_HDR32_MAGIC (32-bit)
+ if (pe.signature != 0x10B)
+ return false;
+
+ // Get IAT
+ var IATStart = pe.DataDirectory[12].VirtualAddress;
+ var IATSize = pe.DataDirectory[12].Size;
+
+ // Get sections table
+ sections = ReadArray(coff.NumberOfSections);
+
+ // Confirm that .rdata section begins at same place as IAT
+ var rData = sections.First(x => x.Name == ".rdata");
+ if (rData.BaseMemory != IATStart)
+ return false;
+
+ // Calculate start of function pointer table
+ pFuncTable = rData.BaseImage + IATSize + 8;
+ GlobalOffset = pe.ImageBase;
+ return true;
+ }
+
+ public override uint[] GetSearchLocations() {
+ Position = pFuncTable;
+ var addrs = new List();
+ uint addr;
+ while ((addr = ReadUInt32()) != 0)
+ addrs.Add(MapVATR(addr) & 0xfffffffc);
+ return addrs.ToArray();
+ }
+
+ public override uint MapVATR(uint uiAddr) {
+ if (uiAddr == 0)
+ return 0;
+
+ var section = sections.First(x => uiAddr - GlobalOffset >= x.BaseMemory &&
+ uiAddr - GlobalOffset < x.BaseMemory + x.SizeMemory);
+ return uiAddr - section.BaseMemory - GlobalOffset + section.BaseImage;
+ }
+ }
+}
diff --git a/README.md b/README.md
index 4a18c0a..f3d6b19 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,45 @@
# Il2CppInspector
-Extract types, methods, properties and fields from Unity IL2CPP binaries
+Extract types, methods, properties and fields from Unity IL2CPP binaries.
+
+* Supports ELF (Android .so) and PE (Windows .exe) file formats
+* Supports ARM, ARMv7 Thumb (T1) and x86 architectures regardless of file format
+* Supports metadata versions 21 and 22
+* No manual reverse-engineering required; all data is calculated automatically
+* **Il2CppInspector** re-usable class library
+
+Targets .NET Standard 1.5 / .NET Core 1.1. Built with Visual Studio 2017.
+
+### Usage
+
+```
+dotnet run [ [ []]]
+```
+
+Defaults if not specified:
+
+- _binary-file_ - searches for `libil2cpp.so` and `GameAssembly.dll`
+- _metadata-file_ - `global-metadata.dat`
+- _output-file_ - `types.cs`
+
+File format and architecture are automatically detected.
+
+### Help with iOS support
+
+Mach-O (iOS) file format is not currently supported. Please contact me via the contact form at http://www.djkaty.com if you have a rooted iOS device and can produce cracked IPA files.
+
+### Acknowledgements
+
+Thanks to the following individuals whose code and research helped me develop this tool:
+
+- Perfare - https://github.com/Perfare/Il2CppDumper
+- Jumboperson - https://github.com/Jumboperson/Il2CppDumper
+- nevermoe - https://github.com/nevermoe/unity_metadata_loader
+- branw - https://github.com/branw/pogo-proto-dumper
+- fry - https://github.com/fry/d3
+- ARMConvertor - http://armconverter.com
+
+This tool uses Perfare's Il2CppDumper code as a base.
+
+### License
+
+All rights reserved. Unauthorized use, re-use or the creation of derivative works of this code for commercial purposes whether directly or indirectly is strictly prohibited. Use, re-use or the creation of derivative works for non-commercial purposes is expressly permitted.