cache tyoes
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
Reference in New Issue
Block a user