From 3560661f9c1e758b66713c0c5b2c807176e73ecb Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Thu, 31 Oct 2019 17:40:33 +0100 Subject: [PATCH] Model: Re-implement the handling of Il2CppType --- Il2CppInspector/Il2CppModel.cs | 58 +++++++++++++++---- Il2CppInspector/Reflection/ConstructorInfo.cs | 1 - Il2CppInspector/Reflection/EventInfo.cs | 8 ++- Il2CppInspector/Reflection/FieldInfo.cs | 8 ++- Il2CppInspector/Reflection/MemberInfo.cs | 2 +- Il2CppInspector/Reflection/MethodInfo.cs | 6 +- Il2CppInspector/Reflection/ParameterInfo.cs | 9 +-- Il2CppInspector/Reflection/TypeInfo.cs | 39 ++++++------- 8 files changed, 85 insertions(+), 46 deletions(-) diff --git a/Il2CppInspector/Il2CppModel.cs b/Il2CppInspector/Il2CppModel.cs index bb1c154..495cdb9 100644 --- a/Il2CppInspector/Il2CppModel.cs +++ b/Il2CppInspector/Il2CppModel.cs @@ -16,25 +16,32 @@ namespace Il2CppInspector.Reflection public List Assemblies { get; } = new List(); // List of all types ordered by their TypeDefinitionIndex - public TypeInfo[] TypesByIndex { get; } + public TypeInfo[] TypesByDefinitionIndex { get; } + + // List of all type usages ordered by their type usage index + public TypeInfo[] TypesByUsageIndex { get; } + + // List of type usages that are initialized via pointers in the image + public Dictionary TypesByVirtualAddress { get; } = new Dictionary(); + + // List of all types public Il2CppModel(Il2CppInspector package) { Package = package; - TypesByIndex = new TypeInfo[package.TypeDefinitions.Length]; + TypesByDefinitionIndex = new TypeInfo[package.TypeDefinitions.Length]; + TypesByUsageIndex = new TypeInfo[package.TypeUsages.Count]; // Create Assembly objects from Il2Cpp package for (var image = 0; image < package.Images.Length; image++) Assemblies.Add(new Assembly(this, image)); } - // Get or generate a type from its IL2CPP binary type usage reference - // (field, return type, generic type parameter etc.) - public TypeInfo GetType(Il2CppType pType, MemberTypes memberType = MemberTypes.All) { - switch (pType.type) { + private TypeInfo getNewTypeUsage(Il2CppType usage, MemberTypes memberType) { + switch (usage.type) { case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: // Classes defined in the metadata - return TypesByIndex[(int) pType.datapoint]; + return TypesByDefinitionIndex[usage.datapoint]; case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: @@ -43,20 +50,49 @@ namespace Il2CppInspector.Reflection case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: // Everything that requires special handling - return new TypeInfo(this, pType, memberType); + return new TypeInfo(this, usage, memberType); default: - return GetTypeFromEnum(pType.type); + // Primitive types + return GetTypeFromTypeEnum(usage.type); } } + // Get or generate a type from its IL2CPP binary type usage reference + // (field, return type, generic type parameter etc.) + public TypeInfo GetTypeFromUsage(int typeUsageIndex, MemberTypes memberType = MemberTypes.All) { + + // Already generated type previously? + if (TypesByUsageIndex[typeUsageIndex] != null) + return TypesByUsageIndex[typeUsageIndex]; + + var usage = Package.TypeUsages[typeUsageIndex]; + var newUsage = getNewTypeUsage(usage, memberType); + + TypesByUsageIndex[typeUsageIndex] = newUsage; + return newUsage; + } + // Basic primitive types - public TypeInfo GetTypeFromEnum(Il2CppTypeEnum t) { + public TypeInfo GetTypeFromTypeEnum(Il2CppTypeEnum t) { if ((int)t >= Il2CppConstants.FullNameTypeString.Count) return null; var fqn = Il2CppConstants.FullNameTypeString[(int) t]; - return TypesByIndex.First(x => x.FullName == fqn); + return TypesByDefinitionIndex.First(x => x.FullName == fqn); + } + + // Type from a virtual address pointer + // These are always nested types frorm usages within another type + public TypeInfo GetTypeFromVirtualAddress(ulong ptr) { + if (TypesByVirtualAddress.ContainsKey(ptr)) + return TypesByVirtualAddress[ptr]; + + var type = Package.BinaryImage.ReadMappedObject(ptr); + var newUsage = getNewTypeUsage(type, MemberTypes.NestedType); + + TypesByVirtualAddress.Add(ptr, newUsage); + return newUsage; } } } \ No newline at end of file diff --git a/Il2CppInspector/Reflection/ConstructorInfo.cs b/Il2CppInspector/Reflection/ConstructorInfo.cs index 66ce82b..771f5d5 100644 --- a/Il2CppInspector/Reflection/ConstructorInfo.cs +++ b/Il2CppInspector/Reflection/ConstructorInfo.cs @@ -16,7 +16,6 @@ namespace Il2CppInspector.Reflection public static readonly string TypeConstructorName = ".cctor"; - // TODO public override MemberTypes MemberType => MemberTypes.Constructor; public ConstructorInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) { } diff --git a/Il2CppInspector/Reflection/EventInfo.cs b/Il2CppInspector/Reflection/EventInfo.cs index fbda0eb..bcdb3a4 100644 --- a/Il2CppInspector/Reflection/EventInfo.cs +++ b/Il2CppInspector/Reflection/EventInfo.cs @@ -24,8 +24,8 @@ namespace Il2CppInspector.Reflection public MethodInfo RaiseMethod { get; } // Event handler delegate type - private Il2CppType eventType; - public TypeInfo EventHandlerType => Assembly.Model.GetType(eventType, MemberTypes.TypeInfo); + private int eventTypeUsage; + public TypeInfo EventHandlerType => Assembly.Model.GetTypeFromUsage(eventTypeUsage, MemberTypes.TypeInfo); // True if the event has a special name public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) == EventAttributes.SpecialName; @@ -38,7 +38,9 @@ namespace Il2CppInspector.Reflection Index = eventIndex; Name = pkg.Strings[Definition.nameIndex]; - eventType = pkg.TypeUsages[Definition.typeIndex]; + eventTypeUsage = Definition.typeIndex; + var eventType = pkg.TypeUsages[eventTypeUsage]; + if ((eventType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME) == Il2CppConstants.FIELD_ATTRIBUTE_SPECIAL_NAME) Attributes |= EventAttributes.SpecialName; diff --git a/Il2CppInspector/Reflection/FieldInfo.cs b/Il2CppInspector/Reflection/FieldInfo.cs index 8106ae0..58a2719 100644 --- a/Il2CppInspector/Reflection/FieldInfo.cs +++ b/Il2CppInspector/Reflection/FieldInfo.cs @@ -37,8 +37,8 @@ namespace Il2CppInspector.Reflection { public FieldAttributes Attributes { get; } // Type of field - private readonly Il2CppType fieldType; - public TypeInfo FieldType => Assembly.Model.GetType(fieldType, MemberTypes.Field); + private readonly int fieldTypeUsage; + public TypeInfo FieldType => Assembly.Model.GetTypeFromUsage(fieldTypeUsage, MemberTypes.Field); // For the Is* definitions below, see: // https://docs.microsoft.com/en-us/dotnet/api/system.reflection.fieldinfo.isfamilyandassembly?view=netframework-4.7.1#System_Reflection_FieldInfo_IsFamilyAndAssembly @@ -88,7 +88,9 @@ namespace Il2CppInspector.Reflection { Offset = pkg.FieldOffsets[fieldIndex]; Name = pkg.Strings[Definition.nameIndex]; - fieldType = pkg.TypeUsages[Definition.typeIndex]; + fieldTypeUsage = Definition.typeIndex; + var fieldType = pkg.TypeUsages[fieldTypeUsage]; + if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PRIVATE) Attributes |= FieldAttributes.Private; if ((fieldType.attrs & Il2CppConstants.FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == Il2CppConstants.FIELD_ATTRIBUTE_PUBLIC) diff --git a/Il2CppInspector/Reflection/MemberInfo.cs b/Il2CppInspector/Reflection/MemberInfo.cs index 464b8db..6d071e4 100644 --- a/Il2CppInspector/Reflection/MemberInfo.cs +++ b/Il2CppInspector/Reflection/MemberInfo.cs @@ -19,7 +19,7 @@ namespace Il2CppInspector.Reflection { // Type that this type is declared in for nested types protected int declaringTypeDefinitionIndex { private get; set; } = -1; - public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByIndex[declaringTypeDefinitionIndex] : null; + public TypeInfo DeclaringType => declaringTypeDefinitionIndex != -1? Assembly.Model.TypesByDefinitionIndex[declaringTypeDefinitionIndex] : null; // What sort of member this is, eg. method, field etc. public abstract MemberTypes MemberType { get; } diff --git a/Il2CppInspector/Reflection/MethodInfo.cs b/Il2CppInspector/Reflection/MethodInfo.cs index 72b6a9f..d5c7fde 100644 --- a/Il2CppInspector/Reflection/MethodInfo.cs +++ b/Il2CppInspector/Reflection/MethodInfo.cs @@ -17,14 +17,14 @@ namespace Il2CppInspector.Reflection public ParameterInfo ReturnParameter { get; } // Return type of the method - private readonly Il2CppType returnType; - public TypeInfo ReturnType => Assembly.Model.GetType(returnType, MemberTypes.TypeInfo); + private readonly int returnTypeUsage; + public TypeInfo ReturnType => Assembly.Model.GetTypeFromUsage(returnTypeUsage, MemberTypes.TypeInfo); // TODO: ReturnTypeCustomAttributes public MethodInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) { // Add return parameter - returnType = pkg.TypeUsages[Definition.returnType]; + returnTypeUsage = Definition.returnType; ReturnParameter = new ParameterInfo(pkg, -1, this); } diff --git a/Il2CppInspector/Reflection/ParameterInfo.cs b/Il2CppInspector/Reflection/ParameterInfo.cs index bc36294..829ead4 100644 --- a/Il2CppInspector/Reflection/ParameterInfo.cs +++ b/Il2CppInspector/Reflection/ParameterInfo.cs @@ -34,8 +34,8 @@ namespace Il2CppInspector.Reflection public string Name { get; } // Type of this parameter - private readonly Il2CppType paramType; - public TypeInfo ParameterType => Member.Assembly.Model.GetType(paramType, MemberTypes.TypeInfo); + private readonly int paramTypeUsage; + public TypeInfo ParameterType => Member.Assembly.Model.GetTypeFromUsage(paramTypeUsage, MemberTypes.TypeInfo); // Zero-indexed position of the parameter in parameter list public int Position { get; } @@ -46,7 +46,7 @@ namespace Il2CppInspector.Reflection if (paramIndex == -1) { Position = -1; - paramType = pkg.TypeUsages[declaringMethod.Definition.returnType]; + paramTypeUsage = declaringMethod.Definition.returnType; Attributes |= ParameterAttributes.Retval; return; } @@ -54,7 +54,8 @@ namespace Il2CppInspector.Reflection var param = pkg.Params[paramIndex]; Name = pkg.Strings[param.nameIndex]; Position = paramIndex - declaringMethod.Definition.parameterStart; - paramType = pkg.TypeUsages[param.typeIndex]; + paramTypeUsage = param.typeIndex; + var paramType = pkg.TypeUsages[paramTypeUsage]; if ((paramType.attrs & Il2CppConstants.PARAM_ATTRIBUTE_OPTIONAL) != 0) Attributes |= ParameterAttributes.Optional; diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index 812e674..1aa9411 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -21,8 +21,8 @@ namespace Il2CppInspector.Reflection { public TypeAttributes Attributes { get; } // Type that this type inherits from - private Il2CppType baseType; - public TypeInfo BaseType => baseType != null? Assembly.Model.GetType(baseType, MemberTypes.TypeInfo) : null; + private int baseTypeUsage; + public TypeInfo BaseType => Assembly.Model.GetTypeFromUsage(baseTypeUsage, MemberTypes.TypeInfo); // True if the type contains unresolved generic type parameters public bool ContainsGenericParameters { get; } @@ -49,7 +49,7 @@ namespace Il2CppInspector.Reflection { public List DeclaredMethods { get; } = new List(); private int[] declaredNestedTypes; - public IEnumerable DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByIndex[x]); + public IEnumerable DeclaredNestedTypes => declaredNestedTypes.Select(x => Assembly.Model.TypesByDefinitionIndex[x]); public List DeclaredProperties { get; } = new List(); @@ -57,12 +57,12 @@ namespace Il2CppInspector.Reflection { public MethodBase DeclaringMethod => throw new NotImplementedException(); // Gets the type of the object encompassed or referred to by the current array, pointer or reference type - private Il2CppType enumElementType; + private int enumElementTypeUsage; private TypeInfo elementType; public TypeInfo ElementType { get { if (IsEnum && elementType == null) - elementType = Assembly.Model.GetType(enumElementType, MemberTypes.TypeInfo); + elementType = Assembly.Model.GetTypeFromUsage(enumElementTypeUsage, MemberTypes.TypeInfo); return elementType; } } @@ -82,8 +82,8 @@ namespace Il2CppInspector.Reflection { public bool HasElementType => ElementType != null; - private Il2CppType[] implementedInterfaces; - public IEnumerable ImplementedInterfaces => implementedInterfaces.Select(x => Assembly.Model.GetType(x, MemberTypes.TypeInfo)); + private int[] implementedInterfaceUsages; + public IEnumerable ImplementedInterfaces => implementedInterfaceUsages.Select(x => Assembly.Model.GetTypeFromUsage(x, MemberTypes.TypeInfo)); public bool IsAbstract => (Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract; public bool IsArray { get; } @@ -155,15 +155,16 @@ namespace Il2CppInspector.Reflection { Name = pkg.Strings[Definition.nameIndex]; if (Definition.parentIndex >= 0) - baseType = pkg.TypeUsages[Definition.parentIndex]; + baseTypeUsage = Definition.parentIndex; // Nested type? if (Definition.declaringTypeIndex >= 0) { declaringTypeDefinitionIndex = (int) pkg.TypeUsages[Definition.declaringTypeIndex].datapoint; + MemberType |= MemberTypes.NestedType; } // Add to global type definition list - Assembly.Model.TypesByIndex[Index] = this; + Assembly.Model.TypesByDefinitionIndex[Index] = this; if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_SERIALIZABLE) != 0) Attributes |= TypeAttributes.Serializable; @@ -196,16 +197,16 @@ namespace Il2CppInspector.Reflection { if ((Definition.flags & Il2CppConstants.TYPE_ATTRIBUTE_INTERFACE) != 0) Attributes |= TypeAttributes.Interface; - // Enumerations - bit 1 of bitfield indicates this (also the baseType will be System.Enum) + // Enumerations - bit 1 of bitfield indicates this (also the baseTypeUsage will be System.Enum) if (((Definition.bitfield >> 1) & 1) == 1) { IsEnum = true; - enumElementType = pkg.TypeUsages[Definition.elementTypeIndex]; + enumElementTypeUsage = Definition.elementTypeIndex; } // Add all implemented interfaces - implementedInterfaces = new Il2CppType[Definition.interfaces_count]; + implementedInterfaceUsages = new int[Definition.interfaces_count]; for (var i = 0; i < Definition.interfaces_count; i++) - implementedInterfaces[i] = pkg.TypeUsages[pkg.InterfaceUsageIndices[Definition.interfacesStart + i]]; + implementedInterfaceUsages[i] = pkg.InterfaceUsageIndices[Definition.interfacesStart + i]; // Add all nested types declaredNestedTypes = new int[Definition.nested_type_count]; @@ -246,7 +247,7 @@ namespace Il2CppInspector.Reflection { // Generic type unresolved and concrete instance types if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { var generic = image.ReadMappedObject(pType.datapoint); - var genericTypeDef = model.TypesByIndex[generic.typeDefinitionIndex]; + var genericTypeDef = model.TypesByDefinitionIndex[generic.typeDefinitionIndex]; Namespace = genericTypeDef.Namespace; Name = genericTypeDef.Name; @@ -261,10 +262,10 @@ namespace Il2CppInspector.Reflection { GenericTypeParameters = new List(); foreach (var pArg in genericTypeParameters) { - var argType = image.ReadMappedObject((ulong) pArg); + var argType = model.GetTypeFromVirtualAddress((ulong) pArg); // TODO: Detect whether unresolved or concrete (add concrete to GenericTypeArguments instead) // TODO: GenericParameterPosition etc. in types we generate here - GenericTypeParameters.Add(model.GetType(argType)); // TODO: Fix MemberType here + GenericTypeParameters.Add(argType); // TODO: Fix MemberType here } Attributes |= TypeAttributes.Class; } @@ -272,8 +273,7 @@ namespace Il2CppInspector.Reflection { // Array with known dimensions and bounds if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) { var descriptor = image.ReadMappedObject(pType.datapoint); - var elementType = image.ReadMappedObject(descriptor.etype); - this.elementType = model.GetType(elementType); + elementType = model.GetTypeFromVirtualAddress(descriptor.etype); Namespace = ElementType.Namespace; Name = ElementType.Name; @@ -283,8 +283,7 @@ namespace Il2CppInspector.Reflection { // Dynamically allocated array if (pType.type == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { - var elementType = image.ReadMappedObject(pType.datapoint); - this.elementType = model.GetType(elementType); + elementType = model.GetTypeFromVirtualAddress(pType.datapoint); Namespace = ElementType.Namespace; Name = ElementType.Name;