Minor re-factoring

This commit is contained in:
Katy Coe
2020-01-26 22:18:29 +01:00
parent 90f563f8d2
commit 274f61d605
12 changed files with 32 additions and 24 deletions

View File

@@ -0,0 +1,205 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace Il2CppInspector
{
public abstract class Il2CppBinary
{
public IFileFormatReader Image { get; }
public Il2CppCodeRegistration CodeRegistration { get; protected set; }
public Il2CppMetadataRegistration MetadataRegistration { get; protected set; }
// Only for <=v24.1
public ulong[] GlobalMethodPointers { get; set; }
// Only for >=v24.2
public Dictionary<Il2CppCodeGenModule, ulong[]> ModuleMethodPointers { get; set; } = new Dictionary<Il2CppCodeGenModule, ulong[]>();
// NOTE: In versions <21 and earlier releases of v21, use FieldOffsets:
// global field index => field offset
// In versions >=22 and later releases of v21, use FieldOffsetPointers:
// type index => RVA in image where the list of field offsets for the type start (4 bytes per field)
// Negative field offsets from start of each function
public uint[] FieldOffsets { get; private set; }
// Pointers to field offsets
public long[] FieldOffsetPointers { get; private set; }
// Generated functions which call constructors on custom attributes
public ulong[] CustomAttributeGenerators { get; private set; }
// Generic method specs for vtables
public Il2CppMethodSpec[] MethodSpecs { get; private set; }
// Addresses where metadata is used
public ulong[] MetadataUsages { get; private set; }
// Every defined type
public List<Il2CppType> Types { get; private set; }
// From v24.2 onwards, this structure is stored for each module (image)
// One assembly may contain multiple modules
public Dictionary<string, Il2CppCodeGenModule> Modules { get; private set; }
protected Il2CppBinary(IFileFormatReader stream) {
Image = stream;
}
protected Il2CppBinary(IFileFormatReader stream, uint codeRegistration, uint metadataRegistration) {
Image = stream;
Configure(Image, codeRegistration, metadataRegistration);
}
// Load and initialize a binary of any supported architecture
public static Il2CppBinary Load(IFileFormatReader stream, double metadataVersion) {
// Get type from image architecture
var type = Assembly.GetExecutingAssembly().GetType("Il2CppInspector.Il2CppBinary" + stream.Arch.ToUpper());
if (type == null)
throw new NotImplementedException("Unsupported architecture: " + stream.Arch);
var inst = (Il2CppBinary) Activator.CreateInstance(type, stream);
// Try to process the IL2CPP image; return the instance if succeeded, otherwise null
return inst.Initialize(metadataVersion) ? inst : null;
}
// Architecture-specific search function
protected abstract (ulong, ulong) ConsiderCode(IFileFormatReader image, uint loc);
// Check all search locations
public bool Initialize(double version, uint imageIndex = 0) {
var subImage = Image[imageIndex];
subImage.Version = version;
// Try searching the symbol table
var symbols = subImage.GetSymbolTable();
if (symbols?.Any() ?? false) {
Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
symbols.TryGetValue("g_CodeRegistration", out var code);
symbols.TryGetValue("g_MetadataRegistration", out var metadata);
if (code == 0)
symbols.TryGetValue("_g_CodeRegistration", out code);
if (metadata == 0)
symbols.TryGetValue("_g_MetadataRegistration", out metadata);
if (code != 0 && metadata != 0) {
Console.WriteLine("Required structures acquired from symbol lookup");
Configure(subImage, code, metadata);
return true;
}
else {
Console.WriteLine("No matches in symbol table");
}
}
else if (symbols != null) {
Console.WriteLine("No symbol table present in binary file");
}
else {
Console.WriteLine("Symbol table search not implemented for this binary format");
}
// Try searching the function table
var addrs = subImage.GetFunctionTable();
Debug.WriteLine("Function table:");
Debug.WriteLine(string.Join(", ", from a in addrs select string.Format($"0x{a:X8}")));
foreach (var loc in addrs)
if (loc != 0) {
var (code, metadata) = ConsiderCode(subImage, loc);
if (code != 0) {
Console.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", loc + subImage.GlobalOffset);
Configure(subImage, code, metadata);
return true;
}
}
Console.WriteLine("No matches via code heuristics");
return false;
}
private void Configure(IFileFormatReader image, ulong codeRegistration, ulong metadataRegistration) {
// Output locations
Console.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, image.MapVATR(codeRegistration));
Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, image.MapVATR(metadataRegistration));
// Set width of long (convert to sizeof(int) for 32-bit files)
if (image.Bits == 32) {
image.Stream.PrimitiveMappings.Add(typeof(long), typeof(int));
image.Stream.PrimitiveMappings.Add(typeof(ulong), typeof(uint));
}
// Root structures from which we find everything else
CodeRegistration = image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
MetadataRegistration = image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
// The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule
if (Image.Version <= 24.1)
GlobalMethodPointers = image.ReadMappedArray<ulong>(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount);
// After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules
if (Image.Version >= 24.2) {
Modules = new Dictionary<string, Il2CppCodeGenModule>();
// Array of pointers to Il2CppCodeGenModule
var modules = image.ReadMappedObjectPointerArray<Il2CppCodeGenModule>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount);
foreach (var module in modules) {
var name = image.ReadMappedNullTerminatedString(module.moduleName);
Modules.Add(name, module);
// Read method pointers
ModuleMethodPointers.Add(module, image.ReadMappedArray<ulong>(module.methodPointers, (int) module.methodPointerCount));
}
}
// Field offset data. Metadata <=21.x uses a value-type array; >=21.x uses a pointer array
// Versions from 22 onwards use an array of pointers in Binary.FieldOffsetData
bool fieldOffsetsArePointers = (image.Version >= 22);
// Some variants of 21 also use an array of pointers
if (image.Version == 21) {
// Always 4-byte values even for 64-bit builds when array is NOT pointers
var fieldTest = image.ReadMappedArray<uint>(MetadataRegistration.pfieldOffsets, 6);
// We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32
// are always the first six defined types, and that all but Int32 have no fields
fieldOffsetsArePointers = (fieldTest[0] == 0 && fieldTest[1] == 0 && fieldTest[2] == 0 && fieldTest[3] == 0 && fieldTest[4] == 0 && fieldTest[5] > 0);
}
// All older versions use values directly in the array
if (!fieldOffsetsArePointers)
FieldOffsets = image.ReadMappedArray<uint>(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount);
else
FieldOffsetPointers = image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount);
// Type definitions (pointer array)
Types = image.ReadMappedObjectPointerArray<Il2CppType>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount);
// Custom attribute constructors
CustomAttributeGenerators = image.ReadMappedArray<ulong>(CodeRegistration.customAttributeGenerators, (int) CodeRegistration.customAttributeCount);
// Generic method specs
MethodSpecs = image.ReadMappedArray<Il2CppMethodSpec>(MetadataRegistration.methodSpecs, (int) MetadataRegistration.methodSpecsCount);
// Metadata usages (addresses)
MetadataUsages = image.ReadMappedArray<ulong>(MetadataRegistration.metadataUsages, (int)MetadataRegistration.metadataUsagesCount);
}
}
}

View File

@@ -0,0 +1,219 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
// From class-internals.h / il2cpp-class-internals.h
public class Il2CppCodeRegistration
{
// Moved to Il2CppCodeGenModule in v24.2
[Version(Max = 24.1)]
public ulong methodPointersCount;
[Version(Max = 24.1)]
public ulong pmethodPointers;
public ulong reversePInvokeWrapperCount; // (was renamed from delegateWrappersFromNativeToManagedCount in v22)
public ulong reversePInvokeWrappers; // (was renamed from delegateWrappersFromNativeToManaged in v22)
// Removed in metadata v23
[Version(Max = 22)]
public ulong delegateWrappersFromManagedToNativeCount;
[Version(Max = 22)]
public ulong delegateWrappersFromManagedToNative;
[Version(Max = 22)]
public ulong marshalingFunctionsCount;
[Version(Max = 22)]
public ulong marshalingFunctions;
[Version(Min = 21, Max = 22)]
public ulong ccwMarshalingFunctionsCount;
[Version(Min = 21, Max = 22)]
public ulong ccwMarshalingFunctions;
public ulong genericMethodPointersCount;
public ulong genericMethodPointers;
public ulong invokerPointersCount;
public ulong invokerPointers;
public long customAttributeCount;
public ulong customAttributeGenerators;
// Removed in metadata v23
[Version(Min = 21, Max = 22)]
public long guidCount;
[Version(Min = 21, Max = 22)]
public ulong guids; // Il2CppGuid
// Added in metadata v22
[Version(Min = 22)]
public ulong unresolvedVirtualCallCount;
[Version(Min = 22)]
public ulong unresolvedVirtualCallPointers;
// Added in metadata v23
[Version(Min = 23)]
public ulong interopDataCount;
[Version(Min = 23)]
public ulong interopData;
// Added in metadata v24.2 to replace methodPointers and methodPointersCount
[Version(Min = 24.2)]
public ulong codeGenModulesCount;
[Version(Min = 24.2)]
public ulong pcodeGenModules;
}
// Introduced in metadata v24.2 (replaces method pointers in Il2CppCodeRegistration)
public class Il2CppCodeGenModule
{
public ulong moduleName;
public ulong methodPointerCount;
public ulong methodPointers;
public ulong invokerIndices;
public ulong reversePInvokeWrapperCount;
public ulong reversePInvokeWrapperIndices;
public ulong rgctxRangesCount;
public ulong rgctxRanges;
public ulong rgctxsCount;
public ulong rgctxs;
public ulong debuggerMetadata;
}
#pragma warning disable CS0649
public class Il2CppMetadataRegistration
{
public long genericClassesCount;
public ulong genericClasses;
public long genericInstsCount;
public ulong genericInsts;
public long genericMethodTableCount;
public ulong genericMethodTable; // Il2CppGenericMethodFunctionsDefinitions
public long typesCount;
public ulong ptypes;
public long methodSpecsCount;
public ulong methodSpecs;
[Version(Max = 16)]
public long methodReferencesCount;
[Version(Max = 16)]
public ulong methodReferences;
public long fieldOffsetsCount;
public ulong pfieldOffsets; // Changed from int32_t* to int32_t** after 5.4.0f3, before 5.5.0f3
public long typeDefinitionsSizesCount;
public ulong typeDefinitionsSizes;
[Version(Min = 19)]
public ulong metadataUsagesCount;
[Version(Min = 19)]
public ulong metadataUsages;
}
#pragma warning restore CS0649
// From blob.h / il2cpp-blob.h
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 */
}
// From metadata.h / il2cpp-runtime-metadata.h
public class Il2CppType
{
/*
union
{
TypeDefinitionIndex klassIndex; // for VALUETYPE and CLASS
const Il2CppType* type; // for PTR and SZARRAY
Il2CppArrayType* array; // for ARRAY
GenericParameterIndex genericParameterIndex; // for VAR and MVAR
Il2CppGenericClass* generic_class; // for GENERICINST
}
*/
public ulong datapoint;
public ulong bits; // this should be private but we need it to be public for BinaryObjectReader to work
public uint attrs => (uint) bits & 0xffff; /* param attributes or field flags */
public Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff);
public uint num_mods => (uint) (bits >> 24) & 0x3f; /* max 64 modifiers follow at the end */
public bool byref => ((bits >> 30) & 1) == 1;
public bool pinned => (bits >> 31) == 1; /* valid when included in a local var signature */
}
public class Il2CppGenericClass
{
public long typeDefinitionIndex; /* the generic type definition */
public Il2CppGenericContext context; /* a context that contains the type instantiation doesn't contain any method instantiation */
public ulong cached_class; /* if present, the Il2CppClass corresponding to the instantiation. */
}
public class Il2CppGenericContext
{
/* The instantiation corresponding to the class generic parameters */
public ulong class_inst;
/* The instantiation corresponding to the method generic parameters */
public ulong method_inst;
}
public class Il2CppGenericInst
{
public ulong type_argc;
public ulong type_argv;
}
public class Il2CppArrayType
{
public ulong etype;
public byte rank;
public byte numsizes;
public byte numlobounds;
public ulong sizes;
public ulong lobounds;
}
public class Il2CppMethodSpec
{
public int methodDefinitionIndex;
public int classIndexIndex;
public int methodIndexIndex;
}
}

View File

@@ -0,0 +1,244 @@
using System.Collections.Generic;
// Constants from il2cpp/tabledefs.h
namespace Il2CppInspector
{
public static class Il2CppConstants
{
/*
* Field Attributes (21.1.5).
*/
public const int FIELD_ATTRIBUTE_FIELD_ACCESS_MASK = 0x0007;
public const int FIELD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000;
public const int FIELD_ATTRIBUTE_PRIVATE = 0x0001;
public const int FIELD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002;
public const int FIELD_ATTRIBUTE_ASSEMBLY = 0x0003;
public const int FIELD_ATTRIBUTE_FAMILY = 0x0004;
public const int FIELD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005;
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 FIELD_ATTRIBUTE_LITERAL = 0x0040;
public const int FIELD_ATTRIBUTE_NOT_SERIALIZED = 0x0080;
public const int FIELD_ATTRIBUTE_SPECIAL_NAME = 0x0200;
public const int FIELD_ATTRIBUTE_PINVOKE_IMPL = 0x2000;
/* For runtime use only */
public const int FIELD_ATTRIBUTE_RESERVED_MASK = 0x9500;
public const int FIELD_ATTRIBUTE_RT_SPECIAL_NAME = 0x0400;
public const int FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL = 0x1000;
public const int FIELD_ATTRIBUTE_HAS_DEFAULT = 0x8000;
public const int FIELD_ATTRIBUTE_HAS_FIELD_RVA = 0x0100;
/*
* Method Attributes (22.1.9)
*/
public const int METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK = 0x0003;
public const int METHOD_IMPL_ATTRIBUTE_IL = 0x0000;
public const int METHOD_IMPL_ATTRIBUTE_NATIVE = 0x0001;
public const int METHOD_IMPL_ATTRIBUTE_OPTIL = 0x0002;
public const int METHOD_IMPL_ATTRIBUTE_RUNTIME = 0x0003;
public const int METHOD_IMPL_ATTRIBUTE_MANAGED_MASK = 0x0004;
public const int METHOD_IMPL_ATTRIBUTE_UNMANAGED = 0x0004;
public const int METHOD_IMPL_ATTRIBUTE_MANAGED = 0x0000;
public const int METHOD_IMPL_ATTRIBUTE_FORWARD_REF = 0x0010;
public const int METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG = 0x0080;
public const int METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL = 0x1000;
public const int METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED = 0x0020;
public const int METHOD_IMPL_ATTRIBUTE_NOINLINING = 0x0008;
public const int METHOD_IMPL_ATTRIBUTE_MAX_METHOD_IMPL_VAL = 0xffff;
public const int METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK = 0x0007;
public const int METHOD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000;
public const int METHOD_ATTRIBUTE_PRIVATE = 0x0001;
public const int METHOD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002;
public const int METHOD_ATTRIBUTE_ASSEM = 0x0003;
public const int METHOD_ATTRIBUTE_FAMILY = 0x0004;
public const int METHOD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005;
public const int METHOD_ATTRIBUTE_PUBLIC = 0x0006;
public const int METHOD_ATTRIBUTE_STATIC = 0x0010;
public const int METHOD_ATTRIBUTE_FINAL = 0x0020;
public const int METHOD_ATTRIBUTE_VIRTUAL = 0x0040;
public const int METHOD_ATTRIBUTE_HIDE_BY_SIG = 0x0080;
public const int METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK = 0x0100;
public const int METHOD_ATTRIBUTE_REUSE_SLOT = 0x0000;
public const int METHOD_ATTRIBUTE_NEW_SLOT = 0x0100;
public const int METHOD_ATTRIBUTE_STRICT = 0x0200;
public const int METHOD_ATTRIBUTE_ABSTRACT = 0x0400;
public const int METHOD_ATTRIBUTE_SPECIAL_NAME = 0x0800;
public const int METHOD_ATTRIBUTE_PINVOKE_IMPL = 0x2000;
public const int METHOD_ATTRIBUTE_UNMANAGED_EXPORT = 0x0008;
/*
* For runtime use only
*/
public const int METHOD_ATTRIBUTE_RESERVED_MASK = 0xd000;
public const int METHOD_ATTRIBUTE_RT_SPECIAL_NAME = 0x1000;
public const int METHOD_ATTRIBUTE_HAS_SECURITY = 0x4000;
public const int METHOD_ATTRIBUTE_REQUIRE_SEC_OBJECT = 0x8000;
/*
* Type Attributes (21.1.13).
*/
public const int TYPE_ATTRIBUTE_VISIBILITY_MASK = 0x00000007;
public const int TYPE_ATTRIBUTE_NOT_PUBLIC = 0x00000000;
public const int TYPE_ATTRIBUTE_PUBLIC = 0x00000001;
public const int TYPE_ATTRIBUTE_NESTED_PUBLIC = 0x00000002;
public const int TYPE_ATTRIBUTE_NESTED_PRIVATE = 0x00000003;
public const int TYPE_ATTRIBUTE_NESTED_FAMILY = 0x00000004;
public const int TYPE_ATTRIBUTE_NESTED_ASSEMBLY = 0x00000005;
public const int TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM = 0x00000006;
public const int TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM = 0x00000007;
public const int TYPE_ATTRIBUTE_LAYOUT_MASK = 0x00000018;
public const int TYPE_ATTRIBUTE_AUTO_LAYOUT = 0x00000000;
public const int TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT = 0x00000008;
public const int TYPE_ATTRIBUTE_EXPLICIT_LAYOUT = 0x00000010;
public const int TYPE_ATTRIBUTE_CLASS_SEMANTIC_MASK = 0x00000020;
public const int TYPE_ATTRIBUTE_CLASS = 0x00000000;
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_SPECIAL_NAME = 0x00000400;
public const int TYPE_ATTRIBUTE_IMPORT = 0x00001000;
public const int TYPE_ATTRIBUTE_SERIALIZABLE = 0x00002000;
public const int TYPE_ATTRIBUTE_STRING_FORMAT_MASK = 0x00030000;
public const int TYPE_ATTRIBUTE_ANSI_CLASS = 0x00000000;
public const int TYPE_ATTRIBUTE_UNICODE_CLASS = 0x00010000;
public const int TYPE_ATTRIBUTE_AUTO_CLASS = 0x00020000;
public const int TYPE_ATTRIBUTE_BEFORE_FIELD_INIT = 0x00100000;
public const int TYPE_ATTRIBUTE_FORWARDER = 0x00200000;
public const int TYPE_ATTRIBUTE_RESERVED_MASK = 0x00040800;
public const int TYPE_ATTRIBUTE_RT_SPECIAL_NAME = 0x00000800;
public const int TYPE_ATTRIBUTE_HAS_SECURITY = 0x00040000;
/*
* Flags for Params (22.1.12)
*/
public const int PARAM_ATTRIBUTE_IN = 0x0001;
public const int PARAM_ATTRIBUTE_OUT = 0x0002;
public const int PARAM_ATTRIBUTE_OPTIONAL = 0x0010;
public const int PARAM_ATTRIBUTE_RESERVED_MASK = 0xf000;
public const int PARAM_ATTRIBUTE_HAS_DEFAULT = 0x1000;
public const int PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL = 0x2000;
public const int PARAM_ATTRIBUTE_UNUSED = 0xcfe0;
// Flags for Generic Parameters (II.23.1.7)
public const int GENERIC_PARAMETER_ATTRIBUTE_NON_VARIANT = 0x00;
public const int GENERIC_PARAMETER_ATTRIBUTE_COVARIANT = 0x01;
public const int GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT = 0x02;
public const int GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK = 0x03;
public const int GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT = 0x04;
public const int GENERIC_PARAMETER_ATTRIBUTE_NOT_NULLABLE_VALUE_TYPE_CONSTRAINT = 0x08;
public const int GENERIC_PARAMETER_ATTRIBUTE_DEFAULT_CONSTRUCTOR_CONSTRAINT = 0x10;
public const int GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINT_MASK = 0x1C;
/**
* 21.5 AssemblyRefs
*/
public const int ASSEMBLYREF_FULL_PUBLIC_KEY_FLAG = 0x00000001;
public const int ASSEMBLYREF_RETARGETABLE_FLAG = 0x00000100;
public const int ASSEMBLYREF_ENABLEJITCOMPILE_TRACKING_FLAG = 0x00008000;
public const int ASSEMBLYREF_DISABLEJITCOMPILE_OPTIMIZER_FLAG = 0x00004000;
// Naming conventions (follows order of Il2CppTypeEnum)
public static List<string> CSharpTypeString = new List<string>
{
"END",
"void",
"bool",
"char",
"sbyte",
"byte",
"short",
"ushort",
"int",
"uint",
"long",
"ulong",
"float",
"double",
"string",
"PTR", // Processed separately
"BYREF",
"ValueType", // Processed separately
"CLASS", // Processed separately
"T",
"Array", // Processed separately
"GENERICINST", // Processed separately
"TypedReference", // params
"None",
"IntPtr",
"UIntPtr",
"None",
"Delegate",
"object",
"SZARRAY", // Processed separately
"T",
"CMOD_REQD",
"CMOD_OPT",
"INTERNAL",
// Added in for convenience
"decimal"
};
public static List<string> FullNameTypeString = new List<string>
{
"END",
"System.Void",
"System.Boolean",
"System.Char",
"System.SByte",
"System.Byte",
"System.Int16",
"System.UInt16",
"System.Int32",
"System.UInt32",
"System.Int64",
"System.UInt64",
"System.Single",
"System.Double",
"System.String",
"PTR", // Processed separately
"BYREF",
"System.ValueType", // Processed separately
"CLASS", // Processed separately
"T",
"System.Array", // Processed separately
"GENERICINST", // Processed separately
"System.TypedReference", // params
"None",
"System.IntPtr",
"System.UIntPtr",
"None",
"System.Delegate",
"System.Object",
"SZARRAY", // Processed separately
"T",
"CMOD_REQD",
"CMOD_OPT",
"INTERNAL",
// Added in for convenience
"System.Decimal"
};
}
}

View File

@@ -0,0 +1,280 @@
/*
Copyright 2017-2020 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Il2CppInspector
{
// Il2CppInspector ties together the binary and metadata files into a congruent API surface
public class Il2CppInspector
{
private Il2CppBinary Binary { get; }
private Metadata Metadata { get; }
// All function pointers including attribute initialization functions etc. (start => end)
public Dictionary<ulong, ulong> FunctionAddresses { get; }
// Attribute indexes (>=24.1) arranged by customAttributeStart and token
public Dictionary<int, Dictionary<uint, int>> AttributeIndicesByToken { get; }
// Shortcuts
public double Version => Metadata.Version;
public Dictionary<int, string> Strings => Metadata.Strings;
public Il2CppTypeDefinition[] TypeDefinitions => Metadata.Types;
public Il2CppAssemblyDefinition[] Assemblies => Metadata.Assemblies;
public Il2CppImageDefinition[] Images => Metadata.Images;
public Il2CppMethodDefinition[] Methods => Metadata.Methods;
public Il2CppParameterDefinition[] Params => Metadata.Params;
public Il2CppFieldDefinition[] Fields => Metadata.Fields;
public Il2CppPropertyDefinition[] Properties => Metadata.Properties;
public Il2CppEventDefinition[] Events => Metadata.Events;
public Il2CppGenericContainer[] GenericContainers => Metadata.GenericContainers;
public Il2CppGenericParameter[] GenericParameters => Metadata.GenericParameters;
public int[] GenericConstraintIndices => Metadata.GenericConstraintIndices;
public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges => Metadata.AttributeTypeRanges;
public Il2CppInterfaceOffsetPair[] InterfaceOffsets => Metadata.InterfaceOffsets;
public List<MetadataUsage> MetadataUsages => Metadata.MetadataUsages;
public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices;
public int[] NestedTypeIndices => Metadata.NestedTypeIndices;
public int[] AttributeTypeIndices => Metadata.AttributeTypeIndices;
public uint[] VTableMethodIndices => Metadata.VTableMethodIndices;
public Dictionary<int, (ulong, object)> FieldDefaultValue { get; } = new Dictionary<int, (ulong, object)>();
public Dictionary<int, (ulong, object)> ParameterDefaultValue { get; } = new Dictionary<int, (ulong, object)>();
public List<long> FieldOffsets { get; }
public List<Il2CppType> TypeUsages => Binary.Types;
public Dictionary<string, Il2CppCodeGenModule> Modules => Binary.Modules;
public ulong[] CustomAttributeGenerators => Binary.CustomAttributeGenerators;
public Il2CppMethodSpec[] MethodSpecs => Binary.MethodSpecs;
public ulong[] BinaryMetadataUsages => Binary.MetadataUsages;
// TODO: Finish all file access in the constructor and eliminate the need for this
public IFileFormatReader BinaryImage => Binary.Image;
private (ulong MetadataAddress, object Value)? getDefaultValue(int typeIndex, int dataIndex) {
// No default
if (dataIndex == -1)
return (0ul, null);
// Get pointer in binary to default value
var pValue = Metadata.Header.fieldAndParameterDefaultValueDataOffset + dataIndex;
var type = TypeUsages[typeIndex];
// Default value is null
if (pValue == 0)
return (0ul, null);
object value = null;
Metadata.Position = pValue;
switch (type.type) {
case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
value = Metadata.ReadBoolean();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U1:
case Il2CppTypeEnum.IL2CPP_TYPE_I1:
value = Metadata.ReadByte();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
// UTF-8 character assumed
value = BitConverter.ToChar(Metadata.ReadBytes(2), 0);
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U2:
value = Metadata.ReadUInt16();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_I2:
value = Metadata.ReadInt16();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U4:
value = Metadata.ReadUInt32();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_I4:
value = Metadata.ReadInt32();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_U8:
value = Metadata.ReadUInt64();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_I8:
value = Metadata.ReadInt64();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_R4:
value = Metadata.ReadSingle();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_R8:
value = Metadata.ReadDouble();
break;
case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
var uiLen = Metadata.ReadInt32();
value = Encoding.UTF8.GetString(Metadata.ReadBytes(uiLen));
break;
}
return ((ulong) pValue, value);
}
public Il2CppInspector(Il2CppBinary binary, Metadata metadata) {
// Store stream representations
Binary = binary;
Metadata = metadata;
// Get all field default values
foreach (var fdv in Metadata.FieldDefaultValues)
FieldDefaultValue.Add(fdv.fieldIndex, ((ulong,object)) getDefaultValue(fdv.typeIndex, fdv.dataIndex));
// Get all parameter default values
foreach (var pdv in Metadata.ParameterDefaultValues)
ParameterDefaultValue.Add(pdv.parameterIndex, ((ulong,object)) getDefaultValue(pdv.typeIndex, pdv.dataIndex));
// Get all field offsets
if (Binary.FieldOffsets != null) {
FieldOffsets = Binary.FieldOffsets.Select(x => (long) x).ToList();
}
// Convert pointer list into fields
else {
var offsets = new Dictionary<int, long>();
for (var i = 0; i < TypeDefinitions.Length; i++) {
var def = TypeDefinitions[i];
var pFieldOffsets = Binary.FieldOffsetPointers[i];
if (pFieldOffsets != 0) {
bool available = true;
// If the target address range is not mapped in the file, assume zeroes
try {
BinaryImage.Position = BinaryImage.MapVATR((ulong) pFieldOffsets);
}
catch (InvalidOperationException) {
available = false;
}
for (var f = 0; f < def.field_count; f++)
offsets.Add(def.fieldStart + f, available? BinaryImage.ReadUInt32() : 0);
}
}
FieldOffsets = offsets.OrderBy(x => x.Key).Select(x => x.Value).ToList();
}
// Get sorted list of function pointers from all sources
var sortedFunctionPointers = (Version <= 24.1)?
Binary.GlobalMethodPointers.ToList() :
Binary.ModuleMethodPointers.SelectMany(module => module.Value).ToList();
sortedFunctionPointers.AddRange(CustomAttributeGenerators);
sortedFunctionPointers.Sort();
sortedFunctionPointers = sortedFunctionPointers.Distinct().ToList();
// Guestimate function end addresses
FunctionAddresses = new Dictionary<ulong, ulong>(sortedFunctionPointers.Count);
for (var i = 0; i < sortedFunctionPointers.Count - 1; i++)
FunctionAddresses.Add(sortedFunctionPointers[i], sortedFunctionPointers[i + 1]);
// The last method end pointer will be incorrect but there is no way of calculating it
FunctionAddresses.Add(sortedFunctionPointers[^1], sortedFunctionPointers[^1]);
// Organize custom attribute indices
if (Version >= 24.1) {
AttributeIndicesByToken = new Dictionary<int, Dictionary<uint, int>>();
foreach (var image in Images) {
var attsByToken = new Dictionary<uint, int>();
for (int i = 0; i < image.customAttributeCount; i++) {
var index = image.customAttributeStart + i;
var token = AttributeTypeRanges[index].token;
attsByToken.Add(token, index);
}
AttributeIndicesByToken.Add(image.customAttributeStart, attsByToken);
}
}
}
public (ulong Start, ulong End)? GetMethodPointer(Il2CppCodeGenModule module, Il2CppMethodDefinition methodDef) {
// Find method pointer
if (methodDef.methodIndex < 0)
return null;
ulong start = 0;
// Global method pointer array
if (Version <= 24.1) {
start = Binary.GlobalMethodPointers[methodDef.methodIndex];
}
// Per-module method pointer array uses the bottom 24 bits of the method's metadata token
// Derived from il2cpp::vm::MetadataCache::GetMethodPointer
if (Version >= 24.2) {
var method = (methodDef.token & 0xffffff);
if (method == 0)
return null;
// In the event of an exception, the method pointer is not set in the file
// This probably means it has been optimized away by the compiler, or is an unused generic method
try {
// Remove ARM Thumb marker LSB if necessary
start = Binary.ModuleMethodPointers[module][method - 1];
}
catch (IndexOutOfRangeException) {
return null;
}
}
if (start == 0)
return null;
// Consider the end of the method to be the start of the next method (or zero)
// The last method end will be wrong but there is no way to calculate it
return (start & 0xffff_ffff_ffff_fffe, FunctionAddresses[start]);
}
public static List<Il2CppInspector> LoadFromFile(string codeFile, string metadataFile) {
// Load the metadata file
Metadata metadata;
try {
metadata = new Metadata(new MemoryStream(File.ReadAllBytes(metadataFile)));
}
catch (Exception ex) {
Console.Error.WriteLine(ex.Message);
return null;
}
Console.WriteLine("Detected metadata version " + metadata.Version);
// Load the il2cpp code file (try all available file formats)
IFileFormatReader stream = FileFormatReader.Load(codeFile);
if (stream == null) {
Console.Error.WriteLine("Unsupported executable file format");
return null;
}
// Multi-image binaries may contain more than one Il2Cpp image
var processors = new List<Il2CppInspector>();
foreach (var image in stream.Images) {
Console.WriteLine("Container format: " + image.Format);
Console.WriteLine("Container endianness: " + ((BinaryObjectReader) image).Endianness);
Console.WriteLine("Architecture word size: {0}-bit", image.Bits);
Console.WriteLine("Instruction set: " + image.Arch);
Console.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
// Architecture-agnostic load attempt
try {
if (Il2CppBinary.Load(image, metadata.Version) is Il2CppBinary binary) {
processors.Add(new Il2CppInspector(binary, metadata));
}
else {
Console.Error.WriteLine("Could not process IL2CPP image. This may mean the binary file is packed, encrypted or obfuscated, that the file is not an IL2CPP image or that Il2CppInspector was not able to automatically find the required data.");
Console.Error.WriteLine("Please check the binary file in a disassembler to ensure that it is an unencrypted IL2CPP binary before submitting a bug report!");
}
}
// Unknown architecture
catch (NotImplementedException ex) {
Console.Error.WriteLine(ex.Message);
}
}
return processors;
}
}
}

View File

@@ -0,0 +1,134 @@
/*
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Il2CppInspector.Reflection
{
public class Il2CppModel
{
public Il2CppInspector Package { get; }
public List<Assembly> Assemblies { get; } = new List<Assembly>();
// List of all types ordered by their TypeDefinitionIndex
public TypeInfo[] TypesByDefinitionIndex { get; }
// List of all type definitions by fully qualified name
public Dictionary<string, TypeInfo> TypesByFullName { get; } = new Dictionary<string, TypeInfo>();
// List of all type usages ordered by their type usage index
public TypeInfo[] TypesByUsageIndex { get; }
// List of type usages that are initialized via pointers in the image
public ConcurrentDictionary<ulong, TypeInfo> TypesByVirtualAddress { get; } = new ConcurrentDictionary<ulong, TypeInfo>();
// Every type
public IEnumerable<TypeInfo> Types => new IEnumerable<TypeInfo>[] { TypesByDefinitionIndex, TypesByUsageIndex, TypesByVirtualAddress.Values }.SelectMany(t => t);
// List of all methods ordered by their MethodDefinitionIndex
public MethodBase[] MethodsByDefinitionIndex { get; }
// List of all generated CustomAttributeData objects by their index into AttributeTypeIndices
public ConcurrentDictionary<int, CustomAttributeData> AttributesByIndices { get; } = new ConcurrentDictionary<int, CustomAttributeData>();
public Il2CppModel(Il2CppInspector package) {
Package = package;
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
TypesByUsageIndex = new TypeInfo[package.TypeUsages.Count];
MethodsByDefinitionIndex = new MethodBase[package.Methods.Length];
// Create Assembly objects from Il2Cpp package
for (var image = 0; image < package.Images.Length; image++)
Assemblies.Add(new Assembly(this, image));
}
// Get an assembly by its image name
public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name);
private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) {
TypeInfo underlyingType;
switch (usage.type) {
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
// Classes defined in the metadata
underlyingType = TypesByDefinitionIndex[usage.datapoint]; // klassIndex
break;
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
// Everything that requires special handling
underlyingType = new TypeInfo(this, usage, memberType);
break;
default:
// Primitive types
underlyingType = GetTypeFromTypeEnum(usage.type);
break;
}
// Create a reference type if necessary
return usage.byref? underlyingType.MakeByRefType() : underlyingType;
}
// Get or generate a type from its IL2CPP binary type usage reference
// (field, return type, generic type parameter etc.)
public TypeInfo GetTypeFromUsage(int typeUsageIndex, MemberTypes memberType = MemberTypes.All) {
// Already generated type previously?
if (TypesByUsageIndex[typeUsageIndex] != null)
return TypesByUsageIndex[typeUsageIndex];
var usage = Package.TypeUsages[typeUsageIndex];
var newUsage = getNewTypeUsage(usage, memberType);
TypesByUsageIndex[typeUsageIndex] = newUsage;
return newUsage;
}
// Basic primitive types
public TypeInfo GetTypeFromTypeEnum(Il2CppTypeEnum t) {
if ((int)t >= Il2CppConstants.FullNameTypeString.Count)
return null;
var fqn = Il2CppConstants.FullNameTypeString[(int) t];
return TypesByFullName[fqn];
}
// Type from a virtual address pointer
// These are always nested types frorm usages within another type
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
if (TypesByVirtualAddress.ContainsKey(ptr))
return TypesByVirtualAddress[ptr];
var type = Package.BinaryImage.ReadMappedObject<Il2CppType>(ptr);
var newUsage = getNewTypeUsage(type, MemberTypes.NestedType);
TypesByVirtualAddress.TryAdd(ptr, newUsage);
return newUsage;
}
// The attribute index is an index into AttributeTypeRanges, each of which is a start-end range index into AttributeTypeIndices, each of which is a TypeIndex
public int GetCustomAttributeIndex(Assembly asm, uint token, int customAttributeIndex) {
// Prior to v24.1, Type, Field, Parameter, Method, Event, Property, Assembly definitions had their own customAttributeIndex field
if (Package.Version <= 24.0)
return customAttributeIndex;
// From v24.1 onwards, token was added to Il2CppCustomAttributeTypeRange and each Il2CppImageDefinition noted the CustomAttributeTypeRanges for the image
if (!Package.AttributeIndicesByToken[asm.ImageDefinition.customAttributeStart].TryGetValue(token, out var index))
return -1;
return index;
}
}
}

View File

@@ -0,0 +1,195 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
public class Metadata : BinaryObjectReader
{
public Il2CppGlobalMetadataHeader Header;
public Il2CppAssemblyDefinition[] Assemblies { get; }
public Il2CppImageDefinition[] Images { get; }
public Il2CppTypeDefinition[] Types { get; }
public Il2CppMethodDefinition[] Methods { get; }
public Il2CppParameterDefinition[] Params { get; }
public Il2CppFieldDefinition[] Fields { get; }
public Il2CppFieldDefaultValue[] FieldDefaultValues { get; }
public Il2CppParameterDefaultValue[] ParameterDefaultValues { get; }
public Il2CppPropertyDefinition[] Properties { get; }
public Il2CppEventDefinition[] Events { get; }
public Il2CppGenericContainer[] GenericContainers { get; }
public Il2CppGenericParameter[] GenericParameters { get; }
public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; }
public Il2CppInterfaceOffsetPair[] InterfaceOffsets { get; }
public Il2CppMetadataUsageList[] MetadataUsageLists { get; }
public Il2CppMetadataUsagePair[] MetadataUsagePairs { get; }
public int[] InterfaceUsageIndices { get; }
public int[] NestedTypeIndices { get; }
public int[] AttributeTypeIndices { get; }
public int[] GenericConstraintIndices { get; }
public uint[] VTableMethodIndices { get; }
public Dictionary<int, string> Strings { get; } = new Dictionary<int, string>();
public List<MetadataUsage> MetadataUsages { get; } = new List<MetadataUsage>();
public Metadata(Stream stream) : base(stream)
{
// Read magic bytes
if (ReadUInt32() != 0xFAB11BAF) {
throw new InvalidOperationException("ERROR: Metadata file supplied is not valid metadata file.");
}
// Set object versioning for Bin2Object from metadata version
Version = ReadInt32();
// Rewind and read metadata header in full
Header = ReadObject<Il2CppGlobalMetadataHeader>(0);
if (Version < 16 || Version > 24)
{
throw new InvalidOperationException($"ERROR: Metadata file supplied is not a supported version ({Header.version}).");
}
// Sanity checking
// Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata always writes the metadata information in the same order it appears in the header,
// with each block always coming directly after the previous block, 4-byte aligned. We can use this to check the integrity of the data and
// detect sub-versions.
// For metadata v24, the header can either be either 0x110 (24.0, 24.1) or 0x108 (24.2) bytes long. Since 'stringLiteralOffset' is the first thing
// in the header after the sanity and version fields, and since it will always point directly to the first byte after the end of the header,
// we can use this value to determine the actual header length and therefore narrow down the metadata version to 24.0/24.1 or 24.2.
var realHeaderLength = Header.stringLiteralOffset;
if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) {
if (Version == 24.0) {
Version = 24.2;
Header = ReadObject<Il2CppGlobalMetadataHeader>(0);
}
}
if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) {
throw new InvalidOperationException("ERROR: Could not verify the integrity of the metadata file or accurately identify the metadata sub-version");
}
// Load all the relevant metadata using offsets provided in the header
if (Version >= 16)
Images = ReadArray<Il2CppImageDefinition>(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition)));
// As an additional sanity check, all images in the metadata should have Mono.Cecil.MetadataToken == 1
// In metadata v24.1, two extra fields were added which will cause the below test to fail.
// In that case, we can then adjust the version number and reload
// Tokens were introduced in v19 - we don't bother testing earlier versions
if (Version >= 19 && Images.Any(x => x.token != 1))
if (Version == 24.0) {
Version = 24.1;
// No need to re-read the header, it's the same for both sub-versions
Images = ReadArray<Il2CppImageDefinition>(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition)));
if (Images.Any(x => x.token != 1))
throw new InvalidOperationException("ERROR: Could not verify the integrity of the metadata file image list");
}
Types = ReadArray<Il2CppTypeDefinition>(Header.typeDefinitionsOffset, Header.typeDefinitionsCount / Sizeof(typeof(Il2CppTypeDefinition)));
Methods = ReadArray<Il2CppMethodDefinition>(Header.methodsOffset, Header.methodsCount / Sizeof(typeof(Il2CppMethodDefinition)));
Params = ReadArray<Il2CppParameterDefinition>(Header.parametersOffset, Header.parametersCount / Sizeof(typeof(Il2CppParameterDefinition)));
Fields = ReadArray<Il2CppFieldDefinition>(Header.fieldsOffset, Header.fieldsCount / Sizeof(typeof(Il2CppFieldDefinition)));
FieldDefaultValues = ReadArray<Il2CppFieldDefaultValue>(Header.fieldDefaultValuesOffset, Header.fieldDefaultValuesCount / Sizeof(typeof(Il2CppFieldDefaultValue)));
Properties = ReadArray<Il2CppPropertyDefinition>(Header.propertiesOffset, Header.propertiesCount / Sizeof(typeof(Il2CppPropertyDefinition)));
Events = ReadArray<Il2CppEventDefinition>(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition)));
InterfaceUsageIndices = ReadArray<int>(Header.interfacesOffset, Header.interfacesCount / sizeof(int));
NestedTypeIndices = ReadArray<int>(Header.nestedTypesOffset, Header.nestedTypesCount / sizeof(int));
GenericContainers = ReadArray<Il2CppGenericContainer>(Header.genericContainersOffset, Header.genericContainersCount / Sizeof(typeof(Il2CppGenericContainer)));
GenericParameters = ReadArray<Il2CppGenericParameter>(Header.genericParametersOffset, Header.genericParametersCount / Sizeof(typeof(Il2CppGenericParameter)));
GenericConstraintIndices = ReadArray<int>(Header.genericParameterConstraintsOffset, Header.genericParameterConstraintsCount / sizeof(int));
InterfaceOffsets = ReadArray<Il2CppInterfaceOffsetPair>(Header.interfaceOffsetsOffset, Header.interfaceOffsetsCount / Sizeof(typeof(Il2CppInterfaceOffsetPair)));
VTableMethodIndices = ReadArray<uint>(Header.vtableMethodsOffset, Header.vtableMethodsCount / sizeof(uint));
if (Version >= 16) {
Assemblies = ReadArray<Il2CppAssemblyDefinition>(Header.assembliesOffset, Header.assembliesCount / Sizeof(typeof(Il2CppAssemblyDefinition)));
ParameterDefaultValues = ReadArray<Il2CppParameterDefaultValue>(Header.parameterDefaultValuesOffset, Header.parameterDefaultValuesCount / Sizeof(typeof(Il2CppParameterDefaultValue)));
}
if (Version >= 19) {
MetadataUsageLists = ReadArray<Il2CppMetadataUsageList>(Header.metadataUsageListsOffset, Header.metadataUsageListsCount / Sizeof(typeof(Il2CppMetadataUsageList)));
MetadataUsagePairs = ReadArray<Il2CppMetadataUsagePair>(Header.metadataUsagePairsOffset, Header.metadataUsagePairsCount / Sizeof(typeof(Il2CppMetadataUsagePair)));
MetadataUsages = buildMetadataUsages();
}
if (Version >= 21) {
AttributeTypeIndices = ReadArray<int>(Header.attributeTypesOffset, Header.attributeTypesCount / sizeof(int));
AttributeTypeRanges = ReadArray<Il2CppCustomAttributeTypeRange>(Header.attributesInfoOffset, Header.attributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange)));
}
// Get all string literals
Position = Header.stringOffset;
while (Position < Header.stringOffset + Header.stringCount)
Strings.Add((int)Position - Header.stringOffset, ReadNullTerminatedString());
}
private List<MetadataUsage> buildMetadataUsages()
{
var usages = new Dictionary<uint, MetadataUsage>();
foreach (var metadataUsageList in MetadataUsageLists)
{
for (var i = 0; i < metadataUsageList.count; i++)
{
var metadataUsagePair = MetadataUsagePairs[metadataUsageList.start + i];
var encodedType = metadataUsagePair.encodedSourceIndex & 0xE0000000;
var usageType = (MetadataUsageType)(encodedType >> 29);
var sourceIndex = metadataUsagePair.encodedSourceIndex & 0x1FFFFFFF;
var destinationIndex = metadataUsagePair.destinationindex;
usages.TryAdd(destinationIndex, new MetadataUsage(usageType, (int)sourceIndex, (int)destinationIndex));
}
}
return usages.Values.ToList();
}
private int Sizeof(Type type)
{
int size = 0;
foreach (var i in type.GetTypeInfo().GetFields())
{
// Only process fields for our selected object versioning
var versionAttr = i.GetCustomAttribute<VersionAttribute>(false);
if (versionAttr != null) {
if (versionAttr.Min != -1 && versionAttr.Min > Version)
continue;
if (versionAttr.Max != -1 && versionAttr.Max < Version)
continue;
}
if (i.FieldType == typeof(int) || i.FieldType == typeof(uint))
size += 4;
else if (i.FieldType == typeof(short) || i.FieldType == typeof(ushort))
size += 2;
// Fixed-length array
else if (i.FieldType.IsArray) {
var attr = i.GetCustomAttribute<ArrayLengthAttribute>(false) ??
throw new InvalidOperationException("Array field " + i.Name + " must have ArrayLength attribute");
size += attr.FixedSize;
}
// Embedded object
else
size += Sizeof(i.FieldType);
}
return size;
}
}
}

View File

@@ -0,0 +1,436 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
// Unity 4.6.1p5 - first release, no global-metadata.dat
// Unity 5.2.0f3 -> v15
// Unity 5.3.0f4 -> v16
// Unity 5.3.1f1 -> v16
// Unity 5.3.2f1 -> v19
// Unity 5.3.3f1 -> v20
// Unity 5.3.4f1 -> v20
// Unity 5.3.5f1 -> v21
// Unity 5.4.0f3 -> v21
// Unity 5.5.0f3 -> v22
// Unity 5.6.2p3 -> v23
// Unity 5.6.4f1 -> v23
// Unity 2017.2f3 -> v24
// Unity 2018.2.0f2 -> v24
// Unity 2018.3.0f2 -> v24.1
// Unity 2019.2.8f1 -> v24.2
// https://unity3d.com/get-unity/download/archive
// Metadata version is written at the end of Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata or WriteMetadata (Unity.IL2CPP.dll)
// From il2cpp-metadata.h
#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;
[Version(Min = 16)]
public int parameterDefaultValuesOffset; // Il2CppParameterDefaultValue
[Version(Min = 16)]
public int parameterDefaultValuesCount;
public int fieldDefaultValuesOffset; // Il2CppFieldDefaultValue
public int fieldDefaultValuesCount;
public int fieldAndParameterDefaultValueDataOffset; // uint8_t
public int fieldAndParameterDefaultValueDataCount;
[Version(Min = 16)]
public int fieldMarshaledSizesOffset; // Il2CppFieldMarshaledSize
[Version(Min = 16)]
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;
[Version(Max = 24.1)]
public int rgctxEntriesOffset; // Il2CppRGCTXDefinition
[Version(Max = 24.1)]
public int rgctxEntriesCount;
[Version(Min = 16)]
public int imagesOffset; // Il2CppImageDefinition
[Version(Min = 16)]
public int imagesCount;
[Version(Min = 16)]
public int assembliesOffset; // Il2CppAssemblyDefinition
[Version(Min = 16)]
public int assembliesCount;
[Version(Min = 19)]
public int metadataUsageListsOffset; // Il2CppMetadataUsageList
[Version(Min = 19)]
public int metadataUsageListsCount;
[Version(Min = 19)]
public int metadataUsagePairsOffset; // Il2CppMetadataUsagePair
[Version(Min = 19)]
public int metadataUsagePairsCount;
[Version(Min = 19)]
public int fieldRefsOffset; // Il2CppFieldRef
[Version(Min = 19)]
public int fieldRefsCount;
[Version(Min = 19)]
public int referencedAssembliesOffset; // int
[Version(Min = 19)]
public int referencedAssembliesCount;
[Version(Min = 21)]
public int attributesInfoOffset; // Il2CppCustomAttributeTypeRange
[Version(Min = 21)]
public int attributesInfoCount;
[Version(Min = 21)]
public int attributeTypesOffset; // TypeIndex
[Version(Min = 21)]
public int attributeTypesCount;
// Added in metadata v22
[Version(Min = 22)]
public int unresolvedVirtualCallParameterTypesOffset; // TypeIndex
[Version(Min = 22)]
public int unresolvedVirtualCallParameterTypesCount;
[Version(Min = 22)]
public int unresolvedVirtualCallParameterRangesOffset; // Il2CppRange
[Version(Min = 22)]
public int unresolvedVirtualCallParameterRangesCount;
// Added in metadata v23
[Version(Min = 23)]
public int windowsRuntimeTypeNamesOffset; // Il2CppWindowsRuntimeTypeNamePair
[Version(Min = 23)]
public int windowsRuntimeTypeNamesSize;
// Added in metadata v24
[Version(Min = 24)]
public int exportedTypeDefinitionsOffset; // TypeDefinitionIndex
[Version(Min = 24)]
public int exportedTypeDefinitionsCount;
}
public class Il2CppImageDefinition
{
public int nameIndex;
public int assemblyIndex;
public int typeStart;
public uint typeCount;
[Version(Min = 24)]
public int exportedTypeStart;
[Version(Min = 24)]
public uint exportedTypeCount;
public int entryPointIndex;
[Version(Min = 19)]
public uint token;
[Version(Min = 24.1)]
public int customAttributeStart;
[Version(Min = 24.1)]
public uint customAttributeCount;
}
#pragma warning restore CS0649
// Renamed from Il2CppAssembly somewhere after Unity 2017.2f3 up to Unity 2018.2.0f2
public class Il2CppAssemblyDefinition
{
// They moved the position of aname in v16 from the top to the bottom of the struct
public Il2CppAssemblyNameDefinition aname => aname_pre16 ?? aname_post16;
[Version(Max = 15)]
public Il2CppAssemblyNameDefinition aname_pre16;
public int imageIndex;
[Version(Min = 24.1)]
public uint token;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 20)]
public int referencedAssemblyStart;
[Version(Min = 20)]
public int referencedAssemblyCount;
[Version(Min = 16)]
public Il2CppAssemblyNameDefinition aname_post16;
}
// Renamed from Il2CppAssemblyName somewhere after Unity 2017.2f3 up to Unity 2018.2.0f2
public class Il2CppAssemblyNameDefinition
{
// They moved the position of publicKeyToken in v16 from the middle to the bottom of the struct
public byte[] publicKeyToken => publicKeyToken_post16;
public int nameIndex;
public int cultureIndex;
public int hashValueIndex;
public int publicKeyIndex;
[Version(Max = 15), ArrayLength(FixedSize = 8)]
public byte[] publicKeyToken_pre16;
public uint hash_alg;
public int hash_len;
public uint flags;
public int major;
public int minor;
public int build;
public int revision;
[Version(Min = 16), ArrayLength(FixedSize = 8)]
public byte[] publicKeyToken_post16;
}
public class Il2CppTypeDefinition
{
public int nameIndex;
public int namespaceIndex;
// Removed in metadata v24.1
[Version(Max = 24.0)]
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
[Version(Max = 24.1)]
public int rgctxStartIndex;
[Version(Max = 24.1)]
public int rgctxCount;
public int genericContainerIndex;
// Removed in metadata v23
[Version(Max = 22)]
public int delegateWrapperFromManagedToNativeIndex; // (was renamed to reversePInvokeWrapperIndex in v22)
[Version(Max = 22)]
public int marshalingFunctionsIndex;
[Version(Min = 21, Max = 22)]
public int ccwFunctionIndex;
[Version(Min = 21, Max = 22)]
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; (from v22: is_import_or_windows_runtime)
// 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128)
public uint bitfield;
[Version(Min = 19)]
public uint token;
}
public class Il2CppMethodDefinition
{
public int nameIndex;
[Version(Min = 16)]
public int declaringType;
public int returnType;
public int parameterStart;
[Version(Max = 24.0)]
public int customAttributeIndex;
public int genericContainerIndex;
[Version(Max = 24.1)]
public int methodIndex;
[Version(Max = 24.1)]
public int invokerIndex;
[Version(Max = 24.1)]
public int reversePInvokeWrapperIndex; // (was renamed from delegateWrapperIndex in v22)
[Version(Max = 24.1)]
public int rgctxStartIndex;
[Version(Max = 24.1)]
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;
[Version(Max = 24.0)]
public int customAttributeIndex;
public int typeIndex;
}
public class Il2CppParameterDefaultValue
{
public int parameterIndex;
public int typeIndex;
public int dataIndex;
}
public class Il2CppFieldDefinition
{
public int nameIndex;
public int typeIndex;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 19)]
public uint token;
}
public class Il2CppFieldDefaultValue
{
public int fieldIndex;
public int typeIndex;
public int dataIndex;
}
public class Il2CppPropertyDefinition
{
public int nameIndex;
public int get;
public int set;
public uint attrs;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 19)]
public uint token;
}
public class Il2CppEventDefinition
{
public int nameIndex;
public int typeIndex;
public int add;
public int remove;
public int raise;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 19)]
public uint token;
}
public class Il2CppGenericContainer
{
/* index of the generic type definition or the generic method definition corresponding to this container */
public int ownerIndex; // either index into Il2CppClass metadata array or Il2CppMethodDefinition array
public int type_argc;
/* If true, we're a generic method, otherwise a generic type definition. */
public int is_method;
/* Our type parameters. */
public uint genericParameterStart; // GenericParameterIndex
}
public class Il2CppGenericParameter
{
public int ownerIndex; /* Type or method this parameter was defined in. */ // GenericContainerIndex
public int nameIndex; // StringIndex
public short constraintsStart; // GenericParameterConstraintIndex
public short constraintsCount;
public ushort num; // Generic parameter position
public ushort flags; // GenericParameterAttributes
}
public class Il2CppCustomAttributeTypeRange
{
[Version(Min = 24.1)]
public uint token;
public int start;
public int count;
}
public class Il2CppInterfaceOffsetPair
{
public int interfaceTypeIndex;
public int offset;
}
public class Il2CppMetadataUsageList
{
public uint start;
public uint count;
}
public class Il2CppMetadataUsagePair
{
public uint destinationindex;
public uint encodedSourceIndex;
}
}

View File

@@ -0,0 +1,32 @@
/*
Copyright (c) 2019-2020 Carter Bush - https://github.com/carterbush
Copyright 2020 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
All rights reserved.
*/
namespace Il2CppInspector
{
public enum MetadataUsageType
{
TypeInfo = 1,
Type = 2,
MethodDef = 3,
FieldInfo = 4,
StringLiteral = 5,
MethodRef = 6,
}
public class MetadataUsage
{
public MetadataUsageType Type { get; }
public int SourceIndex { get; }
public int DestinationIndex { get; }
public MetadataUsage(MetadataUsageType type, int sourceIndex, int destinationIndex) {
Type = type;
SourceIndex = sourceIndex;
DestinationIndex = destinationIndex;
}
}
}