Add FieldRva MetadataUsage type and fix current heuristic

This commit is contained in:
LukeFZ
2023-11-30 05:15:01 +01:00
parent ab841ccb2b
commit a4528e5f55
4 changed files with 96 additions and 137 deletions

View File

@@ -4,6 +4,7 @@
All rights reserved. All rights reserved.
*/ */
using Il2CppInspector.Utils;
using NoisyCowStudios.Bin2Object; using NoisyCowStudios.Bin2Object;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -184,70 +185,30 @@ namespace Il2CppInspector
var encodedToken = (uint)metadataValue; var encodedToken = (uint)metadataValue;
var usage = MetadataUsage.FromEncodedIndex(this, encodedToken); var usage = MetadataUsage.FromEncodedIndex(this, encodedToken);
if (usage.Type > 0 if (CheckMetadataUsageSanity(usage)
&& usage.Type <= MetadataUsageType.MethodRef
&& metadataValue == (((uint)usage.Type << 29) | ((uint)usage.SourceIndex << 1)) + 1
&& BinaryImage.TryMapFileOffsetToVA(i * ((uint)BinaryImage.Bits / 8), out var va)) && 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; return usages;
/*BinaryImage.Position = 0; bool CheckMetadataUsageSanity(MetadataUsage usage)
var sequenceLength = 0; {
var threshold = 6000; // current versions of mscorlib generate about 6000-7000 metadata usages return usage.Type switch
var usagesCount = 0; {
MetadataUsageType.TypeInfo or MetadataUsageType.Type => TypeReferences.Count > usage.SourceIndex,
var words = BinaryImage.ReadArray<ulong>(0, (int) BinaryImage.Length / (BinaryImage.Bits / 8)); MetadataUsageType.MethodDef => Methods.Length > usage.SourceIndex,
MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => FieldRefs.Length > usage.SourceIndex,
// Scan the image looking for a sequential block of at least 'threshold' valid metadata tokens MetadataUsageType.StringLiteral => StringLiterals.Length > usage.SourceIndex,
int pos; MetadataUsageType.MethodRef => MethodSpecs.Length > usage.SourceIndex,
for (pos = 0; pos < words.Length && (usagesCount == 0 || sequenceLength > 0); pos++) { _ => false,
var word = words[pos]; };
if (word % 2 != 1 || word >> 32 != 0) {
sequenceLength = 0;
continue;
} }
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 // Thumb instruction pointers have the bottom bit set to signify a switch from ARM to Thumb when jumping
private ulong getDecodedAddress(ulong addr) { private ulong getDecodedAddress(ulong addr) {

View File

@@ -1,56 +1,57 @@
/* /*
Copyright (c) 2019-2020 Carter Bush - https://github.com/carterbush 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 (c) 2020-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
Copyright 2020 Robert Xiao - https://robertxiao.ca Copyright 2020 Robert Xiao - https://robertxiao.ca
All rights reserved. All rights reserved.
*/ */
namespace Il2CppInspector namespace Il2CppInspector
{ {
public enum MetadataUsageType public enum MetadataUsageType
{ {
TypeInfo = 1, TypeInfo = 1,
Type = 2, Type = 2,
MethodDef = 3, MethodDef = 3,
FieldInfo = 4, FieldInfo = 4,
StringLiteral = 5, StringLiteral = 5,
MethodRef = 6, MethodRef = 6,
} FieldRva = 7
}
public class MetadataUsage
{ public class MetadataUsage
public MetadataUsageType Type { get; } {
public int SourceIndex { get; } public MetadataUsageType Type { get; }
public ulong VirtualAddress { get; private set; } public int SourceIndex { get; }
public ulong VirtualAddress { get; private set; }
public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddress = 0) {
Type = type; public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddress = 0) {
SourceIndex = sourceIndex; Type = type;
VirtualAddress = virtualAddress; SourceIndex = sourceIndex;
} VirtualAddress = virtualAddress;
}
public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encodedIndex, ulong virtualAddress = 0) {
uint index; public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encodedIndex, ulong virtualAddress = 0) {
MetadataUsageType usageType; uint index;
if (package.Version < 19) { MetadataUsageType usageType;
/* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */ if (package.Version < 19) {
var isGeneric = encodedIndex & 0x80000000; /* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */
index = package.Binary.VTableMethodReferences[encodedIndex & 0x7FFFFFFF]; var isGeneric = encodedIndex & 0x80000000;
usageType = (isGeneric != 0) ? MetadataUsageType.MethodRef : MetadataUsageType.MethodDef; index = package.Binary.VTableMethodReferences[encodedIndex & 0x7FFFFFFF];
} else { usageType = (isGeneric != 0) ? MetadataUsageType.MethodRef : MetadataUsageType.MethodDef;
/* These encoded indices appear in metadata usages, and are decoded by GetEncodedIndexType/GetDecodedMethodIndex */ } else {
var encodedType = encodedIndex & 0xE0000000; /* These encoded indices appear in metadata usages, and are decoded by GetEncodedIndexType/GetDecodedMethodIndex */
usageType = (MetadataUsageType)(encodedType >> 29); var encodedType = encodedIndex & 0xE0000000;
index = encodedIndex & 0x1FFFFFFF; 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) // From v27 the bottom bit is set to indicate the usage token hasn't been replaced with a pointer at runtime yet
index >>= 1; if (package.Version >= 27)
} index >>= 1;
return new MetadataUsage(usageType, (int)index, virtualAddress); }
} return new MetadataUsage(usageType, (int)index, virtualAddress);
}
public void SetAddress(ulong virtualAddress) => VirtualAddress = virtualAddress;
} public void SetAddress(ulong virtualAddress) => VirtualAddress = virtualAddress;
}
} }

View File

@@ -210,38 +210,28 @@ namespace Il2CppInspector.Model
switch (usage.Type) { switch (usage.Type) {
case MetadataUsageType.StringLiteral: case MetadataUsageType.StringLiteral:
//if (usage.SourceIndex >= TypeModel.Package.Metadata.StringLiterals.Length)
// break;
var str = TypeModel.GetMetadataUsageName(usage); var str = TypeModel.GetMetadataUsageName(usage);
Strings.Add(address, str); Strings.Add(address, str);
break; break;
case MetadataUsageType.Type: case MetadataUsageType.Type or MetadataUsageType.TypeInfo:
case MetadataUsageType.TypeInfo:
//if (usage.SourceIndex >= TypeModel.TypesByReferenceIndex.Length)
// break;
var type = TypeModel.GetMetadataUsageType(usage); var type = TypeModel.GetMetadataUsageType(usage);
declarationGenerator.IncludeType(type); declarationGenerator.IncludeType(type);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); 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) if (usage.Type == MetadataUsageType.TypeInfo)
// Regular type definition // Regular type definition
Types[type].TypeClassAddress = address; 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 else
// Regular type reference // Regular type reference
Types[type].TypeRefPtrAddress = address; 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); var method = TypeModel.GetMetadataUsageMethod(usage);
declarationGenerator.IncludeMethod(method); declarationGenerator.IncludeMethod(method);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations()); AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
@@ -256,14 +246,18 @@ namespace Il2CppInspector.Model
Methods[method].MethodInfoPtrAddress = address; Methods[method].MethodInfoPtrAddress = address;
break; break;
case MetadataUsageType.FieldInfo: case MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva:
if (usage.SourceIndex > TypeModel.Package.Metadata.FieldRefs.Length)
break;
var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex]; var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex];
var fieldType = TypeModel.GetMetadataUsageType(usage); var fieldType = TypeModel.GetMetadataUsageType(usage);
var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex); 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; break;
} }
} }

View File

@@ -344,18 +344,21 @@ namespace Il2CppInspector.Reflection
type = GetMetadataUsageType(usage); type = GetMetadataUsageType(usage);
method = GetMetadataUsageMethod(usage); method = GetMetadataUsageMethod(usage);
return $"{type.Name}.{method.Name}"; 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); throw new NotImplementedException("Unknown metadata usage type: " + usage.Type);
} }
// Get the type used in a metadata usage // Get the type used in a metadata usage
public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch { public TypeInfo GetMetadataUsageType(MetadataUsage usage) => usage.Type switch {
MetadataUsageType.Type => TypesByReferenceIndex[usage.SourceIndex], MetadataUsageType.Type or MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex],
MetadataUsageType.TypeInfo => TypesByReferenceIndex[usage.SourceIndex], MetadataUsageType.MethodDef or MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType,
MetadataUsageType.MethodDef => GetMetadataUsageMethod(usage).DeclaringType, MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex],
MetadataUsageType.FieldInfo => TypesByReferenceIndex[Package.FieldRefs[usage.SourceIndex].typeIndex],
MetadataUsageType.MethodRef => GetMetadataUsageMethod(usage).DeclaringType,
_ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type") _ => throw new InvalidOperationException("Incorrect metadata usage type to retrieve referenced type")
}; };