cache tyoes

This commit is contained in:
LukeFZ
2023-11-29 21:19:41 +01:00
parent 96e466743d
commit 9f0d0476bb
2 changed files with 381 additions and 361 deletions

View File

@@ -1,359 +1,369 @@
/* /*
Copyright 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty Copyright 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
All rights reserved. All rights reserved.
*/ */
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Aron.Weiler; using Aron.Weiler;
using Il2CppInspector.Cpp; using Il2CppInspector.Cpp;
using Il2CppInspector.Cpp.UnityHeaders; using Il2CppInspector.Cpp.UnityHeaders;
using Il2CppInspector.Reflection; using Il2CppInspector.Reflection;
namespace Il2CppInspector.Model namespace Il2CppInspector.Model
{ {
// Class that represents the entire structure of the IL2CPP binary realized as C++ types and code, // Class that represents the entire structure of the IL2CPP binary realized as C++ types and code,
// correlated with .NET types where applicable. Primarily designed to enable automated static analysis of disassembly code. // correlated with .NET types where applicable. Primarily designed to enable automated static analysis of disassembly code.
public class AppModel : IEnumerable<CppType> public class AppModel : IEnumerable<CppType>
{ {
// The C++ compiler to target // The C++ compiler to target
public CppCompilerType TargetCompiler { get; private set; } public CppCompilerType TargetCompiler { get; private set; }
// The Unity version used to build the binary // The Unity version used to build the binary
public UnityVersion UnityVersion { get; private set; } public UnityVersion UnityVersion { get; private set; }
// The Unity IL2CPP C++ headers for the binary // The Unity IL2CPP C++ headers for the binary
// Use this for code output // Use this for code output
public UnityHeaders UnityHeaders { get; private set; } public UnityHeaders UnityHeaders { get; private set; }
// All of the C++ types used in the application including Unity internal types // All of the C++ types used in the application including Unity internal types
// NOTE: This is for querying individual types for static analysis // NOTE: This is for querying individual types for static analysis
// To generate code output, use DependencyOrderedCppTypes // To generate code output, use DependencyOrderedCppTypes
public CppTypeCollection CppTypeCollection { get; private set; } public CppTypeCollection CppTypeCollection { get; private set; }
// All of the C++ types used in the application (.NET type translations only) // All of the C++ types used in the application (.NET type translations only)
// The types are ordered to enable the production of code output without forward dependencies // The types are ordered to enable the production of code output without forward dependencies
public List<CppType> DependencyOrderedCppTypes { get; private set; } public List<CppType> DependencyOrderedCppTypes { get; private set; }
// Composite mapping of all the .NET methods in the IL2CPP binary // Composite mapping of all the .NET methods in the IL2CPP binary
public MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod> Methods { get; } = new MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod>(); public MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod> Methods { get; } = new MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod>();
// Composite mapping of all the .NET types in the IL2CPP binary // Composite mapping of all the .NET types in the IL2CPP binary
public MultiKeyDictionary<TypeInfo, CppComplexType, AppType> Types { get; } = new MultiKeyDictionary<TypeInfo, CppComplexType, AppType>(); public MultiKeyDictionary<TypeInfo, CppComplexType, AppType> Types { get; } = new MultiKeyDictionary<TypeInfo, CppComplexType, AppType>();
// All of the string literals in the IL2CPP binary // All of the string literals in the IL2CPP binary
// Note: Does not include string literals from global-metadata.dat // Note: Does not include string literals from global-metadata.dat
// Note: The virtual addresses are of String* (VAs of the pointer to String*) objects, not the strings themselves // Note: The virtual addresses are of String* (VAs of the pointer to String*) objects, not the strings themselves
// For il2cpp < 19, the key is the string literal ordinal instead of the address // For il2cpp < 19, the key is the string literal ordinal instead of the address
public Dictionary<ulong, string> Strings { get; } = new Dictionary<ulong, string>(); public Dictionary<ulong, string> Strings { get; } = new Dictionary<ulong, string>();
public Dictionary<ulong, string> Fields { get; } = new Dictionary<ulong, string>(); public Dictionary<ulong, string> Fields { get; } = new Dictionary<ulong, string>();
public bool StringIndexesAreOrdinals => Package.Version < 19; public bool StringIndexesAreOrdinals => Package.Version < 19;
// The .NET type model for the application // The .NET type model for the application
public TypeModel TypeModel { get; } public TypeModel TypeModel { get; }
// All of the exports (including function exports) for the binary // All of the exports (including function exports) for the binary
public List<Export> Exports { get; } public List<Export> Exports { get; }
// All of the symbols representing function names, signatures or type/field names or address labels for the binary // All of the symbols representing function names, signatures or type/field names or address labels for the binary
public Dictionary<string, Symbol> Symbols { get; } public Dictionary<string, Symbol> Symbols { get; }
// All of the API exports defined in the IL2CPP binary // All of the API exports defined in the IL2CPP binary
// Note: Multiple export names may have the same virtual address // Note: Multiple export names may have the same virtual address
public MultiKeyDictionary<string, ulong, CppFnPtrType> AvailableAPIs { get; } = new MultiKeyDictionary<string, ulong, CppFnPtrType>(); public MultiKeyDictionary<string, ulong, CppFnPtrType> AvailableAPIs { get; } = new MultiKeyDictionary<string, ulong, CppFnPtrType>();
// Delegated C++ types iterator // Delegated C++ types iterator
public IEnumerator<CppType> GetEnumerator() => CppTypeCollection.GetEnumerator(); public IEnumerator<CppType> GetEnumerator() => CppTypeCollection.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) CppTypeCollection).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) CppTypeCollection).GetEnumerator();
// The C++ declaration generator for this binary // The C++ declaration generator for this binary
private CppDeclarationGenerator declarationGenerator; private CppDeclarationGenerator declarationGenerator;
// Convenience properties // Convenience properties
// The word size of the binary in bits // The word size of the binary in bits
public int WordSizeBits => Image.Bits; public int WordSizeBits => Image.Bits;
// The word size of the binary in bytes // The word size of the binary in bytes
public int WordSizeBytes => WordSizeBits / 8; public int WordSizeBytes => WordSizeBits / 8;
// The binary image // The binary image
public IFileFormatStream Image => Package.BinaryImage; public IFileFormatStream Image => Package.BinaryImage;
// The IL2CPP package for this application // The IL2CPP package for this application
public Il2CppInspector Package => TypeModel.Package; public Il2CppInspector Package => TypeModel.Package;
// The compiler used to build the binary // The compiler used to build the binary
public CppCompilerType SourceCompiler => declarationGenerator.InheritanceStyle; public CppCompilerType SourceCompiler => declarationGenerator.InheritanceStyle;
// The group that the next added type(s) will be placed in // The group that the next added type(s) will be placed in
private string group = string.Empty; private string group = string.Empty;
private string Group { private string Group {
get => group; get => group;
set { set {
group = value; group = value;
CppTypeCollection.SetGroup(group); CppTypeCollection.SetGroup(group);
} }
} }
// Initialize // Initialize
public AppModel(TypeModel model, bool makeDefaultBuild = true) { public AppModel(TypeModel model, bool makeDefaultBuild = true) {
// Save .NET type model // Save .NET type model
TypeModel = model; TypeModel = model;
// Get addresses of all exports // Get addresses of all exports
Exports = Image.GetExports()?.ToList() ?? new List<Export>(); Exports = Image.GetExports()?.ToList() ?? new List<Export>();
// Get all symbols // Get all symbols
Symbols = Image.GetSymbolTable(); Symbols = Image.GetSymbolTable();
// Build if requested // Build if requested
if (makeDefaultBuild) if (makeDefaultBuild)
Build(); Build();
} }
// Build the application model targeting a specific version of Unity and C++ compiler // Build the application model targeting a specific version of Unity and C++ compiler
// If no Unity version is specified, it will be guessed from the contents of the IL2CPP binary // If no Unity version is specified, it will be guessed from the contents of the IL2CPP binary
// The C++ compiler used to actually build the original IL2CPP binary will always be guessed based on the binary file format // The C++ compiler used to actually build the original IL2CPP binary will always be guessed based on the binary file format
// (via the constructor of CppDeclarationGenerator, in InheritanceStyle) // (via the constructor of CppDeclarationGenerator, in InheritanceStyle)
// If no target C++ compiler is specified, it will be set to match the one assumed to have been used to compile the binary // If no target C++ compiler is specified, it will be set to match the one assumed to have been used to compile the binary
public AppModel Build(UnityVersion unityVersion = null, CppCompilerType compiler = CppCompilerType.BinaryFormat, bool silent = false) { public AppModel Build(UnityVersion unityVersion = null, CppCompilerType compiler = CppCompilerType.BinaryFormat, bool silent = false) {
// Don't re-build if not necessary // Don't re-build if not necessary
var targetCompiler = compiler == CppCompilerType.BinaryFormat ? CppCompiler.GuessFromImage(Image) : compiler; var targetCompiler = compiler == CppCompilerType.BinaryFormat ? CppCompiler.GuessFromImage(Image) : compiler;
if (UnityVersion == unityVersion && TargetCompiler == targetCompiler) if (UnityVersion == unityVersion && TargetCompiler == targetCompiler)
return this; return this;
// Silent operation if requested // Silent operation if requested
var stdout = Console.Out; var stdout = Console.Out;
if (silent) if (silent)
Console.SetOut(new StreamWriter(Stream.Null)); Console.SetOut(new StreamWriter(Stream.Null));
// Reset in case this is not the first build // Reset in case this is not the first build
Methods.Clear(); Methods.Clear();
Types.Clear(); Types.Clear();
Strings.Clear(); Strings.Clear();
// Set target compiler // Set target compiler
TargetCompiler = targetCompiler; TargetCompiler = targetCompiler;
// Determine Unity version and get headers // Determine Unity version and get headers
UnityHeaders = unityVersion != null ? UnityHeaders.GetHeadersForVersion(unityVersion) : UnityHeaders.GuessHeadersForBinary(TypeModel.Package.Binary).Last(); UnityHeaders = unityVersion != null ? UnityHeaders.GetHeadersForVersion(unityVersion) : UnityHeaders.GuessHeadersForBinary(TypeModel.Package.Binary).Last();
UnityVersion = unityVersion ?? UnityHeaders.VersionRange.Min; UnityVersion = unityVersion ?? UnityHeaders.VersionRange.Min;
Console.WriteLine($"Selected Unity version(s) {UnityHeaders.VersionRange} (types: {UnityHeaders.TypeHeaderResource.VersionRange}, APIs: {UnityHeaders.APIHeaderResource.VersionRange})"); Console.WriteLine($"Selected Unity version(s) {UnityHeaders.VersionRange} (types: {UnityHeaders.TypeHeaderResource.VersionRange}, APIs: {UnityHeaders.APIHeaderResource.VersionRange})");
// Check for matching metadata and binary versions // Check for matching metadata and binary versions
if (UnityHeaders.MetadataVersion != Image.Version) { if (UnityHeaders.MetadataVersion != Image.Version) {
Console.WriteLine($"Warning: selected version {UnityVersion} (metadata version {UnityHeaders.MetadataVersion})" + Console.WriteLine($"Warning: selected version {UnityVersion} (metadata version {UnityHeaders.MetadataVersion})" +
$" does not match metadata version {Image.Version}."); $" does not match metadata version {Image.Version}.");
} }
// Initialize declaration generator to process every type in the binary // Initialize declaration generator to process every type in the binary
declarationGenerator = new CppDeclarationGenerator(this); declarationGenerator = new CppDeclarationGenerator(this);
// Start creation of type model by parsing all of the Unity IL2CPP headers // Start creation of type model by parsing all of the Unity IL2CPP headers
// Calling declarationGenerator.GenerateRemainingTypeDeclarations() below will automatically add to this collection // Calling declarationGenerator.GenerateRemainingTypeDeclarations() below will automatically add to this collection
CppTypeCollection = CppTypeCollection.FromUnityHeaders(UnityHeaders, declarationGenerator); CppTypeCollection = CppTypeCollection.FromUnityHeaders(UnityHeaders, declarationGenerator);
// Populate AvailableAPIs with actual API symbols from Binary.GetAPIExports() and their matching header signatures // Populate AvailableAPIs with actual API symbols from Binary.GetAPIExports() and their matching header signatures
// NOTE: This will only be filled with exports that actually exist in both the binary and the API header, // NOTE: This will only be filled with exports that actually exist in both the binary and the API header,
// and have a mappable address. This prevents exceptions when cross-querying the header and binary APIs. // and have a mappable address. This prevents exceptions when cross-querying the header and binary APIs.
var exports = TypeModel.Package.Binary.APIExports var exports = TypeModel.Package.Binary.APIExports
.Where(e => CppTypeCollection.TypedefAliases.ContainsKey(e.Key)) .Where(e => CppTypeCollection.TypedefAliases.ContainsKey(e.Key))
.Select(e => new { .Select(e => new {
VirtualAddress = e.Value, VirtualAddress = e.Value,
FnPtr = CppTypeCollection.TypedefAliases[e.Key] FnPtr = CppTypeCollection.TypedefAliases[e.Key]
}); });
AvailableAPIs.Clear(); AvailableAPIs.Clear();
foreach (var export in exports) foreach (var export in exports)
AvailableAPIs.Add(export.FnPtr.Name, export.VirtualAddress, (CppFnPtrType) export.FnPtr); AvailableAPIs.Add(export.FnPtr.Name, export.VirtualAddress, (CppFnPtrType) export.FnPtr);
// Initialize ordered type list for code output // Initialize ordered type list for code output
DependencyOrderedCppTypes = new List<CppType>(); DependencyOrderedCppTypes = new List<CppType>();
// Add method definitions and types used by them to C++ type model // Add method definitions and types used by them to C++ type model
Group = "types_from_methods"; Group = "types_from_methods";
foreach (var method in TypeModel.MethodsByDefinitionIndex.Where(m => m.VirtualAddress.HasValue)) { foreach (var method in TypeModel.MethodsByDefinitionIndex.Where(m => m.VirtualAddress.HasValue)) {
declarationGenerator.IncludeMethod(method); declarationGenerator.IncludeMethod(method);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
var fnPtr = declarationGenerator.GenerateMethodDeclaration(method); var fnPtr = declarationGenerator.GenerateMethodDeclaration(method);
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) {Group = Group}); Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) {Group = Group});
} }
// Add generic methods definitions and types used by them to C++ type model // Add generic methods definitions and types used by them to C++ type model
Group = "types_from_generic_methods"; Group = "types_from_generic_methods";
foreach (var method in TypeModel.GenericMethods.Values.Where(m => m.VirtualAddress.HasValue)) { foreach (var method in TypeModel.GenericMethods.Values.Where(m => m.VirtualAddress.HasValue)) {
declarationGenerator.IncludeMethod(method); declarationGenerator.IncludeMethod(method);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
var fnPtr = declarationGenerator.GenerateMethodDeclaration(method); var fnPtr = declarationGenerator.GenerateMethodDeclaration(method);
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) {Group = Group}); Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) {Group = Group});
} }
// Add types from metadata usage list to C++ type model // Add types from metadata usage list to C++ type model
// Not supported in il2cpp <19 // Not supported in il2cpp <19
Group = "types_from_usages"; Group = "types_from_usages";
if (Package.MetadataUsages != null) if (Package.MetadataUsages != null)
foreach (var usage in Package.MetadataUsages) { foreach (var usage in Package.MetadataUsages) {
var address = usage.VirtualAddress; var address = usage.VirtualAddress;
switch (usage.Type) { switch (usage.Type) {
case MetadataUsageType.StringLiteral: case MetadataUsageType.StringLiteral:
var str = TypeModel.GetMetadataUsageName(usage); //if (usage.SourceIndex >= TypeModel.Package.Metadata.StringLiterals.Length)
Strings.Add(address, str); // break;
break;
var str = TypeModel.GetMetadataUsageName(usage);
case MetadataUsageType.Type: Strings.Add(address, str);
case MetadataUsageType.TypeInfo: break;
var type = TypeModel.GetMetadataUsageType(usage);
declarationGenerator.IncludeType(type); case MetadataUsageType.Type:
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); case MetadataUsageType.TypeInfo:
//if (usage.SourceIndex >= TypeModel.TypesByReferenceIndex.Length)
if (usage.Type == MetadataUsageType.TypeInfo) // break;
// Regular type definition
Types[type].TypeClassAddress = address; var type = TypeModel.GetMetadataUsageType(usage);
declarationGenerator.IncludeType(type);
else if (!Types.ContainsKey(type)) AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
// Generic type definition has no associated C++ type, therefore no dictionary sub-key
Types.Add(type, new AppType(type, null, cppTypeRefPtr: address) {Group = Group}); if (usage.Type == MetadataUsageType.TypeInfo)
else // Regular type definition
// Regular type reference Types[type].TypeClassAddress = address;
Types[type].TypeRefPtrAddress = address;
break; else if (!Types.ContainsKey(type))
case MetadataUsageType.MethodDef: // Generic type definition has no associated C++ type, therefore no dictionary sub-key
case MetadataUsageType.MethodRef: Types.Add(type, new AppType(type, null, cppTypeRefPtr: address) { Group = Group });
var method = TypeModel.GetMetadataUsageMethod(usage); else
declarationGenerator.IncludeMethod(method); // Regular type reference
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); Types[type].TypeRefPtrAddress = address;
break;
// Any method here SHOULD already be in the Methods list case MetadataUsageType.MethodDef:
// but we have seen one example where this is not the case for a MethodDef case MetadataUsageType.MethodRef:
if (!Methods.ContainsKey(method)) { //if (usage.SourceIndex > TypeModel.Package.Metadata.Methods.Length)
var fnPtr = declarationGenerator.GenerateMethodDeclaration(method); // break;
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) {Group = Group});
} var method = TypeModel.GetMetadataUsageMethod(usage);
Methods[method].MethodInfoPtrAddress = address; declarationGenerator.IncludeMethod(method);
break; AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
case MetadataUsageType.FieldInfo: // Any method here SHOULD already be in the Methods list
if (usage.SourceIndex > TypeModel.Package.Metadata.FieldRefs.Length) // but we have seen one example where this is not the case for a MethodDef
break; if (!Methods.ContainsKey(method))
{
var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex]; var fnPtr = declarationGenerator.GenerateMethodDeclaration(method);
var fieldType = TypeModel.GetMetadataUsageType(usage); Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) { Group = Group });
var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex); }
Fields.Add(usage.VirtualAddress, $"{fieldType.Name}.{field.Name}".ToCIdentifier()); Methods[method].MethodInfoPtrAddress = address;
break; break;
}
} case MetadataUsageType.FieldInfo:
if (usage.SourceIndex > TypeModel.Package.Metadata.FieldRefs.Length)
// Add string literals for metadata <19 to the model break;
if (Package.Version < 19) {
/* Version < 19 calls `il2cpp_codegen_string_literal_from_index` to get string literals. var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex];
* Unfortunately, metadata references are just loose globals in Il2CppMetadataUsage.cpp var fieldType = TypeModel.GetMetadataUsageType(usage);
* so we can't automatically name those. Next best thing is to define an enum for the strings. */ var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex);
for (var i = 0; i < Package.StringLiterals.Length; i++) { Fields.Add(usage.VirtualAddress, $"{fieldType.Name}.{field.Name}".ToCIdentifier());
var str = Package.StringLiterals[i]; break;
Strings.Add((ulong) i, str); }
} }
}
// Add string literals for metadata <19 to the model
// Find unused concrete value types if (Package.Version < 19) {
var usedTypes = Types.Values.Select(t => t.Type); /* Version < 19 calls `il2cpp_codegen_string_literal_from_index` to get string literals.
var unusedTypes = TypeModel.Types.Except(usedTypes); * Unfortunately, metadata references are just loose globals in Il2CppMetadataUsage.cpp
var unusedConcreteTypes = unusedTypes.Where(t => !t.IsGenericType && !t.IsGenericParameter * so we can't automatically name those. Next best thing is to define an enum for the strings. */
&& !t.IsByRef && !t.IsPointer && !t.IsArray && !t.IsAbstract && t.Name != "<Module>"); for (var i = 0; i < Package.StringLiterals.Length; i++) {
var str = Package.StringLiterals[i];
Group = "unused_concrete_types"; Strings.Add((ulong) i, str);
}
foreach (var type in unusedConcreteTypes) }
declarationGenerator.IncludeType(type);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); // Find unused concrete value types
var usedTypes = Types.Values.Select(t => t.Type);
// Restore stdout var unusedTypes = TypeModel.Types.Except(usedTypes);
Console.SetOut(stdout); var unusedConcreteTypes = unusedTypes.Where(t => !t.IsGenericType && !t.IsGenericParameter
&& !t.IsByRef && !t.IsPointer && !t.IsArray && !t.IsAbstract && t.Name != "<Module>");
// Plugin hook to post-process model
PluginHooks.PostProcessAppModel(this); Group = "unused_concrete_types";
// This is to allow this method to be chained after a new expression foreach (var type in unusedConcreteTypes)
return this; declarationGenerator.IncludeType(type);
} AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
private void AddTypes(List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, // Restore stdout
CppComplexType fieldsType, CppComplexType vtableType, CppComplexType staticsType)> types) { Console.SetOut(stdout);
// Add types to dependency-ordered list // Plugin hook to post-process model
foreach (var type in types) { PluginHooks.PostProcessAppModel(this);
if (type.vtableType != null)
DependencyOrderedCppTypes.Add(type.vtableType); // This is to allow this method to be chained after a new expression
if (type.staticsType != null) return this;
DependencyOrderedCppTypes.Add(type.staticsType); }
if (type.fieldsType != null) private void AddTypes(List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType,
DependencyOrderedCppTypes.Add(type.fieldsType); CppComplexType fieldsType, CppComplexType vtableType, CppComplexType staticsType)> types) {
if (type.valueType != null)
DependencyOrderedCppTypes.Add(type.valueType); // Add types to dependency-ordered list
foreach (var type in types) {
DependencyOrderedCppTypes.Add(type.referenceType); if (type.vtableType != null)
} DependencyOrderedCppTypes.Add(type.vtableType);
if (type.staticsType != null)
// Create composite types DependencyOrderedCppTypes.Add(type.staticsType);
foreach (var type in types)
if (!Types.ContainsKey(type.ilType)) if (type.fieldsType != null)
Types.Add(type.ilType, type.referenceType, new AppType(type.ilType, type.referenceType, type.valueType) {Group = Group}); DependencyOrderedCppTypes.Add(type.fieldsType);
} if (type.valueType != null)
DependencyOrderedCppTypes.Add(type.valueType);
// Get all the C++ types for a group
public IEnumerable<CppType> GetCppTypeGroup(string groupName) => CppTypeCollection.GetTypeGroup(groupName); DependencyOrderedCppTypes.Add(type.referenceType);
public IEnumerable<CppType> GetDependencyOrderedCppTypeGroup(string groupName) => DependencyOrderedCppTypes.Where(t => t.Group == groupName); }
// Get all the composite types for a group // Create composite types
public IEnumerable<AppType> GetTypeGroup(string groupName) => Types.Values.Where(t => t.Group == groupName); foreach (var type in types)
if (!Types.ContainsKey(type.ilType))
// Get all the composite methods for a group Types.Add(type.ilType, type.referenceType, new AppType(type.ilType, type.referenceType, type.valueType) {Group = Group});
public IEnumerable<AppMethod> GetMethodGroup(string groupName) => Methods.Values.Where(m => m.Group == groupName); }
// Static analysis tools // Get all the C++ types for a group
public IEnumerable<CppType> GetCppTypeGroup(string groupName) => CppTypeCollection.GetTypeGroup(groupName);
// Get the address map for the model public IEnumerable<CppType> GetDependencyOrderedCppTypeGroup(string groupName) => DependencyOrderedCppTypes.Where(t => t.Group == groupName);
// This takes a while to construct so we only build it if requested
private AddressMap addressMap; // Get all the composite types for a group
public AddressMap GetAddressMap() { public IEnumerable<AppType> GetTypeGroup(string groupName) => Types.Values.Where(t => t.Group == groupName);
if (addressMap == null)
addressMap = new AddressMap(this); // Get all the composite methods for a group
return addressMap; public IEnumerable<AppMethod> GetMethodGroup(string groupName) => Methods.Values.Where(m => m.Group == groupName);
}
// Static analysis tools
// Get the byte offset in Il2CppClass for this app's Unity version to the vtable
public int GetVTableOffset() => CppTypeCollection.GetComplexType("Il2CppClass")["vtable"].OffsetBytes; // Get the address map for the model
// This takes a while to construct so we only build it if requested
// Get the vtable method index from an offset from the start of the Il2CppClass private AddressMap addressMap;
// Unity 5.3.0-5.3.5 uses MethodInfo** - a pointer to a list of MethodInfo pointers public AddressMap GetAddressMap() {
// Unity 5.3.6-5.4.6 uses VirtualInvokeData* - a pointer to an array of VirtualInvokeData if (addressMap == null)
// Unity 5.5.0 onwards moves the VirtualInvokeData to the end of Il2CppClass and makes it an array addressMap = new AddressMap(this);
// We only include support for Unity 5.5.0 onwards return addressMap;
public int GetVTableIndexFromClassOffset(int offset) { }
if (UnityVersion.CompareTo("5.5.0") < 0)
throw new NotImplementedException("VTable index resolution is only supported for Unity 5.5.0 and later"); // Get the byte offset in Il2CppClass for this app's Unity version to the vtable
public int GetVTableOffset() => CppTypeCollection.GetComplexType("Il2CppClass")["vtable"].OffsetBytes;
// VirtualInvokeData has two members. The first is the jump target.
// Il2CppMethodPointer methodPtr; // Get the vtable method index from an offset from the start of the Il2CppClass
// const MethodInfo* method; // Unity 5.3.0-5.3.5 uses MethodInfo** - a pointer to a list of MethodInfo pointers
var offsetIntoVTable = offset - GetVTableOffset(); // Unity 5.3.6-5.4.6 uses VirtualInvokeData* - a pointer to an array of VirtualInvokeData
var vidSize = WordSizeBits == 32? 8 : 16; // Unity 5.5.0 onwards moves the VirtualInvokeData to the end of Il2CppClass and makes it an array
return offsetIntoVTable / vidSize; // We only include support for Unity 5.5.0 onwards
} public int GetVTableIndexFromClassOffset(int offset) {
} if (UnityVersion.CompareTo("5.5.0") < 0)
} throw new NotImplementedException("VTable index resolution is only supported for Unity 5.5.0 and later");
// VirtualInvokeData has two members. The first is the jump target.
// Il2CppMethodPointer methodPtr;
// const MethodInfo* method;
var offsetIntoVTable = offset - GetVTableOffset();
var vidSize = WordSizeBits == 32? 8 : 16;
return offsetIntoVTable / vidSize;
}
}
}

View File

@@ -36,8 +36,18 @@ namespace Il2CppInspector.Reflection
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 => TypesByDefinitionIndex.Concat(TypesByReferenceIndex) public IEnumerable<TypeInfo> Types
.Concat(GenericMethods.Values.Select(m => m.DeclaringType)).Distinct().Where(t => t != null); {
get
{
types ??= TypesByDefinitionIndex.Concat(TypesByReferenceIndex)
.Concat(GenericMethods.Values.Select(m => m.DeclaringType)).Distinct().Where(t => t != null).ToList();
return 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; }