From 896b3b6f69e8070875b0ff665a94ddfc97d251ed Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Mon, 3 Feb 2020 10:06:53 +0100 Subject: [PATCH] Model: Implement MethodInvoker and MethodBase.Invoker --- Il2CppInspector/Reflection/Il2CppModel.cs | 15 +++++ Il2CppInspector/Reflection/MethodBase.cs | 3 + Il2CppInspector/Reflection/MethodInvoker.cs | 64 +++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 Il2CppInspector/Reflection/MethodInvoker.cs diff --git a/Il2CppInspector/Reflection/Il2CppModel.cs b/Il2CppInspector/Reflection/Il2CppModel.cs index d4d0ec8..bf9e2b8 100644 --- a/Il2CppInspector/Reflection/Il2CppModel.cs +++ b/Il2CppInspector/Reflection/Il2CppModel.cs @@ -38,6 +38,9 @@ namespace Il2CppInspector.Reflection // List of all methods ordered by their MethodDefinitionIndex public MethodBase[] MethodsByDefinitionIndex { get; } + // List of all Method.Invoke functions by invoker index + public MethodInvoker[] MethodInvokers { get; } + // List of all generated CustomAttributeData objects by their instanceIndex into AttributeTypeIndices public ConcurrentDictionary AttributesByIndices { get; } = new ConcurrentDictionary(); @@ -60,6 +63,7 @@ namespace Il2CppInspector.Reflection TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; TypesByReferenceIndex = new TypeInfo[package.TypeReferences.Count]; MethodsByDefinitionIndex = new MethodBase[package.Methods.Length]; + MethodInvokers = new MethodInvoker[package.MethodInvokePointers.Length]; // Recursively create hierarchy of assemblies and types from TypeDefs // No code that executes here can access any type through a TypeRef (ie. via TypesByReferenceIndex) @@ -100,6 +104,17 @@ namespace Il2CppInspector.Reflection GenericMethods.Add(spec, concreteMethod); } } + + // Create method invokers (one per signature, in invoker index order) + foreach (var method in MethodsByDefinitionIndex) { + var index = package.GetInvokerIndex(method.DeclaringType.Assembly.ModuleDefinition, method.Definition); + if (index != -1) { + if (MethodInvokers[index] == null) + MethodInvokers[index] = new MethodInvoker(method); + + method.Invoker = MethodInvokers[index]; + } + } } // Get generic arguments from either a type or method instanceIndex from a MethodSpec diff --git a/Il2CppInspector/Reflection/MethodBase.cs b/Il2CppInspector/Reflection/MethodBase.cs index 05db6e2..2713df3 100644 --- a/Il2CppInspector/Reflection/MethodBase.cs +++ b/Il2CppInspector/Reflection/MethodBase.cs @@ -19,6 +19,9 @@ namespace Il2CppInspector.Reflection public int Index { get; } public (ulong Start, ulong End)? VirtualAddress { get; } + // Method.Invoke implementation + public MethodInvoker Invoker { get; set; } + // Information/flags about the method public MethodAttributes Attributes { get; protected set; } diff --git a/Il2CppInspector/Reflection/MethodInvoker.cs b/Il2CppInspector/Reflection/MethodInvoker.cs new file mode 100644 index 0000000..0ccba08 --- /dev/null +++ b/Il2CppInspector/Reflection/MethodInvoker.cs @@ -0,0 +1,64 @@ +/* + Copyright 2020 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com + + All rights reserved. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Il2CppInspector.Reflection +{ + // Class representing a MethodBase.Invoke() method for a specific signature + // Every IL2CPP invoker has the signature: + // void* RuntimeInvoker_{RequiresObject (True/False)}{ReturnType}_{ParameterTypes} + // (Il2CppMethodPointer pointer, const RuntimeMethod* methodMetadata, void* obj, void** args) + public class MethodInvoker + { + // IL2CPP invoker index + public int Index { get; } + + // Virtual address of the invoker function + public (ulong Start, ulong End) VirtualAddress { get; } + + // If false, the first argument to the called function pointers must be the object instance + public bool IsStatic { get; } + + // Return type + public TypeInfo ReturnType { get; } + + // Argument types + public TypeInfo[] ParameterTypes { get; } + + // Find the correct method invoker for a method with a specific signature + public MethodInvoker(MethodBase exampleMethod) { + var model = exampleMethod.Assembly.Model; + var package = exampleMethod.Assembly.Model.Package; + + Index = package.GetInvokerIndex(exampleMethod.DeclaringType.Assembly.ModuleDefinition, exampleMethod.Definition); + IsStatic = exampleMethod.IsStatic; + + ReturnType = exampleMethod.IsConstructor ? model.TypesByFullName["System.Void"] : mapParameterType(model, ((MethodInfo) exampleMethod).ReturnType); + ParameterTypes = exampleMethod.DeclaredParameters.Select(p => mapParameterType(model, p.ParameterType)).ToArray(); + + var start = package.MethodInvokePointers[Index]; + VirtualAddress = (start & 0xffff_ffff_ffff_fffe, package.FunctionAddresses[start]); + } + + // The invokers use Object for all reference types, and SByte for booleans + private TypeInfo mapParameterType(Il2CppModel model, TypeInfo type) => type switch { + { IsValueType: false } => model.TypesByFullName["System.Object"], + { FullName: "System.Boolean" } => model.TypesByFullName["System.SByte"], + _ => type + }; + + public string Name => $"RunTimeInvoker_{!IsStatic}{ReturnType.BaseName}_" + string.Join("_", ParameterTypes.Select(p => p.BaseName)); + + // Display as a C++ method signature + public string Signature => Name + "(Il2CppMethodPointer pointer, const RuntimeMethod* methodMetadata, void* obj, void** args)"; + + public override string ToString() => Signature; + } +}