From 6d7c0483599246e4b9f7e41739eb1cbddb070733 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 27 Oct 2021 01:07:40 +0100 Subject: [PATCH] Finish fully implementing v29 restoration --- Cpp2IL.Core/AssemblyPopulator.cs | 1 + Cpp2IL.Core/AttributeRestorerPost29.cs | 128 ++++++++++++++++-- Cpp2IL.Core/Cpp2IlApi.cs | 5 +- Cpp2IL.Core/Extensions.cs | 18 +++ Cpp2IL.Core/SharedState.cs | 35 ++--- Cpp2IL/Program.cs | 2 +- .../Metadata/Il2CppPropertyDefinition.cs | 2 + LibCpp2IL/Reflection/LibCpp2IlReflection.cs | 24 ++++ 8 files changed, 181 insertions(+), 34 deletions(-) diff --git a/Cpp2IL.Core/AssemblyPopulator.cs b/Cpp2IL.Core/AssemblyPopulator.cs index 07b9f46c..eefad2b2 100644 --- a/Cpp2IL.Core/AssemblyPopulator.cs +++ b/Cpp2IL.Core/AssemblyPopulator.cs @@ -434,6 +434,7 @@ private static void ProcessPropertiesInType(Il2CppTypeDefinition cppTypeDefiniti ilTypeDefinition.Properties.Add(propertyDefinition); SharedState.UnmanagedToManagedProperties[propertyDef] = propertyDefinition; + SharedState.ManagedToUnmanagedProperties[propertyDefinition] = propertyDef; } } diff --git a/Cpp2IL.Core/AttributeRestorerPost29.cs b/Cpp2IL.Core/AttributeRestorerPost29.cs index 2319966a..791b66ec 100644 --- a/Cpp2IL.Core/AttributeRestorerPost29.cs +++ b/Cpp2IL.Core/AttributeRestorerPost29.cs @@ -25,14 +25,47 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, Type var typeDef = SharedState.ManagedToUnmanagedTypes[typeDefinition]; //Apply custom attributes to type itself - GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, typeDef.customAttributeIndex, typeDef.token, typeDef.FullName!) + GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, typeDef.token) .ForEach(attribute => typeDefinition.CustomAttributes.Add(attribute)); + + //Apply custom attributes to fields + foreach (var fieldDef in typeDef.Fields!) + { + var fieldDefinition = SharedState.UnmanagedToManagedFields[fieldDef]; + + GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, fieldDef.token) + .ForEach(attribute => fieldDefinition.CustomAttributes.Add(attribute)); + } + + //Apply custom attributes to methods + foreach (var methodDef in typeDef.Methods!) + { + var methodDefinition = methodDef.AsManaged(); + + GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, methodDef.token) + .ForEach(attribute => methodDefinition.CustomAttributes.Add(attribute)); + } + + //Apply custom attributes to properties + foreach (var propertyDef in typeDef.Properties!) + { + var propertyDefinition = SharedState.UnmanagedToManagedProperties[propertyDef]; + + GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, propertyDef.token) + .ForEach(attribute => propertyDefinition.CustomAttributes.Add(attribute)); + } + + //Nested Types + foreach (var nestedType in typeDefinition.NestedTypes) + { + RestoreAttributesInType(imageDef, nestedType); + } } - public static List GetCustomAttributesByAttributeIndex(Il2CppImageDefinition imageDef, ModuleDefinition moduleDefinition, int attributeIndex, uint token, string warningName) + public static List GetCustomAttributesByAttributeIndex(Il2CppImageDefinition imageDef, ModuleDefinition moduleDefinition, uint token) { var ret = new List(); - + //Search attribute data ranges for one with matching token var target = new Il2CppCustomAttributeDataRange() { token = token }; var customAttributeIndex = LibCpp2IlMain.TheMetadata!.AttributeDataRanges.BinarySearch(imageDef.customAttributeStart, (int)imageDef.customAttributeCount, target, new TokenComparer()); @@ -47,7 +80,7 @@ public static List GetCustomAttributesByAttributeIndex(Il2CppIm var end = LibCpp2IlMain.TheMetadata.metadataHeader.attributeDataOffset + next.startOffset; //Now we start actually reading. Start is a pointer to the address in the metadata file where the attribute data is. - + //Read attribute count as compressed uint var attributeCount = LibCpp2IlMain.TheMetadata.ReadUnityCompressedUIntAtRawAddr(start, out var countBytes); @@ -104,10 +137,9 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod bytesRead += ctorArgBytesRead; pos += ctorArgBytesRead; - if (typeReference.IsArray) + if (typeReference.IsArray && val is Array arr) { - Logger.WarnNewline($"Failed to parse custom attribute {constructor.DeclaringType!.FullName} due to an array type"); - return MakeFallbackAttribute(module, constructor.AsManaged()); + val = (from object? o in arr select new CustomAttributeArgument(Utils.TryLookupTypeDefKnownNotGeneric(o.GetType().FullName), o)).ToArray(); } ret.ConstructorArguments.Add(new CustomAttributeArgument(typeReference, val)); @@ -119,6 +151,35 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod var val = ReadBlob(pos, out var fieldBytesRead, out var typeReference); bytesRead += fieldBytesRead; pos += fieldBytesRead; + + FieldDefinition field; + + var fieldIndex = LibCpp2IlMain.TheMetadata.ReadUnityCompressedIntAtRawAddr(pos, out var fieldIdxBytesRead); + bytesRead += fieldIdxBytesRead; + pos += fieldIdxBytesRead; + + if (fieldIndex < 0) + { + var typeIndex = LibCpp2IlMain.TheMetadata.ReadUnityCompressedUIntAtRawAddr(pos, out var typeIdxBytesRead); + bytesRead += typeIdxBytesRead; + pos += typeIdxBytesRead; + + fieldIndex = -(fieldIndex + 1); + + var declaringType = LibCpp2IlMain.TheMetadata.typeDefs[typeIndex]; + field = declaringType!.Fields![fieldIndex].AsManaged(); + } + else + { + field = constructor.DeclaringType!.Fields![fieldIndex].AsManaged(); + } + + if (typeReference.IsArray && val is Array arr) + { + val = (from object? o in arr select new CustomAttributeArgument(Utils.TryLookupTypeDefKnownNotGeneric(o.GetType().FullName), o)).ToArray(); + } + + ret.Fields.Add(new(field.Name, new(typeReference, val))); } //Read n props @@ -127,6 +188,35 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod var val = ReadBlob(pos, out var propBytesRead, out var typeReference); bytesRead += propBytesRead; pos += propBytesRead; + + PropertyDefinition prop; + + var propIndex = LibCpp2IlMain.TheMetadata.ReadUnityCompressedIntAtRawAddr(pos, out var propIdxBytesRead); + bytesRead += propIdxBytesRead; + pos += propIdxBytesRead; + + if (propIndex < 0) + { + var typeIndex = LibCpp2IlMain.TheMetadata.ReadUnityCompressedUIntAtRawAddr(pos, out var typeIdxBytesRead); + bytesRead += typeIdxBytesRead; + pos += typeIdxBytesRead; + + propIndex = -(propIndex + 1); + + var declaringType = LibCpp2IlMain.TheMetadata.typeDefs[typeIndex]; + prop = declaringType!.Properties![propIndex].AsManaged(); + } + else + { + prop = constructor.DeclaringType!.Properties![propIndex].AsManaged(); + } + + if (typeReference.IsArray && val is Array arr) + { + val = (from object? o in arr select new CustomAttributeArgument(Utils.TryLookupTypeDefKnownNotGeneric(o.GetType().FullName), o)).ToArray(); + } + + ret.Properties.Add(new(prop.Name, new(typeReference, val))); } return ret; @@ -161,7 +251,7 @@ private static CustomAttribute MakeFallbackAttribute(ModuleDefinition module, Me private static object? ReadBlob(long pos, out int bytesRead, out TypeReference typeReference) { bytesRead = 0; - + var type = ReadBlobType(pos, out var typeBytesRead, out typeReference); bytesRead += typeBytesRead; pos += typeBytesRead; @@ -236,9 +326,17 @@ private static CustomAttribute MakeFallbackAttribute(ModuleDefinition module, Me var strLength = md.ReadUnityCompressedIntAtRawAddr(pos, out var stringLenBytesRead); bytesRead += stringLenBytesRead; pos += stringLenBytesRead; - - ret = Encoding.UTF8.GetString(md.ReadByteArrayAtRawAddress(pos, strLength)); - bytesRead += strLength; + + if (strLength > 0) + { + ret = Encoding.UTF8.GetString(md.ReadByteArrayAtRawAddress(pos, strLength)); + bytesRead += strLength; + } + else + { + ret = null; + } + break; case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: var arrLength = md.ReadUnityCompressedIntAtRawAddr(pos, out var arrayLenBytesRead); @@ -288,7 +386,7 @@ private static CustomAttribute MakeFallbackAttribute(ModuleDefinition module, Me var arrElem = ReadBlobValue(pos, thisElementType, out var elemBytesRead, out _); bytesRead += elemBytesRead; pos += elemBytesRead; - + //Set in the array resultArray.SetValue(arrElem, i); } @@ -317,6 +415,7 @@ private static CustomAttribute MakeFallbackAttribute(ModuleDefinition module, Me if (ret == null) throw new($"Failed to resolve type reflection data for type index {typeIndex}"); } + break; default: throw new ArgumentOutOfRangeException(); @@ -331,7 +430,7 @@ private static Il2CppTypeEnum ReadBlobType(long pos, out int bytesRead, out Type var ret = (Il2CppTypeEnum)LibCpp2IlMain.TheMetadata!.ReadClassAtRawAddr(pos); pos += 1; bytesRead += 1; - + if (ret == Il2CppTypeEnum.IL2CPP_TYPE_ENUM) { var enumTypeIndex = LibCpp2IlMain.TheMetadata.ReadUnityCompressedIntAtRawAddr(pos, out var compressedBytesRead); @@ -341,7 +440,8 @@ private static Il2CppTypeEnum ReadBlobType(long pos, out int bytesRead, out Type var typeDef = LibCpp2IlReflection.GetTypeDefinitionByTypeIndex(enumTypeIndex)!; type = typeDef.AsManaged(); //Get enum type ret = LibCpp2IlMain.Binary!.GetType(typeDef.elementTypeIndex).type; //Get enum underlying type's type enum - } else if (ret == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) + } + else if (ret == Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { type = Utils.ArrayReference; } diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index 22cec8ca..77493ed8 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -261,11 +261,12 @@ public static void RunAttributeRestorationForAllAssemblies(BaseKeyFunctionAddres enumerable.Select(def => { - if(!parallel) + if(!parallel && LibCpp2IlMain.MetadataVersion < 29) Logger.Verbose($"Processing {def.Name.Name}..."); RunAttributeRestorationForAssembly(def, keyFunctionAddresses); - Logger.VerboseNewline($"Finished processing {def.Name.Name}"); + if(LibCpp2IlMain.MetadataVersion < 29) + Logger.VerboseNewline($"Finished processing {def.Name.Name}"); return true; }).ToList(); //Force full evaluation } diff --git a/Cpp2IL.Core/Extensions.cs b/Cpp2IL.Core/Extensions.cs index 409c0b26..3a5ee464 100644 --- a/Cpp2IL.Core/Extensions.cs +++ b/Cpp2IL.Core/Extensions.cs @@ -170,6 +170,24 @@ public static T[] SubArray(this T[] source, Range range) return SharedState.ManagedToUnmanagedTypes[managed]; } + + [return: NotNullIfNotNull("unmanaged")] + public static PropertyDefinition? AsManaged(this Il2CppPropertyDefinition? unmanaged) + { + if (unmanaged == null) + return null; + + return SharedState.UnmanagedToManagedProperties[unmanaged]; + } + + [return: NotNullIfNotNull("managed")] + public static Il2CppPropertyDefinition? AsUnmanaged(this PropertyDefinition? managed) + { + if (managed == null) + return null; + + return SharedState.ManagedToUnmanagedProperties[managed]; + } [return: NotNullIfNotNull("unmanaged")] public static TypeDefinition? AsManaged(this Il2CppTypeDefinition? unmanaged) diff --git a/Cpp2IL.Core/SharedState.cs b/Cpp2IL.Core/SharedState.cs index b4f87942..e2e1ece4 100644 --- a/Cpp2IL.Core/SharedState.cs +++ b/Cpp2IL.Core/SharedState.cs @@ -9,36 +9,37 @@ namespace Cpp2IL.Core public static class SharedState { //Virt methods - internal static readonly Dictionary VirtualMethodsBySlot = new Dictionary(); + internal static readonly Dictionary VirtualMethodsBySlot = new(); //Methods - internal static readonly ConcurrentDictionary MethodsByAddress = new ConcurrentDictionary(); - internal static readonly ConcurrentDictionary MethodsByIndex = new ConcurrentDictionary(); - internal static readonly ConcurrentDictionary UnmanagedToManagedMethods = new ConcurrentDictionary(); - internal static readonly ConcurrentDictionary ManagedToUnmanagedMethods = new ConcurrentDictionary(); + internal static readonly ConcurrentDictionary MethodsByAddress = new(); + internal static readonly ConcurrentDictionary MethodsByIndex = new(); + internal static readonly ConcurrentDictionary UnmanagedToManagedMethods = new(); + internal static readonly ConcurrentDictionary ManagedToUnmanagedMethods = new(); //Generic params - internal static readonly Dictionary GenericParamsByIndex = new Dictionary(); + internal static readonly Dictionary GenericParamsByIndex = new(); //Type defs - internal static readonly ConcurrentDictionary TypeDefsByIndex = new ConcurrentDictionary(); - internal static readonly List AllTypeDefinitions = new List(); - internal static readonly ConcurrentDictionary ManagedToUnmanagedTypes = new ConcurrentDictionary(); - internal static readonly ConcurrentDictionary UnmanagedToManagedTypes = new ConcurrentDictionary(); + internal static readonly ConcurrentDictionary TypeDefsByIndex = new(); + internal static readonly List AllTypeDefinitions = new(); + internal static readonly ConcurrentDictionary ManagedToUnmanagedTypes = new(); + internal static readonly ConcurrentDictionary UnmanagedToManagedTypes = new(); - internal static readonly Dictionary ConcreteImplementations = new Dictionary(); + internal static readonly Dictionary ConcreteImplementations = new(); //Fields - internal static readonly ConcurrentDictionary UnmanagedToManagedFields = new ConcurrentDictionary(); - internal static readonly ConcurrentDictionary ManagedToUnmanagedFields = new ConcurrentDictionary(); - internal static readonly ConcurrentDictionary> FieldsByType = new ConcurrentDictionary>(); + internal static readonly ConcurrentDictionary UnmanagedToManagedFields = new(); + internal static readonly ConcurrentDictionary ManagedToUnmanagedFields = new(); + internal static readonly ConcurrentDictionary> FieldsByType = new(); //Properties - internal static readonly ConcurrentDictionary UnmanagedToManagedProperties = new ConcurrentDictionary(); + internal static readonly ConcurrentDictionary UnmanagedToManagedProperties = new(); + internal static readonly ConcurrentDictionary ManagedToUnmanagedProperties = new(); //Assemblies - internal static readonly List AssemblyList = new List(); - internal static readonly Dictionary ManagedToUnmanagedAssemblies = new Dictionary(); + internal static readonly List AssemblyList = new(); + internal static readonly Dictionary ManagedToUnmanagedAssemblies = new(); internal static HashSet AttributeGeneratorStarts = new(); diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index d3770510..9b08a0c2 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -270,7 +270,7 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) // if(LibCpp2IlMain.MetadataVersion >= 29) // Logger.WarnNewline("Unable to run attribute restoration, because v29 is not fully supported yet."); // else - Cpp2IlApi.RunAttributeRestorationForAllAssemblies(keyFunctionAddresses, parallel: LibCpp2IlMain.Binary!.InstructionSet is InstructionSet.X86_32 or InstructionSet.X86_64); + Cpp2IlApi.RunAttributeRestorationForAllAssemblies(keyFunctionAddresses, parallel: LibCpp2IlMain.MetadataVersion >= 29 || LibCpp2IlMain.Binary!.InstructionSet is InstructionSet.X86_32 or InstructionSet.X86_64); Logger.InfoNewline($"Finished Applying Attributes in {(DateTime.Now - start).TotalMilliseconds:F0}ms"); diff --git a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs index 4a0f5d1b..0fa349fd 100644 --- a/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppPropertyDefinition.cs @@ -14,6 +14,8 @@ public class Il2CppPropertyDefinition public uint token; [NonSerialized] private Il2CppTypeDefinition? _type; + + public int PropertyIndex => LibCpp2IlReflection.GetPropertyIndexFromProperty(this); public Il2CppTypeDefinition? DeclaringType { diff --git a/LibCpp2IL/Reflection/LibCpp2IlReflection.cs b/LibCpp2IL/Reflection/LibCpp2IlReflection.cs index 42931738..34e93f44 100644 --- a/LibCpp2IL/Reflection/LibCpp2IlReflection.cs +++ b/LibCpp2IL/Reflection/LibCpp2IlReflection.cs @@ -12,6 +12,7 @@ public static class LibCpp2IlReflection private static readonly Dictionary _typeIndices = new(); private static readonly Dictionary _methodIndices = new(); private static readonly Dictionary _fieldIndices = new(); + private static readonly Dictionary _propertyIndices = new(); internal static void ResetCaches() { @@ -21,6 +22,7 @@ internal static void ResetCaches() _typeIndices.Clear(); _methodIndices.Clear(); _fieldIndices.Clear(); + _propertyIndices.Clear(); } public static Il2CppTypeDefinition? GetType(string name, string? @namespace = null) @@ -134,5 +136,27 @@ public static int GetFieldIndexFromField(Il2CppFieldDefinition fieldDefinition) return _fieldIndices[fieldDefinition]; } + + public static int GetPropertyIndexFromProperty(Il2CppPropertyDefinition propertyDefinition) + { + if (LibCpp2IlMain.TheMetadata == null) return -1; + + if (_propertyIndices.Count == 0) + { + lock (_propertyIndices) + { + if (_propertyIndices.Count == 0) + { + for (var i = 0; i < LibCpp2IlMain.TheMetadata.propertyDefs.Length; i++) + { + var def = LibCpp2IlMain.TheMetadata.propertyDefs[i]; + _propertyIndices[def] = i; + } + } + } + } + + return _propertyIndices[propertyDefinition]; + } } } \ No newline at end of file