Add stuff for v29

This commit is contained in:
LukeFZ
2023-11-29 21:32:04 +01:00
parent a6d9291303
commit 8ffc7e0021
5 changed files with 1145 additions and 1073 deletions

View File

@@ -1,437 +1,444 @@
/* /*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
All rights reserved. All rights reserved.
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Il2CppInspector namespace Il2CppInspector
{ {
public abstract partial class Il2CppBinary public abstract partial class Il2CppBinary
{ {
// File image // File image
public IFileFormatStream Image { get; } public IFileFormatStream Image { get; }
// The metadata associed with this binary - this is optional and may be null. Contents should not be modified // The metadata associed with this binary - this is optional and may be null. Contents should not be modified
public Metadata Metadata { get; private set; } public Metadata Metadata { get; private set; }
// IL2CPP-only API exports with decrypted names // IL2CPP-only API exports with decrypted names
public Dictionary<string, ulong> APIExports { get; } = new Dictionary<string, ulong>(); public Dictionary<string, ulong> APIExports { get; } = new Dictionary<string, ulong>();
// Binary metadata structures // Binary metadata structures
public Il2CppCodeRegistration CodeRegistration { get; protected set; } public Il2CppCodeRegistration CodeRegistration { get; protected set; }
public Il2CppMetadataRegistration MetadataRegistration { get; protected set; } public Il2CppMetadataRegistration MetadataRegistration { get; protected set; }
// Information for disassembly reverse engineering // Information for disassembly reverse engineering
public ulong CodeRegistrationPointer { get; private set; } public ulong CodeRegistrationPointer { get; private set; }
public ulong MetadataRegistrationPointer { get; private set; } public ulong MetadataRegistrationPointer { get; private set; }
public ulong RegistrationFunctionPointer { get; private set; } public ulong RegistrationFunctionPointer { get; private set; }
public Dictionary<string, ulong> CodeGenModulePointers { get; } = new Dictionary<string, ulong>(); public Dictionary<string, ulong> CodeGenModulePointers { get; } = new Dictionary<string, ulong>();
// Only for <=v24.1 // Only for <=v24.1
public ulong[] GlobalMethodPointers { get; set; } public ulong[] GlobalMethodPointers { get; set; }
// Only for >=v24.2 // Only for >=v24.2
public Dictionary<Il2CppCodeGenModule, ulong[]> ModuleMethodPointers { get; set; } = new Dictionary<Il2CppCodeGenModule, ulong[]>(); public Dictionary<Il2CppCodeGenModule, ulong[]> ModuleMethodPointers { get; set; } = new Dictionary<Il2CppCodeGenModule, ulong[]>();
// Only for >=v24.2. In earlier versions, invoker indices are stored in Il2CppMethodDefinition in the metadata file // Only for >=v24.2. In earlier versions, invoker indices are stored in Il2CppMethodDefinition in the metadata file
public Dictionary<Il2CppCodeGenModule, int[]> MethodInvokerIndices { get; set; } = new Dictionary<Il2CppCodeGenModule, int[]>(); public Dictionary<Il2CppCodeGenModule, int[]> MethodInvokerIndices { get; set; } = new Dictionary<Il2CppCodeGenModule, int[]>();
// NOTE: In versions <21 and earlier releases of v21, use FieldOffsets: // NOTE: In versions <21 and earlier releases of v21, use FieldOffsets:
// global field index => field offset // global field index => field offset
// In versions >=22 and later releases of v21, use FieldOffsetPointers: // 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) // 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 // Negative field offsets from start of each function
public uint[] FieldOffsets { get; private set; } public uint[] FieldOffsets { get; private set; }
// Pointers to field offsets // Pointers to field offsets
public long[] FieldOffsetPointers { get; private set; } public long[] FieldOffsetPointers { get; private set; }
// Generated functions which call constructors on custom attributes // Generated functions which call constructors on custom attributes
// Only for < 27 // Only for < 27
public ulong[] CustomAttributeGenerators { get; private set; } public ulong[] CustomAttributeGenerators { get; private set; }
// IL2CPP-generated functions which implement MethodBase.Invoke with a unique signature per invoker, defined in Il2CppInvokerTable.cpp // IL2CPP-generated functions which implement MethodBase.Invoke with a unique signature per invoker, defined in Il2CppInvokerTable.cpp
// One invoker specifies a return type and argument list. Multiple methods with the same signature can be invoked with the same invoker // One invoker specifies a return type and argument list. Multiple methods with the same signature can be invoked with the same invoker
public ulong[] MethodInvokePointers { get; private set; } public ulong[] MethodInvokePointers { get; private set; }
// Version 16 and below: method references for vtable // Version 16 and below: method references for vtable
public uint[] VTableMethodReferences { get; private set; } public uint[] VTableMethodReferences { get; private set; }
// Generic method specs for vtables // Generic method specs for vtables
public Il2CppMethodSpec[] MethodSpecs { get; private set; } public Il2CppMethodSpec[] MethodSpecs { get; private set; }
// List of run-time concrete generic class and method signatures // List of run-time concrete generic class and method signatures
public List<Il2CppGenericInst> GenericInstances { get; private set; } public List<Il2CppGenericInst> GenericInstances { get; private set; }
// List of constructed generic method function pointers corresponding to each possible method instantiation // List of constructed generic method function pointers corresponding to each possible method instantiation
public Dictionary<Il2CppMethodSpec, ulong> GenericMethodPointers { get; } = new Dictionary<Il2CppMethodSpec, ulong>(); public Dictionary<Il2CppMethodSpec, ulong> GenericMethodPointers { get; } = new Dictionary<Il2CppMethodSpec, ulong>();
// List of invoker pointers for concrete generic methods from MethodSpecs (as above) // List of invoker pointers for concrete generic methods from MethodSpecs (as above)
public Dictionary<Il2CppMethodSpec, int> GenericMethodInvokerIndices { get; } = new Dictionary<Il2CppMethodSpec, int>(); public Dictionary<Il2CppMethodSpec, int> GenericMethodInvokerIndices { get; } = new Dictionary<Il2CppMethodSpec, int>();
// Every type reference (TypeRef) sorted by index // Every type reference (TypeRef) sorted by index
public List<Il2CppType> TypeReferences { get; private set; } public List<Il2CppType> TypeReferences { get; private set; }
// Every type reference index sorted by virtual address // Every type reference index sorted by virtual address
public Dictionary<ulong, int> TypeReferenceIndicesByAddress { get; private set; } public Dictionary<ulong, int> TypeReferenceIndicesByAddress { get; private set; }
// From v24.2 onwards, this structure is stored for each module (image) // From v24.2 onwards, this structure is stored for each module (image)
// One assembly may contain multiple modules // One assembly may contain multiple modules
public Dictionary<string, Il2CppCodeGenModule> Modules { get; private set; } public Dictionary<string, Il2CppCodeGenModule> Modules { get; private set; }
// Status update callback // Status update callback
private EventHandler<string> OnStatusUpdate { get; set; } private EventHandler<string> OnStatusUpdate { get; set; }
private void StatusUpdate(string status) => OnStatusUpdate?.Invoke(this, status); private void StatusUpdate(string status) => OnStatusUpdate?.Invoke(this, status);
// Set if something in the binary has been modified / decrypted // Set if something in the binary has been modified / decrypted
private bool isModified = false; private bool isModified = false;
public bool IsModified => Image.IsModified || isModified; public bool IsModified => Image.IsModified || isModified;
protected Il2CppBinary(IFileFormatStream stream, EventHandler<string> statusCallback = null) { protected Il2CppBinary(IFileFormatStream stream, EventHandler<string> statusCallback = null) {
Image = stream; Image = stream;
OnStatusUpdate = statusCallback; OnStatusUpdate = statusCallback;
DiscoverAPIExports(); DiscoverAPIExports();
} }
protected Il2CppBinary(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null) { protected Il2CppBinary(IFileFormatStream stream, uint codeRegistration, uint metadataRegistration, EventHandler<string> statusCallback = null) {
Image = stream; Image = stream;
OnStatusUpdate = statusCallback; OnStatusUpdate = statusCallback;
DiscoverAPIExports(); DiscoverAPIExports();
TryPrepareMetadata(codeRegistration, metadataRegistration); TryPrepareMetadata(codeRegistration, metadataRegistration);
} }
// Load and initialize a binary of any supported architecture // Load and initialize a binary of any supported architecture
private static Il2CppBinary LoadImpl(IFileFormatStream stream, EventHandler<string> statusCallback) { private static Il2CppBinary LoadImpl(IFileFormatStream stream, EventHandler<string> statusCallback) {
// Get type from image architecture // Get type from image architecture
var type = Assembly.GetExecutingAssembly().GetType("Il2CppInspector.Il2CppBinary" + stream.Arch.ToUpper()); var type = Assembly.GetExecutingAssembly().GetType("Il2CppInspector.Il2CppBinary" + stream.Arch.ToUpper());
if (type == null) if (type == null)
throw new NotImplementedException("Unsupported architecture: " + stream.Arch); throw new NotImplementedException("Unsupported architecture: " + stream.Arch);
// Set width of long (convert to sizeof(int) for 32-bit files) // Set width of long (convert to sizeof(int) for 32-bit files)
if (stream[0].Bits == 32) { if (stream[0].Bits == 32) {
try { try {
stream[0].AddPrimitiveMapping(typeof(long), typeof(int)); stream[0].AddPrimitiveMapping(typeof(long), typeof(int));
} catch (ArgumentException) { } } catch (ArgumentException) { }
try { try {
stream[0].AddPrimitiveMapping(typeof(ulong), typeof(uint)); stream[0].AddPrimitiveMapping(typeof(ulong), typeof(uint));
} catch (ArgumentException) { } } catch (ArgumentException) { }
} }
return (Il2CppBinary) Activator.CreateInstance(type, stream[0], statusCallback); return (Il2CppBinary) Activator.CreateInstance(type, stream[0], statusCallback);
} }
// Load binary without a global-metadata.dat available // Load binary without a global-metadata.dat available
public static Il2CppBinary Load(IFileFormatStream stream, double metadataVersion, EventHandler<string> statusCallback = null) { public static Il2CppBinary Load(IFileFormatStream stream, double metadataVersion, EventHandler<string> statusCallback = null) {
foreach (var loadedImage in stream.TryNextLoadStrategy()) { foreach (var loadedImage in stream.TryNextLoadStrategy()) {
var inst = LoadImpl(stream, statusCallback); var inst = LoadImpl(stream, statusCallback);
if (inst.FindRegistrationStructs(metadataVersion)) if (inst.FindRegistrationStructs(metadataVersion))
return inst; return inst;
} }
return null; return null;
} }
// Load binary with a global-metadata.dat available // Load binary with a global-metadata.dat available
// Supplying the Metadata class when loading a binary is optional // Supplying the Metadata class when loading a binary is optional
// If it is specified and both symbol table and function scanning fail, // If it is specified and both symbol table and function scanning fail,
// Metadata will be used to try to find the required structures with data analysis // Metadata will be used to try to find the required structures with data analysis
// If it is not specified, data analysis will not be performed // If it is not specified, data analysis will not be performed
public static Il2CppBinary Load(IFileFormatStream stream, Metadata metadata, EventHandler<string> statusCallback = null) { public static Il2CppBinary Load(IFileFormatStream stream, Metadata metadata, EventHandler<string> statusCallback = null) {
foreach (var loadedImage in stream.TryNextLoadStrategy()) { foreach (var loadedImage in stream.TryNextLoadStrategy()) {
var inst = LoadImpl(stream, statusCallback); var inst = LoadImpl(stream, statusCallback);
if (inst.FindRegistrationStructs(metadata)) if (inst.FindRegistrationStructs(metadata))
return inst; return inst;
} }
return null; return null;
} }
// Save binary to file, overwriting if necessary // Save binary to file, overwriting if necessary
// Save metadata to file, overwriting if necessary // Save metadata to file, overwriting if necessary
public void SaveToFile(string pathname) { public void SaveToFile(string pathname) {
Image.Position = 0; Image.Position = 0;
using (var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write)) using (var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write))
Image.CopyTo(outFile); Image.CopyTo(outFile);
} }
// Initialize binary without a global-metadata.dat available // Initialize binary without a global-metadata.dat available
public bool FindRegistrationStructs(double metadataVersion) { public bool FindRegistrationStructs(double metadataVersion) {
Image.Version = metadataVersion; Image.Version = metadataVersion;
StatusUpdate("Searching for binary metadata"); StatusUpdate("Searching for binary metadata");
if (!((FindMetadataFromSymbols() ?? FindMetadataFromData() ?? FindMetadataFromCode()) is (ulong code, ulong meta))) if (!((FindMetadataFromSymbols() ?? FindMetadataFromData() ?? FindMetadataFromCode()) is (ulong code, ulong meta)))
return false; return false;
TryPrepareMetadata(code, meta); TryPrepareMetadata(code, meta);
return true; return true;
} }
// Initialize binary with a global-metadata.dat available // Initialize binary with a global-metadata.dat available
public bool FindRegistrationStructs(Metadata metadata) { public bool FindRegistrationStructs(Metadata metadata) {
Metadata = metadata; Metadata = metadata;
return FindRegistrationStructs(metadata.Version); return FindRegistrationStructs(metadata.Version);
} }
// Try to find data structures via symbol table lookup // Try to find data structures via symbol table lookup
private (ulong, ulong)? FindMetadataFromSymbols() { private (ulong, ulong)? FindMetadataFromSymbols() {
// Try searching the symbol table // Try searching the symbol table
var symbols = Image.GetSymbolTable(); var symbols = Image.GetSymbolTable();
if (symbols.Any()) { if (symbols.Any()) {
Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries"); Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
symbols.TryGetValue("g_CodeRegistration", out var code); symbols.TryGetValue("g_CodeRegistration", out var code);
symbols.TryGetValue("g_MetadataRegistration", out var metadata); symbols.TryGetValue("g_MetadataRegistration", out var metadata);
if (code == null) if (code == null)
symbols.TryGetValue("_g_CodeRegistration", out code); symbols.TryGetValue("_g_CodeRegistration", out code);
if (metadata == null) if (metadata == null)
symbols.TryGetValue("_g_MetadataRegistration", out metadata); symbols.TryGetValue("_g_MetadataRegistration", out metadata);
if (code != null && metadata != null) { if (code != null && metadata != null) {
Console.WriteLine("Required structures acquired from symbol lookup"); Console.WriteLine("Required structures acquired from symbol lookup");
return (code.VirtualAddress, metadata.VirtualAddress); return (code.VirtualAddress, metadata.VirtualAddress);
} else { } else {
Console.WriteLine("No matches in symbol table"); Console.WriteLine("No matches in symbol table");
} }
} else if (symbols != null) { } else if (symbols != null) {
Console.WriteLine("No symbol table present in binary file"); Console.WriteLine("No symbol table present in binary file");
} else { } else {
Console.WriteLine("Symbol table search not implemented for this binary format"); Console.WriteLine("Symbol table search not implemented for this binary format");
} }
return null; return null;
} }
// Try to find data structures via init function code analysis // Try to find data structures via init function code analysis
private (ulong, ulong)? FindMetadataFromCode() { private (ulong, ulong)? FindMetadataFromCode() {
// Try searching the function table // Try searching the function table
var addrs = Image.GetFunctionTable(); var addrs = Image.GetFunctionTable();
Debug.WriteLine("Function table:"); Debug.WriteLine("Function table:");
Debug.WriteLine(string.Join(", ", from a in addrs select string.Format($"0x{a:X8}"))); Debug.WriteLine(string.Join(", ", from a in addrs select string.Format($"0x{a:X8}")));
foreach (var loc in addrs) { foreach (var loc in addrs) {
var (code, metadata) = ConsiderCode(Image, loc); var (code, metadata) = ConsiderCode(Image, loc);
if (code != 0) { if (code != 0) {
RegistrationFunctionPointer = loc + Image.GlobalOffset; RegistrationFunctionPointer = loc + Image.GlobalOffset;
Console.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer); Console.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
return (code, metadata); return (code, metadata);
} }
} }
Console.WriteLine("No matches via code heuristics"); Console.WriteLine("No matches via code heuristics");
return null; return null;
} }
// Try to find data structures via data heuristics // Try to find data structures via data heuristics
// Requires succeesful global-metadata.dat analysis first // Requires succeesful global-metadata.dat analysis first
private (ulong, ulong)? FindMetadataFromData() { private (ulong, ulong)? FindMetadataFromData() {
if (Metadata == null) if (Metadata == null)
return null; return null;
var (codePtr, metadataPtr) = ImageScan(Metadata); var (codePtr, metadataPtr) = ImageScan(Metadata);
if (codePtr == 0) { if (codePtr == 0) {
Console.WriteLine("No matches via data heuristics"); Console.WriteLine("No matches via data heuristics");
return null; return null;
} }
Console.WriteLine("Required structures acquired from data heuristics"); Console.WriteLine("Required structures acquired from data heuristics");
return (codePtr, metadataPtr); return (codePtr, metadataPtr);
} }
// Architecture-specific search function // Architecture-specific search function
protected abstract (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc); protected abstract (ulong, ulong) ConsiderCode(IFileFormatStream image, uint loc);
// Load all of the discovered metadata in the binary // Load all of the discovered metadata in the binary
private void TryPrepareMetadata(ulong codeRegistration, ulong metadataRegistration) { private void TryPrepareMetadata(ulong codeRegistration, ulong metadataRegistration) {
try { try {
PrepareMetadata(codeRegistration, metadataRegistration); PrepareMetadata(codeRegistration, metadataRegistration);
} }
catch (Exception ex) when (!(ex is NotSupportedException)) { catch (Exception ex) when (!(ex is NotSupportedException)) {
throw new InvalidOperationException($"Could not analyze IL2CPP data. Ensure that the latest core plugins package is installed and all core plugins are enabled before filing a bug report. The error was: {ex.Message}", ex); throw new InvalidOperationException($"Could not analyze IL2CPP data. Ensure that the latest core plugins package is installed and all core plugins are enabled before filing a bug report. The error was: {ex.Message}", ex);
} }
} }
// Load all of the discovered metadata in the binary // Load all of the discovered metadata in the binary
private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) { private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) {
// Store locations // Store locations
CodeRegistrationPointer = codeRegistration; CodeRegistrationPointer = codeRegistration;
MetadataRegistrationPointer = metadataRegistration; MetadataRegistrationPointer = metadataRegistration;
var pointerSize = Image.Bits == 32 ? 4u : 8u; var pointerSize = Image.Bits == 32 ? 4u : 8u;
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("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)); Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
// Root structures from which we find everything else // Root structures from which we find everything else
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration); CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
MetadataRegistration = Image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration); MetadataRegistration = Image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
// genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1
// pointer expected if we need to bump version // pointer expected if we need to bump version
if (Image.Version == 24.4 && CodeRegistration.invokerPointersCount > 0x50000) if (Image.Version == 24.4 && CodeRegistration.invokerPointersCount > 0x50000)
{ {
Image.Version = 24.5; Image.Version = 24.5;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration); CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
} }
if (Image.Version == 24.4 && CodeRegistration.reversePInvokeWrapperCount > 0x50000) { if (Image.Version == 24.4 && CodeRegistration.reversePInvokeWrapperCount > 0x50000) {
Image.Version = 24.5; Image.Version = 24.5;
codeRegistration -= 1 * pointerSize; codeRegistration -= 1 * pointerSize;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration); CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
} }
// Plugin hook to pre-process binary if (Image.Version == 29 && CodeRegistration.genericMethodPointersCount > 0x50000)
isModified |= PluginHooks.PreProcessBinary(this).IsStreamModified; {
Image.Version = 29.1;
StatusUpdate($"Analyzing IL2CPP data for {Image.Format}/{Image.Arch} image"); codeRegistration -= 2 * pointerSize;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
// Do basic validatation that MetadataRegistration and CodeRegistration are sane }
/*
* GlobalMethodPointers (<= 24.1) must be a series of pointers in il2cpp or .text, and in sequential order // Plugin hook to pre-process binary
* FieldOffsetPointers (>= 21.1) must be a series of pointers in __const or zero, and in sequential order isModified |= PluginHooks.PreProcessBinary(this).IsStreamModified;
* typeRefPointers must be a series of pointers in __const
* MethodInvokePointers must be a series of pointers in __text or .text, and in sequential order StatusUpdate($"Analyzing IL2CPP data for {Image.Format}/{Image.Arch} image");
*/
if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.typeDefinitionsSizesCount) // Do basic validatation that MetadataRegistration and CodeRegistration are sane
|| CodeRegistration.reversePInvokeWrapperCount > 0x10000 /*
|| CodeRegistration.unresolvedVirtualCallCount > 0x4000 // >= 22 * GlobalMethodPointers (<= 24.1) must be a series of pointers in il2cpp or .text, and in sequential order
|| CodeRegistration.interopDataCount > 0x1000 // >= 23 * FieldOffsetPointers (>= 21.1) must be a series of pointers in __const or zero, and in sequential order
|| (Image.Version <= 24.1 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount)) * typeRefPointers must be a series of pointers in __const
throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again."); * MethodInvokePointers must be a series of pointers in __text or .text, and in sequential order
*/
// The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.typeDefinitionsSizesCount)
if (Image.Version <= 24.1) || CodeRegistration.reversePInvokeWrapperCount > 0x10000
GlobalMethodPointers = Image.ReadMappedArray<ulong>(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount); || CodeRegistration.unresolvedVirtualCallCount > 0x4000 // >= 22
|| CodeRegistration.interopDataCount > 0x1000 // >= 23
// After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules || (Image.Version <= 24.1 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount))
if (Image.Version >= 24.2) { throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again.");
Modules = new Dictionary<string, Il2CppCodeGenModule>();
// The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule
// In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0; if (Image.Version <= 24.1)
// if this changes we'll have to get smarter about disambiguating these two. GlobalMethodPointers = Image.ReadMappedArray<ulong>(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount);
if (CodeRegistration.codeGenModulesCount == 0) {
Image.Version = 24.3; // After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration); if (Image.Version >= 24.2) {
} Modules = new Dictionary<string, Il2CppCodeGenModule>();
// Array of pointers to Il2CppCodeGenModule // In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0;
var codeGenModulePointers = Image.ReadMappedArray<ulong>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount); // if this changes we'll have to get smarter about disambiguating these two.
var modules = Image.ReadMappedObjectPointerArray<Il2CppCodeGenModule>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount); if (CodeRegistration.codeGenModulesCount == 0) {
Image.Version = 24.3;
foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) { CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
var module = mp.Module; }
var name = Image.ReadMappedNullTerminatedString(module.moduleName); // Array of pointers to Il2CppCodeGenModule
Modules.Add(name, module); var codeGenModulePointers = Image.ReadMappedArray<ulong>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount);
CodeGenModulePointers.Add(name, mp.Pointer); var modules = Image.ReadMappedObjectPointerArray<Il2CppCodeGenModule>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount);
// Read method pointers foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) {
// If a module contains only interfaces, abstract methods and/or non-concrete generic methods, var module = mp.Module;
// the entire method pointer array will be NULL values, causing the methodPointer to be mapped to .bss
// and therefore out of scope of the binary image var name = Image.ReadMappedNullTerminatedString(module.moduleName);
try { Modules.Add(name, module);
ModuleMethodPointers.Add(module, Image.ReadMappedArray<ulong>(module.methodPointers, (int) module.methodPointerCount)); CodeGenModulePointers.Add(name, mp.Pointer);
} catch (InvalidOperationException) {
ModuleMethodPointers.Add(module, new ulong[module.methodPointerCount]); // Read method pointers
} // If a module contains only interfaces, abstract methods and/or non-concrete generic methods,
// the entire method pointer array will be NULL values, causing the methodPointer to be mapped to .bss
// Read method invoker pointer indices - one per method // and therefore out of scope of the binary image
MethodInvokerIndices.Add(module, Image.ReadMappedArray<int>(module.invokerIndices, (int) module.methodPointerCount)); try {
} ModuleMethodPointers.Add(module, Image.ReadMappedArray<ulong>(module.methodPointers, (int) module.methodPointerCount));
} } catch (InvalidOperationException) {
ModuleMethodPointers.Add(module, new ulong[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 // Read method invoker pointer indices - one per method
bool fieldOffsetsArePointers = (Image.Version >= 22); MethodInvokerIndices.Add(module, Image.ReadMappedArray<int>(module.invokerIndices, (int) module.methodPointerCount));
}
// Some variants of 21 also use an array of pointers }
if (Image.Version == 21) {
var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6); // Field offset data. Metadata <=21.x uses a value-type array; >=21.x uses a pointer array
// We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32 // Versions from 22 onwards use an array of pointers in Binary.FieldOffsetData
// are always the first six defined types, and that all but Int32 have no fields bool fieldOffsetsArePointers = (Image.Version >= 22);
fieldOffsetsArePointers = (fieldTest[0] == 0 && fieldTest[1] == 0 && fieldTest[2] == 0 && fieldTest[3] == 0 && fieldTest[4] == 0 && fieldTest[5] > 0);
} // Some variants of 21 also use an array of pointers
if (Image.Version == 21) {
// All older versions use values directly in the array var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6);
if (!fieldOffsetsArePointers)
FieldOffsets = Image.ReadMappedArray<uint>(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); // We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32
else // are always the first six defined types, and that all but Int32 have no fields
FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); fieldOffsetsArePointers = (fieldTest[0] == 0 && fieldTest[1] == 0 && fieldTest[2] == 0 && fieldTest[3] == 0 && fieldTest[4] == 0 && fieldTest[5] > 0);
}
// Type references (pointer array)
var typeRefPointers = Image.ReadMappedArray<ulong>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount); // All older versions use values directly in the array
TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i); if (!fieldOffsetsArePointers)
TypeReferences = Image.ReadMappedObjectPointerArray<Il2CppType>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount); FieldOffsets = Image.ReadMappedArray<uint>(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount);
else
// Custom attribute constructors (function pointers) FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount);
// This is managed in Il2CppInspector for metadata >= 27
if (Image.Version < 27) { // Type references (pointer array)
CustomAttributeGenerators = Image.ReadMappedArray<ulong>(CodeRegistration.customAttributeGenerators, (int) CodeRegistration.customAttributeCount); var typeRefPointers = Image.ReadMappedArray<ulong>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount);
} TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i);
TypeReferences = Image.ReadMappedObjectPointerArray<Il2CppType>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount);
// Method.Invoke function pointers
MethodInvokePointers = Image.ReadMappedArray<ulong>(CodeRegistration.invokerPointers, (int) CodeRegistration.invokerPointersCount); // Custom attribute constructors (function pointers)
// This is managed in Il2CppInspector for metadata >= 27
// TODO: Function pointers as shown below if (Image.Version < 27) {
// reversePInvokeWrappers CustomAttributeGenerators = Image.ReadMappedArray<ulong>(CodeRegistration.customAttributeGenerators, (int) CodeRegistration.customAttributeCount);
// <=22: delegateWrappersFromManagedToNative, marshalingFunctions }
// >=21 <=22: ccwMarshalingFunctions
// >=22: unresolvedVirtualCallPointers // Method.Invoke function pointers
// >=23: interopData MethodInvokePointers = Image.ReadMappedArray<ulong>(CodeRegistration.invokerPointers, (int) CodeRegistration.invokerPointersCount);
if (Image.Version < 19) { // TODO: Function pointers as shown below
VTableMethodReferences = Image.ReadMappedArray<uint>(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount); // reversePInvokeWrappers
} // <=22: delegateWrappersFromManagedToNative, marshalingFunctions
// >=21 <=22: ccwMarshalingFunctions
// Generic type and method specs (open and closed constructed types) // >=22: unresolvedVirtualCallPointers
MethodSpecs = Image.ReadMappedArray<Il2CppMethodSpec>(MetadataRegistration.methodSpecs, (int) MetadataRegistration.methodSpecsCount); // >=23: interopData
// Concrete generic class and method signatures if (Image.Version < 19) {
GenericInstances = Image.ReadMappedObjectPointerArray<Il2CppGenericInst>(MetadataRegistration.genericInsts, (int) MetadataRegistration.genericInstsCount); VTableMethodReferences = Image.ReadMappedArray<uint>(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount);
}
// Concrete generic method pointers
var genericMethodPointers = Image.ReadMappedArray<ulong>(CodeRegistration.genericMethodPointers, (int) CodeRegistration.genericMethodPointersCount); // Generic type and method specs (open and closed constructed types)
var genericMethodTable = Image.ReadMappedArray<Il2CppGenericMethodFunctionsDefinitions>(MetadataRegistration.genericMethodTable, (int) MetadataRegistration.genericMethodTableCount); MethodSpecs = Image.ReadMappedArray<Il2CppMethodSpec>(MetadataRegistration.methodSpecs, (int) MetadataRegistration.methodSpecsCount);
foreach (var tableEntry in genericMethodTable) {
GenericMethodPointers.Add(MethodSpecs[tableEntry.genericMethodIndex], genericMethodPointers[tableEntry.indices.methodIndex]); // Concrete generic class and method signatures
GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.genericMethodIndex], tableEntry.indices.invokerIndex); GenericInstances = Image.ReadMappedObjectPointerArray<Il2CppGenericInst>(MetadataRegistration.genericInsts, (int) MetadataRegistration.genericInstsCount);
}
// Concrete generic method pointers
// Plugin hook to pre-process binary var genericMethodPointers = Image.ReadMappedArray<ulong>(CodeRegistration.genericMethodPointers, (int) CodeRegistration.genericMethodPointersCount);
isModified |= PluginHooks.PostProcessBinary(this).IsStreamModified; var genericMethodTable = Image.ReadMappedArray<Il2CppGenericMethodFunctionsDefinitions>(MetadataRegistration.genericMethodTable, (int) MetadataRegistration.genericMethodTableCount);
} foreach (var tableEntry in genericMethodTable) {
GenericMethodPointers.Add(MethodSpecs[tableEntry.genericMethodIndex], genericMethodPointers[tableEntry.indices.methodIndex]);
// IL2CPP API exports GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.genericMethodIndex], tableEntry.indices.invokerIndex);
// This strips leading underscores and selects only il2cpp_* symbols which can be mapped into the binary }
// (therefore ignoring extern imports)
// Some binaries have functions starting "il2cpp_z_" - ignore these too // Plugin hook to pre-process binary
private void DiscoverAPIExports() { isModified |= PluginHooks.PostProcessBinary(this).IsStreamModified;
var exports = Image.GetExports()? }
.Where(e => (e.Name.StartsWith("il2cpp_") || e.Name.StartsWith("_il2cpp_") || e.Name.StartsWith("__il2cpp_"))
&& !e.Name.Contains("il2cpp_z_")); // IL2CPP API exports
// This strips leading underscores and selects only il2cpp_* symbols which can be mapped into the binary
if (exports == null) // (therefore ignoring extern imports)
return; // Some binaries have functions starting "il2cpp_z_" - ignore these too
private void DiscoverAPIExports() {
var exportRgx = new Regex(@"^_+"); var exports = Image.GetExports()?
.Where(e => (e.Name.StartsWith("il2cpp_") || e.Name.StartsWith("_il2cpp_") || e.Name.StartsWith("__il2cpp_"))
foreach (var export in exports) && !e.Name.Contains("il2cpp_z_"));
if (Image.TryMapVATR(export.VirtualAddress, out _))
APIExports.Add(exportRgx.Replace(export.Name, ""), export.VirtualAddress); if (exports == null)
} return;
}
} var exportRgx = new Regex(@"^_+");
foreach (var export in exports)
if (Image.TryMapVATR(export.VirtualAddress, out _))
APIExports.Add(exportRgx.Replace(export.Name, ""), export.VirtualAddress);
}
}
}

View File

@@ -1,267 +1,316 @@
/* /*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
All rights reserved. All rights reserved.
*/ */
using NoisyCowStudios.Bin2Object; using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector namespace Il2CppInspector
{ {
// From class-internals.h / il2cpp-class-internals.h // From class-internals.h / il2cpp-class-internals.h
public class Il2CppCodeRegistration public class Il2CppCodeRegistration
{ {
// Moved to Il2CppCodeGenModule in v24.2 // Moved to Il2CppCodeGenModule in v24.2
[Version(Max = 24.1)] [Version(Max = 24.1)]
public ulong methodPointersCount; public ulong methodPointersCount;
[Version(Max = 24.1)] [Version(Max = 24.1)]
public ulong pmethodPointers; public ulong pmethodPointers;
public ulong reversePInvokeWrapperCount; // (was renamed from delegateWrappersFromNativeToManagedCount in v22) public ulong reversePInvokeWrapperCount; // (was renamed from delegateWrappersFromNativeToManagedCount in v22)
public ulong reversePInvokeWrappers; // (was renamed from delegateWrappersFromNativeToManaged in v22) public ulong reversePInvokeWrappers; // (was renamed from delegateWrappersFromNativeToManaged in v22)
// Removed in metadata v23 // Removed in metadata v23
[Version(Max = 22)] [Version(Max = 22)]
public ulong delegateWrappersFromManagedToNativeCount; public ulong delegateWrappersFromManagedToNativeCount;
[Version(Max = 22)] [Version(Max = 22)]
public ulong delegateWrappersFromManagedToNative; public ulong delegateWrappersFromManagedToNative;
[Version(Max = 22)] [Version(Max = 22)]
public ulong marshalingFunctionsCount; public ulong marshalingFunctionsCount;
[Version(Max = 22)] [Version(Max = 22)]
public ulong marshalingFunctions; public ulong marshalingFunctions;
[Version(Min = 21, Max = 22)] [Version(Min = 21, Max = 22)]
public ulong ccwMarshalingFunctionsCount; public ulong ccwMarshalingFunctionsCount;
[Version(Min = 21, Max = 22)] [Version(Min = 21, Max = 22)]
public ulong ccwMarshalingFunctions; public ulong ccwMarshalingFunctions;
public ulong genericMethodPointersCount; public ulong genericMethodPointersCount;
public ulong genericMethodPointers; public ulong genericMethodPointers;
[Version(Min = 24.5, Max = 24.5)] [Version(Min = 24.5, Max = 24.5)]
[Version(Min = 27.1)] [Version(Min = 27.1)]
public ulong genericAdjustorThunks; public ulong genericAdjustorThunks;
public ulong invokerPointersCount; public ulong invokerPointersCount;
public ulong invokerPointers; public ulong invokerPointers;
// Removed in metadata v27 // Removed in metadata v27
[Version(Max = 24.5)] [Version(Max = 24.5)]
public long customAttributeCount; public long customAttributeCount;
[Version(Max = 24.5)] [Version(Max = 24.5)]
public ulong customAttributeGenerators; public ulong customAttributeGenerators;
// Removed in metadata v23 // Removed in metadata v23
[Version(Min = 21, Max = 22)] [Version(Min = 21, Max = 22)]
public long guidCount; public long guidCount;
[Version(Min = 21, Max = 22)] [Version(Min = 21, Max = 22)]
public ulong guids; // Il2CppGuid public ulong guids; // Il2CppGuid
// Added in metadata v22 // Added in metadata v22
[Version(Min = 22)] [Version(Min = 22, Max = 29)]
public ulong unresolvedVirtualCallCount; public ulong unresolvedVirtualCallCount;
[Version(Min = 22)]
public ulong unresolvedVirtualCallPointers; [Version(Min = 29.1)]
public ulong unresolvedIndirectCallCount;
// Added in metadata v23
[Version(Min = 23)] [Version(Min = 22)]
public ulong interopDataCount; public ulong unresolvedVirtualCallPointers;
[Version(Min = 23)]
public ulong interopData; [Version(Min = 29.1)]
public ulong unresolvedInstanceCallPointers;
[Version(Min = 24.3)] [Version(Min = 29.1)]
public ulong windowsRuntimeFactoryCount; public ulong unresolvedStaticCallPointers;
[Version(Min = 24.3)]
public ulong windowsRuntimeFactoryTable; // Added in metadata v23
[Version(Min = 23)]
// Added in metadata v24.2 to replace methodPointers and methodPointersCount public ulong interopDataCount;
[Version(Min = 24.2)] [Version(Min = 23)]
public ulong codeGenModulesCount; public ulong interopData;
[Version(Min = 24.2)]
public ulong pcodeGenModules; [Version(Min = 24.3)]
} public ulong windowsRuntimeFactoryCount;
[Version(Min = 24.3)]
// Introduced in metadata v24.2 (replaces method pointers in Il2CppCodeRegistration) public ulong windowsRuntimeFactoryTable;
public class Il2CppCodeGenModule
{ // Added in metadata v24.2 to replace methodPointers and methodPointersCount
public ulong moduleName; [Version(Min = 24.2)]
public ulong methodPointerCount; public ulong codeGenModulesCount;
public ulong methodPointers; [Version(Min = 24.2)]
[Version(Min = 24.5, Max = 24.5)] public ulong pcodeGenModules;
[Version(Min = 27.1)] }
public long adjustorThunkCount;
[Version(Min = 24.5, Max = 24.5)] // Introduced in metadata v24.2 (replaces method pointers in Il2CppCodeRegistration)
[Version(Min = 27.1)] public class Il2CppCodeGenModule
public ulong adjustorThunks; //Pointer {
public ulong invokerIndices; public ulong moduleName;
public ulong reversePInvokeWrapperCount; public ulong methodPointerCount;
public ulong reversePInvokeWrapperIndices; public ulong methodPointers;
public ulong rgctxRangesCount; [Version(Min = 24.5, Max = 24.5)]
public ulong rgctxRanges; [Version(Min = 27.1)]
public ulong rgctxsCount; public long adjustorThunkCount;
public ulong rgctxs; [Version(Min = 24.5, Max = 24.5)]
public ulong debuggerMetadata; [Version(Min = 27.1)]
public ulong adjustorThunks; //Pointer
// Added in metadata v27 public ulong invokerIndices;
public ulong customAttributeCacheGenerator; // CustomAttributesCacheGenerator* public ulong reversePInvokeWrapperCount;
public ulong moduleInitializer; // Il2CppMethodPointer public ulong reversePInvokeWrapperIndices;
public ulong staticConstructorTypeIndices; // TypeDefinitionIndex* public ulong rgctxRangesCount;
public ulong metadataRegistration; // Il2CppMetadataRegistration* // Per-assembly mode only public ulong rgctxRanges;
public ulong codeRegistration; // Il2CppCodeRegistration* // Per-assembly mode only public ulong rgctxsCount;
} public ulong rgctxs;
public ulong debuggerMetadata;
#pragma warning disable CS0649
public class Il2CppMetadataRegistration // Added in metadata v27
{ [Version(Min = 27, Max = 27.2)]
public long genericClassesCount; public ulong customAttributeCacheGenerator; // CustomAttributesCacheGenerator*
public ulong genericClasses; [Version(Min = 27)]
public long genericInstsCount; public ulong moduleInitializer; // Il2CppMethodPointer
public ulong genericInsts; [Version(Min = 27)]
public long genericMethodTableCount; public ulong staticConstructorTypeIndices; // TypeDefinitionIndex*
public ulong genericMethodTable; // Il2CppGenericMethodFunctionsDefinitions [Version(Min = 27)]
public long typesCount; public ulong metadataRegistration; // Il2CppMetadataRegistration* // Per-assembly mode only
public ulong ptypes; [Version(Min = 27)]
public long methodSpecsCount; public ulong codeRegistration; // Il2CppCodeRegistration* // Per-assembly mode only
public ulong methodSpecs; }
[Version(Max = 16)]
public long methodReferencesCount; #pragma warning disable CS0649
[Version(Max = 16)] public class Il2CppMetadataRegistration
public ulong methodReferences; {
public long genericClassesCount;
public long fieldOffsetsCount; public ulong genericClasses;
public ulong pfieldOffsets; // Changed from int32_t* to int32_t** after 5.4.0f3, before 5.5.0f3 public long genericInstsCount;
public ulong genericInsts;
public long typeDefinitionsSizesCount; public long genericMethodTableCount;
public ulong typeDefinitionsSizes; public ulong genericMethodTable; // Il2CppGenericMethodFunctionsDefinitions
[Version(Min = 19)] public long typesCount;
public ulong metadataUsagesCount; public ulong ptypes;
[Version(Min = 19)] public long methodSpecsCount;
public ulong metadataUsages; public ulong methodSpecs;
} [Version(Max = 16)]
#pragma warning restore CS0649 public long methodReferencesCount;
[Version(Max = 16)]
// From blob.h / il2cpp-blob.h public ulong methodReferences;
public enum Il2CppTypeEnum
{ public long fieldOffsetsCount;
IL2CPP_TYPE_END = 0x00, /* End of List */ public ulong pfieldOffsets; // Changed from int32_t* to int32_t** after 5.4.0f3, before 5.5.0f3
IL2CPP_TYPE_VOID = 0x01,
IL2CPP_TYPE_BOOLEAN = 0x02, public long typeDefinitionsSizesCount;
IL2CPP_TYPE_CHAR = 0x03, public ulong typeDefinitionsSizes;
IL2CPP_TYPE_I1 = 0x04, [Version(Min = 19)]
IL2CPP_TYPE_U1 = 0x05, public ulong metadataUsagesCount;
IL2CPP_TYPE_I2 = 0x06, [Version(Min = 19)]
IL2CPP_TYPE_U2 = 0x07, public ulong metadataUsages;
IL2CPP_TYPE_I4 = 0x08, }
IL2CPP_TYPE_U4 = 0x09, #pragma warning restore CS0649
IL2CPP_TYPE_I8 = 0x0a,
IL2CPP_TYPE_U8 = 0x0b, // From blob.h / il2cpp-blob.h
IL2CPP_TYPE_R4 = 0x0c, public enum Il2CppTypeEnum
IL2CPP_TYPE_R8 = 0x0d, {
IL2CPP_TYPE_STRING = 0x0e, IL2CPP_TYPE_END = 0x00, /* End of List */
IL2CPP_TYPE_PTR = 0x0f, /* arg: <type> token */ IL2CPP_TYPE_VOID = 0x01,
IL2CPP_TYPE_BYREF = 0x10, /* arg: <type> token */ IL2CPP_TYPE_BOOLEAN = 0x02,
IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: <type> token */ IL2CPP_TYPE_CHAR = 0x03,
IL2CPP_TYPE_CLASS = 0x12, /* arg: <type> token */ IL2CPP_TYPE_I1 = 0x04,
IL2CPP_TYPE_VAR = 0x13, /* Generic parameter in a generic type definition, represented as number (compressed unsigned integer) number */ IL2CPP_TYPE_U1 = 0x05,
IL2CPP_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */ IL2CPP_TYPE_I2 = 0x06,
IL2CPP_TYPE_GENERICINST = 0x15, /* <type> <type-arg-count> <type-1> \x{2026} <type-n> */ IL2CPP_TYPE_U2 = 0x07,
IL2CPP_TYPE_TYPEDBYREF = 0x16, IL2CPP_TYPE_I4 = 0x08,
IL2CPP_TYPE_I = 0x18, IL2CPP_TYPE_U4 = 0x09,
IL2CPP_TYPE_U = 0x19, IL2CPP_TYPE_I8 = 0x0a,
IL2CPP_TYPE_FNPTR = 0x1b, /* arg: full method signature */ IL2CPP_TYPE_U8 = 0x0b,
IL2CPP_TYPE_OBJECT = 0x1c, IL2CPP_TYPE_R4 = 0x0c,
IL2CPP_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */ IL2CPP_TYPE_R8 = 0x0d,
IL2CPP_TYPE_MVAR = 0x1e, /* Generic parameter in a generic method definition, represented as number (compressed unsigned integer) */ IL2CPP_TYPE_STRING = 0x0e,
IL2CPP_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */ IL2CPP_TYPE_PTR = 0x0f, /* arg: <type> token */
IL2CPP_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */ IL2CPP_TYPE_BYREF = 0x10, /* arg: <type> token */
IL2CPP_TYPE_INTERNAL = 0x21, /* CLR internal type */ IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: <type> token */
IL2CPP_TYPE_CLASS = 0x12, /* arg: <type> token */
IL2CPP_TYPE_MODIFIER = 0x40, /* Or with the following types */ IL2CPP_TYPE_VAR = 0x13, /* Generic parameter in a generic type definition, represented as number (compressed unsigned integer) number */
IL2CPP_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */ IL2CPP_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */
IL2CPP_TYPE_PINNED = 0x45, /* Local var that points to pinned object */ IL2CPP_TYPE_GENERICINST = 0x15, /* <type> <type-arg-count> <type-1> \x{2026} <type-n> */
IL2CPP_TYPE_TYPEDBYREF = 0x16,
IL2CPP_TYPE_ENUM = 0x55 /* an enumeration */ IL2CPP_TYPE_I = 0x18,
} IL2CPP_TYPE_U = 0x19,
IL2CPP_TYPE_FNPTR = 0x1b, /* arg: full method signature */
// From metadata.h / il2cpp-runtime-metadata.h IL2CPP_TYPE_OBJECT = 0x1c,
public class Il2CppType 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 */
union IL2CPP_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */
{ IL2CPP_TYPE_INTERNAL = 0x21, /* CLR internal type */
TypeDefinitionIndex klassIndex; // for VALUETYPE and CLASS (<v27; v27: at startup)
Il2CppMetadataTypeHandle typeHandle; // for VALUETYPE and CLASS (added in v27: at runtime) IL2CPP_TYPE_MODIFIER = 0x40, /* Or with the following types */
const Il2CppType* type; // for PTR and SZARRAY IL2CPP_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */
Il2CppArrayType* array; // for ARRAY IL2CPP_TYPE_PINNED = 0x45, /* Local var that points to pinned object */
GenericParameterIndex genericParameterIndex; // for VAR and MVAR (<v27; v27: at startup)
Il2CppMetadataGenericParameterHandle genericParameterHandle; // for VAR and MVAR (added in v27: at runtime) IL2CPP_TYPE_ENUM = 0x55, /* an enumeration */
Il2CppGenericClass* generic_class; // for GENERICINST IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff /* Type index metadata table */
} }
*/
public ulong datapoint; // From metadata.h / il2cpp-runtime-metadata.h
public ulong bits; // this should be private but we need it to be public for BinaryObjectReader to work public class Il2CppType
{
public uint attrs => (uint) bits & 0xffff; /* param attributes or field flags */ /*
public Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff); union
// TODO: Unity 2021.1 (v27.2): num_mods becomes 1 bit shorter, shifting byref and pinned right 1 bit, valuetype bit added {
public uint num_mods => (uint) (bits >> 24) & 0x3f; /* max 64 modifiers follow at the end */ TypeDefinitionIndex klassIndex; // for VALUETYPE and CLASS (<v27; v27: at startup)
public bool byref => ((bits >> 30) & 1) == 1; Il2CppMetadataTypeHandle typeHandle; // for VALUETYPE and CLASS (added in v27: at runtime)
public bool pinned => (bits >> 31) == 1; /* valid when included in a local var signature */ const Il2CppType* type; // for PTR and SZARRAY
} Il2CppArrayType* array; // for ARRAY
GenericParameterIndex genericParameterIndex; // for VAR and MVAR (<v27; v27: at startup)
public class Il2CppGenericClass Il2CppMetadataGenericParameterHandle genericParameterHandle; // for VAR and MVAR (added in v27: at runtime)
{ Il2CppGenericClass* generic_class; // for GENERICINST
[Version(Max = 24.5)] }
public long typeDefinitionIndex; /* the generic type definition */ */
[Version(Min = 27)] public ulong datapoint;
public ulong type; // Il2CppType* /* the generic type definition */ public ulong bits; // this should be private but we need it to be public for BinaryObjectReader to work
//public Union data { get; set; }
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 uint attrs => (uint) bits & 0xffff; /* param attributes or field flags */
} public Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff);
// TODO: Unity 2021.1 (v27.2): num_mods becomes 1 bit shorter, shifting byref and pinned right 1 bit, valuetype bit added
public class Il2CppGenericContext public uint num_mods => (uint) (bits >> 24) & 0x3f; /* max 64 modifiers follow at the end */
{ public bool byref => ((bits >> 30) & 1) == 1;
/* The instantiation corresponding to the class generic parameters */ public bool pinned => (bits >> 31) == 1; /* valid when included in a local var signature */
public ulong class_inst;
/* The instantiation corresponding to the method generic parameters */ /*public class Union
public ulong method_inst; {
} public ulong dummy;
/// <summary>
public class Il2CppGenericInst /// for VALUETYPE and CLASS
{ /// </summary>
public ulong type_argc; public long klassIndex => (long)dummy;
public ulong type_argv; /// <summary>
} /// for VALUETYPE and CLASS at runtime
/// </summary>
public class Il2CppArrayType public ulong typeHandle => dummy;
{ /// <summary>
public ulong etype; /// for PTR and SZARRAY
public byte rank; /// </summary>
public byte numsizes; public ulong type => dummy;
public byte numlobounds; /// <summary>
public ulong sizes; /// for ARRAY
public ulong lobounds; /// </summary>
} public ulong array => dummy;
/// <summary>
public class Il2CppMethodSpec /// for VAR and MVAR
{ /// </summary>
public int methodDefinitionIndex; public long genericParameterIndex => (long)dummy;
public int classIndexIndex; /// <summary>
public int methodIndexIndex; /// for VAR and MVAR at runtime
} /// </summary>
public ulong genericParameterHandle => dummy;
public class Il2CppGenericMethodFunctionsDefinitions /// <summary>
{ /// for GENERICINST
public int genericMethodIndex; /// </summary>
public Il2CppGenericMethodIndices indices; public ulong generic_class => dummy;
} }*/
}
public class Il2CppGenericMethodIndices
{ public class Il2CppGenericClass
public int methodIndex; {
public int invokerIndex; [Version(Max = 24.5)]
[Version(Min = 24.5, Max = 24.5)] public long typeDefinitionIndex; /* the generic type definition */
[Version(Min = 27.1)] [Version(Min = 27)]
public int adjustorThunk; public ulong type; // Il2CppType* /* 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;
}
public class Il2CppGenericMethodFunctionsDefinitions
{
public int genericMethodIndex;
public Il2CppGenericMethodIndices indices;
}
public class Il2CppGenericMethodIndices
{
public int methodIndex;
public int invokerIndex;
[Version(Min = 24.5, Max = 24.5)]
[Version(Min = 27.1)]
public int adjustorThunk;
}
}

View File

@@ -102,10 +102,14 @@ namespace Il2CppInspector
value = Metadata.ReadInt16(); value = Metadata.ReadInt16();
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_U4: case Il2CppTypeEnum.IL2CPP_TYPE_U4:
value = Metadata.ReadUInt32(); value = Metadata.Version >= 29
? Metadata.ReadCompressedUInt32()
: Metadata.ReadUInt32();
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_I4: case Il2CppTypeEnum.IL2CPP_TYPE_I4:
value = Metadata.ReadInt32(); value = Metadata.Version >= 29
? Metadata.ReadCompressedInt32()
: Metadata.ReadInt32();
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_U8: case Il2CppTypeEnum.IL2CPP_TYPE_U8:
value = Metadata.ReadUInt64(); value = Metadata.ReadUInt64();
@@ -120,7 +124,10 @@ namespace Il2CppInspector
value = Metadata.ReadDouble(); value = Metadata.ReadDouble();
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_STRING: case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
var uiLen = Metadata.ReadInt32(); var uiLen = Metadata.Version >= 29
? Metadata.ReadCompressedInt32()
: Metadata.ReadInt32();
value = Encoding.UTF8.GetString(Metadata.ReadBytes(uiLen)); value = Encoding.UTF8.GetString(Metadata.ReadBytes(uiLen));
break; break;
} }
@@ -310,7 +317,7 @@ namespace Il2CppInspector
FunctionAddresses.Add(sortedFunctionPointers[^1], sortedFunctionPointers[^1]); FunctionAddresses.Add(sortedFunctionPointers[^1], sortedFunctionPointers[^1]);
// Organize custom attribute indices // Organize custom attribute indices
if (Version >= 24.1) { if (Version >= 24.1 && Version < 29) {
AttributeIndicesByToken = new Dictionary<int, Dictionary<uint, int>>(); AttributeIndicesByToken = new Dictionary<int, Dictionary<uint, int>>();
foreach (var image in Images) { foreach (var image in Images) {
var attsByToken = new Dictionary<uint, int>(); var attsByToken = new Dictionary<uint, int>();

View File

@@ -63,6 +63,12 @@ namespace Il2CppInspector.Reflection
yield return attribute; yield return attribute;
} }
} }
else
{
Console.WriteLine("Skipping custom attributes for 29+");
yield break;
}
}
private static IList<CustomAttributeData> getCustomAttributes(Assembly asm, int token, int customAttributeIndex) => private static IList<CustomAttributeData> getCustomAttributes(Assembly asm, int token, int customAttributeIndex) =>
getCustomAttributes(asm, asm.Model.GetCustomAttributeIndex(asm, token, customAttributeIndex)).ToList(); getCustomAttributes(asm, asm.Model.GetCustomAttributeIndex(asm, token, customAttributeIndex)).ToList();

View File

@@ -1,366 +1,369 @@
/* /*
Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
Copyright 2020 Robert Xiao - https://robertxiao.ca Copyright 2020 Robert Xiao - https://robertxiao.ca
All rights reserved. All rights reserved.
*/ */
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Il2CppInspector.Reflection namespace Il2CppInspector.Reflection
{ {
public class TypeModel public class TypeModel
{ {
public Il2CppInspector Package { get; } public Il2CppInspector Package { get; }
public List<Assembly> Assemblies { get; } = new List<Assembly>(); public List<Assembly> Assemblies { get; } = new List<Assembly>();
// List of all namespaces defined by the application // List of all namespaces defined by the application
public List<string> Namespaces { get; } public List<string> Namespaces { get; }
// List of all types from TypeDefs ordered by their TypeDefinitionIndex // List of all types from TypeDefs ordered by their TypeDefinitionIndex
public TypeInfo[] TypesByDefinitionIndex { get; } public TypeInfo[] TypesByDefinitionIndex { get; }
// List of all types from TypeRefs ordered by instanceIndex // List of all types from TypeRefs ordered by instanceIndex
public TypeInfo[] TypesByReferenceIndex { get; } public TypeInfo[] TypesByReferenceIndex { get; }
// List of all types from GenericParameters // List of all types from GenericParameters
public TypeInfo[] GenericParameterTypes { get; } public TypeInfo[] GenericParameterTypes { get; }
// List of all methods from MethodSpecs (closed generic methods that can be called; does not need to be in a generic class) // List of all methods from MethodSpecs (closed generic methods that can be called; does not need to be in a generic class)
public Dictionary<Il2CppMethodSpec, MethodBase> GenericMethods { get; } = new Dictionary<Il2CppMethodSpec, MethodBase>(); public Dictionary<Il2CppMethodSpec, MethodBase> GenericMethods { get; } = new Dictionary<Il2CppMethodSpec, MethodBase>();
// List of all type definitions by fully qualified name (TypeDefs only) // List of all type definitions by fully qualified name (TypeDefs only)
public Dictionary<string, TypeInfo> TypesByFullName { get; } = new Dictionary<string, TypeInfo>(); public Dictionary<string, TypeInfo> TypesByFullName { get; } = new Dictionary<string, TypeInfo>();
// Every type // Every type
public IEnumerable<TypeInfo> Types public IEnumerable<TypeInfo> Types
{ {
get get
{ {
types ??= TypesByDefinitionIndex.Concat(TypesByReferenceIndex) types ??= TypesByDefinitionIndex.Concat(TypesByReferenceIndex)
.Concat(GenericMethods.Values.Select(m => m.DeclaringType)).Distinct().Where(t => t != null).ToList(); .Concat(GenericMethods.Values.Select(m => m.DeclaringType)).Distinct().Where(t => t != null).ToList();
return types; return types;
} }
} }
private List<TypeInfo> types; private List<TypeInfo> types;
// List of all methods ordered by their MethodDefinitionIndex // List of all methods ordered by their MethodDefinitionIndex
public MethodBase[] MethodsByDefinitionIndex { get; } public MethodBase[] MethodsByDefinitionIndex { get; }
// List of all Method.Invoke functions by invoker index // List of all Method.Invoke functions by invoker index
public MethodInvoker[] MethodInvokers { get; } public MethodInvoker[] MethodInvokers { get; }
// List of all generated CustomAttributeData objects by their instanceIndex into AttributeTypeIndices // List of all generated CustomAttributeData objects by their instanceIndex into AttributeTypeIndices
public ConcurrentDictionary<int, CustomAttributeData> AttributesByIndices { get; } = new ConcurrentDictionary<int, CustomAttributeData>(); public ConcurrentDictionary<int, CustomAttributeData> AttributesByIndices { get; } = new ConcurrentDictionary<int, CustomAttributeData>();
// List of unique custom attributes generators indexed by type (multiple indices above may refer to a single generator function) // List of unique custom attributes generators indexed by type (multiple indices above may refer to a single generator function)
public Dictionary<TypeInfo, List<CustomAttributeData>> CustomAttributeGenerators { get; } public Dictionary<TypeInfo, List<CustomAttributeData>> CustomAttributeGenerators { get; }
// List of unique custom attributes generators indexed by virtual address // List of unique custom attributes generators indexed by virtual address
public Dictionary<ulong, List<CustomAttributeData>> CustomAttributeGeneratorsByAddress { get; } public Dictionary<ulong, List<CustomAttributeData>> CustomAttributeGeneratorsByAddress { get; }
// Get an assembly by its image name // Get an assembly by its image name
public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name); public Assembly GetAssembly(string name) => Assemblies.FirstOrDefault(a => a.ShortName == name);
// Get a type by its fully qualified name including generic type arguments, array brackets etc. // Get a type by its fully qualified name including generic type arguments, array brackets etc.
// In other words, rather than only being able to fetch a type definition such as in Assembly.GetType(), // In other words, rather than only being able to fetch a type definition such as in Assembly.GetType(),
// this method can also find reference types, types created from TypeRefs and constructed types from MethodSpecs // this method can also find reference types, types created from TypeRefs and constructed types from MethodSpecs
public TypeInfo GetType(string fullName) => Types.FirstOrDefault( public TypeInfo GetType(string fullName) => Types.FirstOrDefault(
t => fullName == t.Namespace + (!string.IsNullOrEmpty(t.Namespace)? "." : "") + t.Name); t => fullName == t.Namespace + (!string.IsNullOrEmpty(t.Namespace)? "." : "") + t.Name);
// Get a concrete instantiation of a generic method from its fully qualified name and type arguments // Get a concrete instantiation of a generic method from its fully qualified name and type arguments
public MethodBase GetGenericMethod(string fullName, params TypeInfo[] typeArguments) => public MethodBase GetGenericMethod(string fullName, params TypeInfo[] typeArguments) =>
GenericMethods.Values.First( GenericMethods.Values.First(
m => fullName == m.DeclaringType.Namespace + (!string.IsNullOrEmpty(m.DeclaringType.Namespace)? "." : "") m => fullName == m.DeclaringType.Namespace + (!string.IsNullOrEmpty(m.DeclaringType.Namespace)? "." : "")
+ m.DeclaringType.Name + "." + m.Name + m.DeclaringType.Name + "." + m.Name
&& m.GetGenericArguments().SequenceEqual(typeArguments)); && m.GetGenericArguments().SequenceEqual(typeArguments));
// Create type model // Create type model
public TypeModel(Il2CppInspector package) { public TypeModel(Il2CppInspector package) {
Package = package; Package = package;
TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length];
TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count]; TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count];
GenericParameterTypes = new TypeInfo[package.GenericParameters.Length]; GenericParameterTypes = new TypeInfo[package.GenericParameters.Length];
MethodsByDefinitionIndex = new MethodBase[package.Methods.Length]; MethodsByDefinitionIndex = new MethodBase[package.Methods.Length];
MethodInvokers = new MethodInvoker[package.MethodInvokePointers.Length]; MethodInvokers = new MethodInvoker[package.MethodInvokePointers.Length];
// Recursively create hierarchy of assemblies and types from TypeDefs // Recursively create hierarchy of assemblies and types from TypeDefs
// No code that executes here can access any type through a TypeRef (ie. via TypesByReferenceIndex) // No code that executes here can access any type through a TypeRef (ie. via TypesByReferenceIndex)
for (var image = 0; image < package.Images.Length; image++) for (var image = 0; image < package.Images.Length; image++)
Assemblies.Add(new Assembly(this, image)); Assemblies.Add(new Assembly(this, image));
// Create and reference types from TypeRefs // Create and reference types from TypeRefs
// Note that you can't resolve any TypeRefs until all the TypeDefs have been processed // Note that you can't resolve any TypeRefs until all the TypeDefs have been processed
for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) { for (int typeRefIndex = 0; typeRefIndex < package.TypeReferences.Count; typeRefIndex++) {
if(TypesByReferenceIndex[typeRefIndex] != null) { if(TypesByReferenceIndex[typeRefIndex] != null) {
/* type already generated - probably by forward reference through GetTypeFromVirtualAddress */ /* type already generated - probably by forward reference through GetTypeFromVirtualAddress */
continue; continue;
} }
var typeRef = Package.TypeReferences[typeRefIndex]; var typeRef = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(typeRef); var referencedType = resolveTypeReference(typeRef);
TypesByReferenceIndex[typeRefIndex] = referencedType; TypesByReferenceIndex[typeRefIndex] = referencedType;
} }
// Create types and methods from MethodSpec (which incorporates TypeSpec in IL2CPP) // Create types and methods from MethodSpec (which incorporates TypeSpec in IL2CPP)
foreach (var spec in Package.MethodSpecs) { foreach (var spec in Package.MethodSpecs) {
var methodDefinition = MethodsByDefinitionIndex[spec.methodDefinitionIndex]; var methodDefinition = MethodsByDefinitionIndex[spec.methodDefinitionIndex];
var declaringType = methodDefinition.DeclaringType; var declaringType = methodDefinition.DeclaringType;
// Concrete instance of a generic class // Concrete instance of a generic class
// If the class index is not specified, we will later create a generic method in a non-generic class // If the class index is not specified, we will later create a generic method in a non-generic class
if (spec.classIndexIndex != -1) { if (spec.classIndexIndex != -1) {
var genericInstance = Package.GenericInstances[spec.classIndexIndex]; var genericInstance = Package.GenericInstances[spec.classIndexIndex];
var genericArguments = ResolveGenericArguments(genericInstance); var genericArguments = ResolveGenericArguments(genericInstance);
declaringType = declaringType.MakeGenericType(genericArguments); declaringType = declaringType.MakeGenericType(genericArguments);
} }
MethodBase method; MethodBase method;
if (methodDefinition is ConstructorInfo) if (methodDefinition is ConstructorInfo)
method = declaringType.GetConstructorByDefinition((ConstructorInfo)methodDefinition); method = declaringType.GetConstructorByDefinition((ConstructorInfo)methodDefinition);
else else
method = declaringType.GetMethodByDefinition((MethodInfo)methodDefinition); method = declaringType.GetMethodByDefinition((MethodInfo)methodDefinition);
if (spec.methodIndexIndex != -1) { if (spec.methodIndexIndex != -1) {
var genericInstance = Package.GenericInstances[spec.methodIndexIndex]; var genericInstance = Package.GenericInstances[spec.methodIndexIndex];
var genericArguments = ResolveGenericArguments(genericInstance); var genericArguments = ResolveGenericArguments(genericInstance);
method = method.MakeGenericMethod(genericArguments); method = method.MakeGenericMethod(genericArguments);
} }
method.VirtualAddress = Package.GetGenericMethodPointer(spec); method.VirtualAddress = Package.GetGenericMethodPointer(spec);
GenericMethods[spec] = method; GenericMethods[spec] = method;
} }
// Generate a list of all namespaces used // Generate a list of all namespaces used
Namespaces = Assemblies.SelectMany(x => x.DefinedTypes).GroupBy(t => t.Namespace).Select(n => n.Key).Distinct().ToList(); Namespaces = Assemblies.SelectMany(x => x.DefinedTypes).GroupBy(t => t.Namespace).Select(n => n.Key).Distinct().ToList();
// Find all custom attribute generators (populate AttributesByIndices) (use ToList() to force evaluation) // Find all custom attribute generators (populate AttributesByIndices) (use ToList() to force evaluation)
var allAssemblyAttributes = Assemblies.Select(a => a.CustomAttributes).ToList(); var allAssemblyAttributes = Assemblies.Select(a => a.CustomAttributes).ToList();
var allTypeAttributes = TypesByDefinitionIndex.Select(t => t.CustomAttributes).ToList(); var allTypeAttributes = TypesByDefinitionIndex.Select(t => t.CustomAttributes).ToList();
var allEventAttributes = TypesByDefinitionIndex.SelectMany(t => t.DeclaredEvents).Select(e => e.CustomAttributes).ToList(); var allEventAttributes = TypesByDefinitionIndex.SelectMany(t => t.DeclaredEvents).Select(e => e.CustomAttributes).ToList();
var allFieldAttributes = TypesByDefinitionIndex.SelectMany(t => t.DeclaredFields).Select(f => f.CustomAttributes).ToList(); var allFieldAttributes = TypesByDefinitionIndex.SelectMany(t => t.DeclaredFields).Select(f => f.CustomAttributes).ToList();
var allPropertyAttributes = TypesByDefinitionIndex.SelectMany(t => t.DeclaredProperties).Select(p => p.CustomAttributes).ToList(); var allPropertyAttributes = TypesByDefinitionIndex.SelectMany(t => t.DeclaredProperties).Select(p => p.CustomAttributes).ToList();
var allMethodAttributes = MethodsByDefinitionIndex.Select(m => m.CustomAttributes).ToList(); var allMethodAttributes = MethodsByDefinitionIndex.Select(m => m.CustomAttributes).ToList();
var allParameterAttributes = MethodsByDefinitionIndex.SelectMany(m => m.DeclaredParameters).Select(p => p.CustomAttributes).ToList(); var allParameterAttributes = MethodsByDefinitionIndex.SelectMany(m => m.DeclaredParameters).Select(p => p.CustomAttributes).ToList();
// Populate list of unique custom attribute generators for each type // Populate list of unique custom attribute generators for each type
CustomAttributeGenerators = AttributesByIndices.Values CustomAttributeGenerators = AttributesByIndices.Values
.GroupBy(a => a.AttributeType) .GroupBy(a => a.AttributeType)
.ToDictionary(g => g.Key, g => g.GroupBy(a => a.VirtualAddress.Start).Select(g => g.First()).ToList()); .ToDictionary(g => g.Key, g => g.GroupBy(a => a.VirtualAddress.Start).Select(g => g.First()).ToList());
// Populate list of unique custom attribute generators for each address // Populate list of unique custom attribute generators for each address
CustomAttributeGeneratorsByAddress = AttributesByIndices.Values CustomAttributeGeneratorsByAddress = AttributesByIndices.Values
.GroupBy(a => a.VirtualAddress.Start) .GroupBy(a => a.VirtualAddress.Start)
.ToDictionary(g => g.Key, g => g.GroupBy(a => a.AttributeType).Select(g => g.First()).ToList()); .ToDictionary(g => g.Key, g => g.GroupBy(a => a.AttributeType).Select(g => g.First()).ToList());
// Create method invokers (one per signature, in invoker index order) // Create method invokers (one per signature, in invoker index order)
// Generic type definitions have an invoker index of -1 // Generic type definitions have an invoker index of -1
foreach (var method in MethodsByDefinitionIndex) { foreach (var method in MethodsByDefinitionIndex) {
var index = package.GetInvokerIndex(method.DeclaringType.Assembly.ModuleDefinition, method.Definition); var index = package.GetInvokerIndex(method.DeclaringType.Assembly.ModuleDefinition, method.Definition);
if (index != -1) { if (index != -1) {
if (MethodInvokers[index] == null) if (MethodInvokers[index] == null)
MethodInvokers[index] = new MethodInvoker(method, index); MethodInvokers[index] = new MethodInvoker(method, index);
method.Invoker = MethodInvokers[index]; method.Invoker = MethodInvokers[index];
} }
} }
// Create method invokers sourced from generic method invoker indices // Create method invokers sourced from generic method invoker indices
foreach (var spec in GenericMethods.Keys) { foreach (var spec in GenericMethods.Keys) {
if (package.GenericMethodInvokerIndices.TryGetValue(spec, out var index)) { if (package.GenericMethodInvokerIndices.TryGetValue(spec, out var index)) {
if (MethodInvokers[index] == null) if (MethodInvokers[index] == null)
MethodInvokers[index] = new MethodInvoker(GenericMethods[spec], index); MethodInvokers[index] = new MethodInvoker(GenericMethods[spec], index);
GenericMethods[spec].Invoker = MethodInvokers[index]; GenericMethods[spec].Invoker = MethodInvokers[index];
} }
} }
// Post-processing hook // Post-processing hook
PluginHooks.PostProcessTypeModel(this); PluginHooks.PostProcessTypeModel(this);
} }
// Get generic arguments from either a type or method instanceIndex from a MethodSpec // Get generic arguments from either a type or method instanceIndex from a MethodSpec
public TypeInfo[] ResolveGenericArguments(Il2CppGenericInst inst) { public TypeInfo[] ResolveGenericArguments(Il2CppGenericInst inst) {
// Get list of pointers to type parameters (both unresolved and concrete) // Get list of pointers to type parameters (both unresolved and concrete)
var genericTypeArguments = Package.BinaryImage.ReadMappedArray<ulong>(inst.type_argv, (int)inst.type_argc); var genericTypeArguments = Package.BinaryImage.ReadMappedArray<ulong>(inst.type_argv, (int)inst.type_argc);
return genericTypeArguments.Select(a => GetTypeFromVirtualAddress(a)).ToArray(); return genericTypeArguments.Select(a => GetTypeFromVirtualAddress(a)).ToArray();
} }
// Initialize type from type reference (TypeRef) // Initialize type from type reference (TypeRef)
// Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType // Much of the following is adapted from il2cpp::vm::Class::FromIl2CppType
private TypeInfo resolveTypeReference(Il2CppType typeRef) { private TypeInfo resolveTypeReference(Il2CppType typeRef) {
var image = Package.BinaryImage; var image = Package.BinaryImage;
TypeInfo underlyingType; TypeInfo underlyingType;
switch (typeRef.type) { switch (typeRef.type) {
// Classes defined in the metadata (reference to a TypeDef) // Classes defined in the metadata (reference to a TypeDef)
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
underlyingType = TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex underlyingType = TypesByDefinitionIndex[typeRef.datapoint]; // klassIndex
break; break;
// Constructed types // Constructed types
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
// TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses // TODO: Replace with array load from Il2CppMetadataRegistration.genericClasses
var generic = image.ReadMappedObject<Il2CppGenericClass>(typeRef.datapoint); // Il2CppGenericClass * var generic = image.ReadMappedObject<Il2CppGenericClass>(typeRef.datapoint); // Il2CppGenericClass *
// Get generic type definition // Get generic type definition
TypeInfo genericTypeDef; TypeInfo genericTypeDef;
if (Package.Version < 27) { if (Package.Version < 27) {
// It appears that TypeRef can be -1 if the generic depth recursion limit // It appears that TypeRef can be -1 if the generic depth recursion limit
// (--maximum-recursive-generic-depth=) is reached in Il2Cpp. In this case, // (--maximum-recursive-generic-depth=) is reached in Il2Cpp. In this case,
// no generic instance type is generated, so we just produce a null TypeInfo here. // no generic instance type is generated, so we just produce a null TypeInfo here.
if ((generic.typeDefinitionIndex & 0xffff_ffff) == 0x0000_0000_ffff_ffff) if ((generic.typeDefinitionIndex & 0xffff_ffff) == 0x0000_0000_ffff_ffff)
return null; return null;
genericTypeDef = TypesByDefinitionIndex[generic.typeDefinitionIndex]; genericTypeDef = TypesByDefinitionIndex[generic.typeDefinitionIndex];
} else { } else {
genericTypeDef = GetTypeFromVirtualAddress(generic.type); genericTypeDef = GetTypeFromVirtualAddress(generic.type);
} }
// Get the instantiation // Get the instantiation
// TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts // TODO: Replace with array load from Il2CppMetadataRegistration.genericInsts
var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst); var genericInstance = image.ReadMappedObject<Il2CppGenericInst>(generic.context.class_inst);
var genericArguments = ResolveGenericArguments(genericInstance); var genericArguments = ResolveGenericArguments(genericInstance);
underlyingType = genericTypeDef.MakeGenericType(genericArguments); underlyingType = genericTypeDef.MakeGenericType(genericArguments);
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY:
var descriptor = image.ReadMappedObject<Il2CppArrayType>(typeRef.datapoint); var descriptor = image.ReadMappedObject<Il2CppArrayType>(typeRef.datapoint);
var elementType = GetTypeFromVirtualAddress(descriptor.etype); var elementType = GetTypeFromVirtualAddress(descriptor.etype);
underlyingType = elementType.MakeArrayType(descriptor.rank); underlyingType = elementType.MakeArrayType(descriptor.rank);
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
elementType = GetTypeFromVirtualAddress(typeRef.datapoint); elementType = GetTypeFromVirtualAddress(typeRef.datapoint);
underlyingType = elementType.MakeArrayType(1); underlyingType = elementType.MakeArrayType(1);
break; break;
case Il2CppTypeEnum.IL2CPP_TYPE_PTR: case Il2CppTypeEnum.IL2CPP_TYPE_PTR:
elementType = GetTypeFromVirtualAddress(typeRef.datapoint); elementType = GetTypeFromVirtualAddress(typeRef.datapoint);
underlyingType = elementType.MakePointerType(); underlyingType = elementType.MakePointerType();
break; break;
// Generic type and generic method parameters // Generic type and generic method parameters
case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_VAR:
case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR:
underlyingType = GetGenericParameterType((int)typeRef.datapoint); underlyingType = GetGenericParameterType((int)typeRef.datapoint);
break; break;
// Primitive types // Primitive types
default: default:
underlyingType = getTypeDefinitionFromTypeEnum(typeRef.type); underlyingType = getTypeDefinitionFromTypeEnum(typeRef.type);
break; break;
} }
// Create a reference type if necessary // Create a reference type if necessary
return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType; return typeRef.byref ? underlyingType.MakeByRefType() : underlyingType;
} }
// Basic primitive types are specified via a flag value // Basic primitive types are specified via a flag value
private TypeInfo getTypeDefinitionFromTypeEnum(Il2CppTypeEnum t) { private TypeInfo getTypeDefinitionFromTypeEnum(Il2CppTypeEnum t) {
if ((int)t >= Il2CppConstants.FullNameTypeString.Count) if ((int)t >= Il2CppConstants.FullNameTypeString.Count)
return null; return null;
var fqn = Il2CppConstants.FullNameTypeString[(int)t]; var fqn = Il2CppConstants.FullNameTypeString[(int)t];
return TypesByFullName[fqn]; return TypesByFullName[fqn];
} }
// Get a TypeRef by its virtual address // Get a TypeRef by its virtual address
// These are always nested types from references within another TypeRef // These are always nested types from references within another TypeRef
public TypeInfo GetTypeFromVirtualAddress(ulong ptr) { public TypeInfo GetTypeFromVirtualAddress(ulong ptr) {
var typeRefIndex = Package.TypeReferenceIndicesByAddress[ptr]; var typeRefIndex = Package.TypeReferenceIndicesByAddress[ptr];
if (TypesByReferenceIndex[typeRefIndex] != null) if (TypesByReferenceIndex[typeRefIndex] != null)
return TypesByReferenceIndex[typeRefIndex]; return TypesByReferenceIndex[typeRefIndex];
var type = Package.TypeReferences[typeRefIndex]; var type = Package.TypeReferences[typeRefIndex];
var referencedType = resolveTypeReference(type); var referencedType = resolveTypeReference(type);
TypesByReferenceIndex[typeRefIndex] = referencedType; TypesByReferenceIndex[typeRefIndex] = referencedType;
return referencedType; return referencedType;
} }
public TypeInfo GetGenericParameterType(int index) { public TypeInfo GetGenericParameterType(int index) {
if (GenericParameterTypes[index] != null) if (GenericParameterTypes[index] != null)
return GenericParameterTypes[index]; return GenericParameterTypes[index];
var paramType = Package.GenericParameters[index]; // genericParameterIndex var paramType = Package.GenericParameters[index]; // genericParameterIndex
var container = Package.GenericContainers[paramType.ownerIndex]; var container = Package.GenericContainers[paramType.ownerIndex];
TypeInfo result; TypeInfo result;
if (container.is_method == 1) { if (container.is_method == 1) {
var owner = MethodsByDefinitionIndex[container.ownerIndex]; var owner = MethodsByDefinitionIndex[container.ownerIndex];
result = new TypeInfo(owner, paramType); result = new TypeInfo(owner, paramType);
} else { } else {
var owner = TypesByDefinitionIndex[container.ownerIndex]; var owner = TypesByDefinitionIndex[container.ownerIndex];
result = new TypeInfo(owner, paramType); result = new TypeInfo(owner, paramType);
} }
GenericParameterTypes[index] = result; GenericParameterTypes[index] = result;
return result; return result;
} }
// 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 // 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, int token, int customAttributeIndex) { public int GetCustomAttributeIndex(Assembly asm, int token, int customAttributeIndex) {
// Prior to v24.1, Type, Field, Parameter, Method, Event, Property, Assembly definitions had their own customAttributeIndex field // Prior to v24.1, Type, Field, Parameter, Method, Event, Property, Assembly definitions had their own customAttributeIndex field
if (Package.Version <= 24.0) if (Package.Version <= 24.0)
return customAttributeIndex; return customAttributeIndex;
// From v24.1 onwards, token was added to Il2CppCustomAttributeTypeRange and each Il2CppImageDefinition noted the CustomAttributeTypeRanges for the image if (Package.Version >= 29)
if (!Package.AttributeIndicesByToken[asm.ImageDefinition.customAttributeStart].TryGetValue((uint) token, out var index)) return -1;
return -1;
return index; // 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((uint) token, out var index))
return -1;
// Get the name of a metadata typeRef return index;
public string GetMetadataUsageName(MetadataUsage usage) { }
switch (usage.Type) {
case MetadataUsageType.TypeInfo: // Get the name of a metadata typeRef
case MetadataUsageType.Type: public string GetMetadataUsageName(MetadataUsage usage) {
return GetMetadataUsageType(usage).Name; switch (usage.Type) {
case MetadataUsageType.TypeInfo:
case MetadataUsageType.MethodDef: case MetadataUsageType.Type:
var method = GetMetadataUsageMethod(usage); return GetMetadataUsageType(usage).Name;
return $"{method.DeclaringType.Name}.{method.Name}";
case MetadataUsageType.MethodDef:
case MetadataUsageType.FieldInfo: var method = GetMetadataUsageMethod(usage);
var fieldRef = Package.FieldRefs[usage.SourceIndex]; return $"{method.DeclaringType.Name}.{method.Name}";
var type = GetMetadataUsageType(usage);
var field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex); case MetadataUsageType.FieldInfo:
return $"{type.Name}.{field.Name}"; var fieldRef = Package.FieldRefs[usage.SourceIndex];
var type = GetMetadataUsageType(usage);
case MetadataUsageType.StringLiteral: var field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex);
return Package.StringLiterals[usage.SourceIndex]; return $"{type.Name}.{field.Name}";
case MetadataUsageType.MethodRef: case MetadataUsageType.StringLiteral:
type = GetMetadataUsageType(usage); return Package.StringLiterals[usage.SourceIndex];
method = GetMetadataUsageMethod(usage);
return $"{type.Name}.{method.Name}"; case MetadataUsageType.MethodRef:
} type = GetMetadataUsageType(usage);
throw new NotImplementedException("Unknown metadata usage type: " + usage.Type); method = GetMetadataUsageMethod(usage);
} return $"{type.Name}.{method.Name}";
}
// Get the type used in a metadata usage throw new NotImplementedException("Unknown metadata usage type: " + usage.Type);
public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch { }
MetadataUsageType.Type => TypesByReferenceIndex[usage.SourceIndex],
MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex], // Get the type used in a metadata usage
MetadataUsageType.MethodDef => GetMetadataUsageMethod(usage).DeclaringType, public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch {
MetadataUsageType.FieldInfo => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex], MetadataUsageType.Type => TypesByReferenceIndex[usage.SourceIndex],
MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType, MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex],
MetadataUsageType.MethodDef => GetMetadataUsageMethod(usage).DeclaringType,
_ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type") MetadataUsageType.FieldInfo => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex],
}; MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType,
// Get the method used in a metadata usage _ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type")
public MethodBase GetMetadataUsageMethod(MetadataUsage usage) => usage.Type switch { };
MetadataUsageType.MethodDef => MethodsByDefinitionIndex[usage.SourceIndex],
MetadataUsageType.MethodRef => GenericMethods[Package.MethodSpecs[usage.SourceIndex]], // Get the method used in a metadata usage
_ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type") public MethodBase GetMetadataUsageMethod(MetadataUsage usage) => usage.Type switch {
}; MetadataUsageType.MethodDef => MethodsByDefinitionIndex[usage.SourceIndex],
} MetadataUsageType.MethodRef => GenericMethods[Package.MethodSpecs[usage.SourceIndex]],
_ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type")
};
}
} }