Separate project from Il2Cpp2Proto
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "Bin2Object"]
|
||||||
|
path = Bin2Object
|
||||||
|
url = https://github.com/djkaty/Bin2Object
|
||||||
1
Bin2Object
Submodule
1
Bin2Object
Submodule
Submodule Bin2Object added at 8cb9cc95df
161
Il2CppDumper/Il2CppDumper.cs
Normal file
161
Il2CppDumper/Il2CppDumper.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Il2CppDumper/Il2CppDumper.csproj
Normal file
12
Il2CppDumper/Il2CppDumper.csproj
Normal 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
52
Il2CppDumper/Program.cs
Normal 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
34
Il2CppInspector.sln
Normal 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
|
||||||
20
Il2CppInspector/DefineConstants.cs
Normal file
20
Il2CppInspector/DefineConstants.cs
Normal 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;
|
||||||
|
}
|
||||||
89
Il2CppInspector/ElfHeaders.cs
Normal file
89
Il2CppInspector/ElfHeaders.cs
Normal 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
|
||||||
|
}
|
||||||
93
Il2CppInspector/ElfReader.cs
Normal file
93
Il2CppInspector/ElfReader.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
Il2CppInspector/FileFormatReader.cs
Normal file
70
Il2CppInspector/FileFormatReader.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
207
Il2CppInspector/Il2CppClasses.cs
Normal file
207
Il2CppInspector/Il2CppClasses.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Il2CppInspector/Il2CppInspector.csproj
Normal file
15
Il2CppInspector/Il2CppInspector.csproj
Normal 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>
|
||||||
137
Il2CppInspector/Il2CppProcessor.cs
Normal file
137
Il2CppInspector/Il2CppProcessor.cs
Normal 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",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Il2CppInspector/Il2CppReader.cs
Normal file
68
Il2CppInspector/Il2CppReader.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Il2CppInspector/Il2CppReaderARM.cs
Normal file
64
Il2CppInspector/Il2CppReaderARM.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Il2CppInspector/Il2CppReaderX86.cs
Normal file
58
Il2CppInspector/Il2CppReaderX86.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
Il2CppInspector/Metadata.cs
Normal file
99
Il2CppInspector/Metadata.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
182
Il2CppInspector/MetadataClass.cs
Normal file
182
Il2CppInspector/MetadataClass.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
Il2CppInspector/PEHeaders.cs
Normal file
78
Il2CppInspector/PEHeaders.cs
Normal 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
101
Il2CppInspector/PEReader.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
README.md
45
README.md
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user