diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs index fd9866f..bb4a2c1 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppInspector.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using Il2CppInspector.Utils; using NoisyCowStudios.Bin2Object; using System; using System.Collections.Generic; @@ -184,70 +185,30 @@ namespace Il2CppInspector var encodedToken = (uint)metadataValue; var usage = MetadataUsage.FromEncodedIndex(this, encodedToken); - if (usage.Type > 0 - && usage.Type <= MetadataUsageType.MethodRef - && metadataValue == (((uint)usage.Type << 29) | ((uint)usage.SourceIndex << 1)) + 1 + if (CheckMetadataUsageSanity(usage) && BinaryImage.TryMapFileOffsetToVA(i * ((uint)BinaryImage.Bits / 8), out var va)) { - usages.Add(MetadataUsage.FromEncodedIndex(this, encodedToken, va)); + usage.SetAddress(va); + usages.Add(usage); } } } return usages; - /*BinaryImage.Position = 0; - var sequenceLength = 0; - var threshold = 6000; // current versions of mscorlib generate about 6000-7000 metadata usages - var usagesCount = 0; - - var words = BinaryImage.ReadArray(0, (int) BinaryImage.Length / (BinaryImage.Bits / 8)); - - // Scan the image looking for a sequential block of at least 'threshold' valid metadata tokens - int pos; - for (pos = 0; pos < words.Length && (usagesCount == 0 || sequenceLength > 0); pos++) { - var word = words[pos]; - - if (word % 2 != 1 || word >> 32 != 0) { - sequenceLength = 0; - continue; + bool CheckMetadataUsageSanity(MetadataUsage usage) + { + return usage.Type switch + { + MetadataUsageType.TypeInfo or MetadataUsageType.Type => TypeReferences.Count > usage.SourceIndex, + MetadataUsageType.MethodDef => Methods.Length > usage.SourceIndex, + MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => FieldRefs.Length > usage.SourceIndex, + MetadataUsageType.StringLiteral => StringLiterals.Length > usage.SourceIndex, + MetadataUsageType.MethodRef => MethodSpecs.Length > usage.SourceIndex, + _ => false, + }; } - - var potentialUsage = MetadataUsage.FromEncodedIndex(this, (uint) word); - switch (potentialUsage.Type) { - case MetadataUsageType.Type: - case MetadataUsageType.TypeInfo: - case MetadataUsageType.MethodDef: - case MetadataUsageType.MethodRef: - case MetadataUsageType.FieldInfo: - case MetadataUsageType.StringLiteral: - sequenceLength++; - - if (sequenceLength >= threshold) - usagesCount = sequenceLength; - break; - default: - sequenceLength = 0; - break; } - } - - // If we found a block, read all the tokens and map them with their VAs to MetadataUsage objects - if (usagesCount > 0) { - var wordSize = BinaryImage.Bits / 8; - var pMetadataUsages = (uint) (pos * wordSize - (usagesCount + 1) * wordSize); - var pMetadataUsagesVA = BinaryImage.MapFileOffsetToVA(pMetadataUsages); - var usageTokens = BinaryImage.ReadWordArray(pMetadataUsages, usagesCount); - var usages = usageTokens.Zip(Enumerable.Range(0, usagesCount) - .Select(a => pMetadataUsagesVA + (ulong) (a * wordSize)), (t, a) => MetadataUsage.FromEncodedIndex(this, (uint) t, a)); - - Console.WriteLine("Late binding metadata usage block found successfully for metadata v27"); - return usages.ToList(); - } - - Console.WriteLine("Late binding metadata usage block could not be auto-detected - metadata usage references will not be available for this project"); - return null;*/ - } // Thumb instruction pointers have the bottom bit set to signify a switch from ARM to Thumb when jumping private ulong getDecodedAddress(ulong addr) { diff --git a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs index 90c62e4..f30df66 100644 --- a/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs +++ b/Il2CppInspector.Common/IL2CPP/MetadataUsage.cs @@ -1,56 +1,57 @@ -/* - Copyright (c) 2019-2020 Carter Bush - https://github.com/carterbush - Copyright (c) 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty - Copyright 2020 Robert Xiao - https://robertxiao.ca - - All rights reserved. -*/ - -namespace Il2CppInspector -{ - public enum MetadataUsageType - { - TypeInfo = 1, - Type = 2, - MethodDef = 3, - FieldInfo = 4, - StringLiteral = 5, - MethodRef = 6, - } - - public class MetadataUsage - { - public MetadataUsageType Type { get; } - public int SourceIndex { get; } - public ulong VirtualAddress { get; private set; } - - public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddress = 0) { - Type = type; - SourceIndex = sourceIndex; - VirtualAddress = virtualAddress; - } - - public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encodedIndex, ulong virtualAddress = 0) { - uint index; - MetadataUsageType usageType; - if (package.Version < 19) { - /* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */ - var isGeneric = encodedIndex & 0x80000000; - index = package.Binary.VTableMethodReferences[encodedIndex & 0x7FFFFFFF]; - usageType = (isGeneric != 0) ? MetadataUsageType.MethodRef : MetadataUsageType.MethodDef; - } else { - /* These encoded indices appear in metadata usages, and are decoded by GetEncodedIndexType/GetDecodedMethodIndex */ - var encodedType = encodedIndex & 0xE0000000; - usageType = (MetadataUsageType)(encodedType >> 29); - index = encodedIndex & 0x1FFFFFFF; - - // From v27 the bottom bit is set to indicate the usage token hasn't been replaced with a pointer at runtime yet - if (package.Version >= 27) - index >>= 1; - } - return new MetadataUsage(usageType, (int)index, virtualAddress); - } - - public void SetAddress(ulong virtualAddress) => VirtualAddress = virtualAddress; - } +/* + Copyright (c) 2019-2020 Carter Bush - https://github.com/carterbush + Copyright (c) 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + Copyright 2020 Robert Xiao - https://robertxiao.ca + + All rights reserved. +*/ + +namespace Il2CppInspector +{ + public enum MetadataUsageType + { + TypeInfo = 1, + Type = 2, + MethodDef = 3, + FieldInfo = 4, + StringLiteral = 5, + MethodRef = 6, + FieldRva = 7 + } + + public class MetadataUsage + { + public MetadataUsageType Type { get; } + public int SourceIndex { get; } + public ulong VirtualAddress { get; private set; } + + public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddress = 0) { + Type = type; + SourceIndex = sourceIndex; + VirtualAddress = virtualAddress; + } + + public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encodedIndex, ulong virtualAddress = 0) { + uint index; + MetadataUsageType usageType; + if (package.Version < 19) { + /* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */ + var isGeneric = encodedIndex & 0x80000000; + index = package.Binary.VTableMethodReferences[encodedIndex & 0x7FFFFFFF]; + usageType = (isGeneric != 0) ? MetadataUsageType.MethodRef : MetadataUsageType.MethodDef; + } else { + /* These encoded indices appear in metadata usages, and are decoded by GetEncodedIndexType/GetDecodedMethodIndex */ + var encodedType = encodedIndex & 0xE0000000; + usageType = (MetadataUsageType)(encodedType >> 29); + index = encodedIndex & 0x1FFFFFFF; + + // From v27 the bottom bit is set to indicate the usage token hasn't been replaced with a pointer at runtime yet + if (package.Version >= 27) + index >>= 1; + } + return new MetadataUsage(usageType, (int)index, virtualAddress); + } + + public void SetAddress(ulong virtualAddress) => VirtualAddress = virtualAddress; + } } \ No newline at end of file diff --git a/Il2CppInspector.Common/Model/AppModel.cs b/Il2CppInspector.Common/Model/AppModel.cs index 83db2c8..c32609c 100644 --- a/Il2CppInspector.Common/Model/AppModel.cs +++ b/Il2CppInspector.Common/Model/AppModel.cs @@ -210,38 +210,28 @@ namespace Il2CppInspector.Model switch (usage.Type) { case MetadataUsageType.StringLiteral: - //if (usage.SourceIndex >= TypeModel.Package.Metadata.StringLiterals.Length) - // break; - var str = TypeModel.GetMetadataUsageName(usage); Strings.Add(address, str); break; - case MetadataUsageType.Type: - case MetadataUsageType.TypeInfo: - //if (usage.SourceIndex >= TypeModel.TypesByReferenceIndex.Length) - // break; - + case MetadataUsageType.Type or MetadataUsageType.TypeInfo: var type = TypeModel.GetMetadataUsageType(usage); declarationGenerator.IncludeType(type); AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); + if (!Types.ContainsKey(type)) + // Generic type definition has no associated C++ type, therefore no dictionary sub-key + Types.Add(type, new AppType(type, null) { Group = Group }); + if (usage.Type == MetadataUsageType.TypeInfo) // Regular type definition Types[type].TypeClassAddress = address; - - else if (!Types.ContainsKey(type)) - // Generic type definition has no associated C++ type, therefore no dictionary sub-key - Types.Add(type, new AppType(type, null, cppTypeRefPtr: address) { Group = Group }); else // Regular type reference Types[type].TypeRefPtrAddress = address; - break; - case MetadataUsageType.MethodDef: - case MetadataUsageType.MethodRef: - //if (usage.SourceIndex > TypeModel.Package.Metadata.Methods.Length) - // break; + break; + case MetadataUsageType.MethodDef or MetadataUsageType.MethodRef: var method = TypeModel.GetMetadataUsageMethod(usage); declarationGenerator.IncludeMethod(method); AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); @@ -256,14 +246,18 @@ namespace Il2CppInspector.Model Methods[method].MethodInfoPtrAddress = address; break; - case MetadataUsageType.FieldInfo: - if (usage.SourceIndex > TypeModel.Package.Metadata.FieldRefs.Length) - break; - + case MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva: var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex]; var fieldType = TypeModel.GetMetadataUsageType(usage); var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex); - Fields.Add(usage.VirtualAddress, $"{fieldType.Name}.{field.Name}".ToCIdentifier()); + + if (usage.Type == MetadataUsageType.FieldInfo) + Fields.Add(usage.VirtualAddress, $"{fieldType.Name}.{field.Name}".ToCIdentifier()); + else + { + var defaultValue = Package.FieldDefaultValue[field.Index]; + // TODO: Unsure what it could be used for here. Maybe PID array initializers? + } break; } } diff --git a/Il2CppInspector.Common/Reflection/TypeModel.cs b/Il2CppInspector.Common/Reflection/TypeModel.cs index 6f460ff..40d7847 100644 --- a/Il2CppInspector.Common/Reflection/TypeModel.cs +++ b/Il2CppInspector.Common/Reflection/TypeModel.cs @@ -344,18 +344,21 @@ namespace Il2CppInspector.Reflection type = GetMetadataUsageType(usage); method = GetMetadataUsageMethod(usage); return $"{type.Name}.{method.Name}"; + + case MetadataUsageType.FieldRva: + fieldRef = Package.FieldRefs[usage.SourceIndex]; + type = GetMetadataUsageType(usage); + field = type.DeclaredFields.First(f => f.Index == type.Definition.fieldStart + fieldRef.fieldIndex); + return $"{type.Name}.{field.Name}_Default"; // TODO: Find out if this is really needed for anything } throw new NotImplementedException("Unknown metadata usage type: " + usage.Type); } // Get the type used in a metadata usage public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch { - MetadataUsageType.Type => TypesByReferenceIndex[usage.SourceIndex], - MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex], - MetadataUsageType.MethodDef => GetMetadataUsageMethod(usage).DeclaringType, - MetadataUsageType.FieldInfo => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex], - MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType, - + MetadataUsageType.Type or MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex], + MetadataUsageType.MethodDef or MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType, + MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex], _ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type") };