Separate project from Il2Cpp2Proto

This commit is contained in:
Katy Coe
2017-03-17 11:29:54 +01:00
parent 75fb3d6c67
commit 9ced0b6c56
21 changed files with 1588 additions and 1 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "Bin2Object"]
path = Bin2Object
url = https://github.com/djkaty/Bin2Object

1
Bin2Object Submodule

Submodule Bin2Object added at 8cb9cc95df

View File

@@ -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");
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Il2CppInspector\Il2CppInspector.csproj" />
</ItemGroup>
</Project>

52
Il2CppDumper/Program.cs Normal file
View File

@@ -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 [<binary-file> [<metadata-file> [<output-file>]]]
// 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);
}
}
}

34
Il2CppInspector.sln Normal file
View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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<ElfReader>
{
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<elf_header>();
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<program_header_table>(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<uint>(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);
}
}
}

View File

@@ -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<U>(uint uiAddr) where U : new();
U[] ReadMappedArray<U>(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<T> : BinaryObjectReader, IFileFormatReader where T : FileFormatReader<T>
{
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<U>(uint uiAddr) where U : new() {
return ReadObject<U>(MapVATR(uiAddr));
}
public U[] ReadMappedArray<U>(uint uiAddr, int count) where U : new() {
return ReadArray<U>(MapVATR(uiAddr), count);
}
}
}

View File

@@ -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: <type> token */
IL2CPP_TYPE_BYREF = 0x10, /* arg: <type> token */
IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: <type> token */
IL2CPP_TYPE_CLASS = 0x12, /* arg: <type> 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, /* <type> <type-arg-count> <type-1> \x{2026} <type-n> */
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;
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.5</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bin2Object\Bin2Object\Bin2Object.csproj" />
</ItemGroup>
</Project>

View File

@@ -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<Il2CppGenericClass>(pType.data.generic_class);
Il2CppTypeDefinition pMainDef = Metadata.Types[generic_class.typeDefinitionIndex];
ret = Metadata.GetString(pMainDef.nameIndex);
var typeNames = new List<string>();
Il2CppGenericInst pInst = Code.Image.ReadMappedObject<Il2CppGenericInst>(generic_class.context.class_inst);
var pointers = Code.Image.ReadMappedArray<uint>(pInst.type_argv, (int)pInst.type_argc);
for (int i = 0; i < pInst.type_argc; ++i) {
var pOriType = Code.Image.ReadMappedObject<Il2CppType>(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<Il2CppArrayType>(pType.data.array);
var type = Code.Image.ReadMappedObject<Il2CppType>(arrayType.etype);
type.Init();
ret = $"{GetTypeName(type)}[]";
}
else if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) {
var type = Code.Image.ReadMappedObject<Il2CppType>(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",
};
}
}

View File

@@ -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<Il2CppCodeRegistration>(codeRegistration);
PtrMetadataRegistration = Image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
PtrCodeRegistration.methodPointers = Image.ReadMappedArray<uint>(PtrCodeRegistration.pmethodPointers,
(int) PtrCodeRegistration.methodPointersCount);
PtrMetadataRegistration.fieldOffsets = Image.ReadMappedArray<int>(PtrMetadataRegistration.pfieldOffsets,
PtrMetadataRegistration.fieldOffsetsCount);
var types = Image.ReadMappedArray<uint>(PtrMetadataRegistration.ptypes, PtrMetadataRegistration.typesCount);
PtrMetadataRegistration.types = new Il2CppType[PtrMetadataRegistration.typesCount];
for (int i = 0; i < PtrMetadataRegistration.typesCount; ++i) {
PtrMetadataRegistration.types[i] = Image.ReadMappedObject<Il2CppType>(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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<Il2CppGlobalMetadataHeader>();
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<Il2CppImageDefinition>(pMetadataHdr.imagesOffset, uiImageCount);
//GetTypeDefFromIndex
Types = ReadArray<Il2CppTypeDefinition>(pMetadataHdr.typeDefinitionsOffset, uiNumTypes);
//GetMethodDefinition
Methods = ReadArray<Il2CppMethodDefinition>(pMetadataHdr.methodsOffset, pMetadataHdr.methodsCount / MySizeOf(typeof(Il2CppMethodDefinition)));
//GetParameterFromIndex
parameterDefs = ReadArray<Il2CppParameterDefinition>(pMetadataHdr.parametersOffset, pMetadataHdr.parametersCount / MySizeOf(typeof(Il2CppParameterDefinition)));
//GetFieldDefFromIndex
Fields = ReadArray<Il2CppFieldDefinition>(pMetadataHdr.fieldsOffset, pMetadataHdr.fieldsCount / MySizeOf(typeof(Il2CppFieldDefinition)));
//GetFieldDefaultFromIndex
fieldDefaultValues = ReadArray<Il2CppFieldDefaultValue>(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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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
}

101
Il2CppInspector/PEReader.cs Normal file
View File

@@ -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<PEReader>
{
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<COFFHeader>();
// 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<PEOptHeader>();
// 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<PESection>(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>();
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;
}
}
}

View File

@@ -1,2 +1,45 @@
# Il2CppInspector # 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 [<binary-file> [<metadata-file> [<output-file>]]]
```
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.