From 3e4268bd73cd01c2613f5cde5e297002e0f158f7 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 20 Oct 2021 14:54:55 +0100 Subject: [PATCH] Implement underlying work for arm64 attribute restoration --- .../ARM64/Arm64ImmediateToFieldAction.cs | 10 +- .../ARM64/Arm64RegisterToFieldAction.cs | 12 +- .../AbstractAttributeLoadFromListAction.cs | 25 +++++ .../LoadAttributeFromAttributeListAction.cs | 31 +---- Cpp2IL.Core/Analysis/IAsmAnalyzer.cs | 1 + Cpp2IL.Core/AttributeRestorer.cs | 106 ++++++++++++------ Cpp2IL.Core/Cpp2IlApi.cs | 18 ++- 7 files changed, 128 insertions(+), 75 deletions(-) create mode 100644 Cpp2IL.Core/Analysis/Actions/Base/AbstractAttributeLoadFromListAction.cs diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs index 0ee487f0..7c861688 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs @@ -7,14 +7,14 @@ namespace Cpp2IL.Core.Analysis.Actions.ARM64 { public class Arm64ImmediateToFieldAction : AbstractFieldWriteAction { - private long _immValue; + public readonly long ImmValue; public Arm64ImmediateToFieldAction(MethodAnalysis context, Arm64Instruction instruction) : base(context, instruction) { var memReg = Utils.GetRegisterNameNew(instruction.MemoryBase()!.Id); InstanceBeingSetOn = context.GetLocalInReg(memReg); - _immValue = instruction.Details.Operands[0].Immediate; + ImmValue = instruction.Details.Operands[0].Immediate; if(InstanceBeingSetOn?.Type == null) return; @@ -24,13 +24,13 @@ public Arm64ImmediateToFieldAction(MethodAnalysis context, Arm FieldWritten = FieldUtils.GetFieldBeingAccessed(InstanceBeingSetOn.Type, (ulong)instruction.MemoryOffset(), false); } - protected override string? GetValueSummary() => _immValue.ToString(); + protected override string? GetValueSummary() => ImmValue.ToString(); - protected override string? GetValuePseudocode() => _immValue.ToString(); + protected override string? GetValuePseudocode() => ImmValue.ToString(); protected override Instruction[] GetIlToLoadValue(MethodAnalysis context, ILProcessor processor) => new[] { - processor.Create(OpCodes.Ldc_I4, (int) _immValue), + processor.Create(OpCodes.Ldc_I4, (int) ImmValue), }; } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs index 56b617f9..3a6f6fa5 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs @@ -8,7 +8,7 @@ namespace Cpp2IL.Core.Analysis.Actions.ARM64 { public class Arm64RegisterToFieldAction : AbstractFieldWriteAction { - private IAnalysedOperand? _sourceOperand; + public readonly IAnalysedOperand? SourceOperand; public Arm64RegisterToFieldAction(MethodAnalysis context, Arm64Instruction instruction) : base(context, instruction) { @@ -16,23 +16,23 @@ public Arm64RegisterToFieldAction(MethodAnalysis context, Arm6 InstanceBeingSetOn = context.GetLocalInReg(memReg); var sourceReg = Utils.GetRegisterNameNew(instruction.Details.Operands[0].Register.Id); - _sourceOperand = context.GetOperandInRegister(sourceReg); + SourceOperand = context.GetOperandInRegister(sourceReg); if(InstanceBeingSetOn?.Type == null) return; RegisterUsedLocal(InstanceBeingSetOn, context); - if(_sourceOperand is LocalDefinition l) + if(SourceOperand is LocalDefinition l) RegisterUsedLocal(l, context); FieldWritten = FieldUtils.GetFieldBeingAccessed(InstanceBeingSetOn.Type, (ulong)instruction.MemoryOffset(), sourceReg[0] == 'v'); } - protected override string? GetValueSummary() => _sourceOperand?.ToString(); + protected override string? GetValueSummary() => SourceOperand?.ToString(); - protected override string? GetValuePseudocode() => _sourceOperand?.GetPseudocodeRepresentation(); + protected override string? GetValuePseudocode() => SourceOperand?.GetPseudocodeRepresentation(); - protected override Instruction[] GetIlToLoadValue(MethodAnalysis context, ILProcessor processor) => _sourceOperand?.GetILToLoad(context, processor) ?? Array.Empty(); + protected override Instruction[] GetIlToLoadValue(MethodAnalysis context, ILProcessor processor) => SourceOperand?.GetILToLoad(context, processor) ?? Array.Empty(); } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractAttributeLoadFromListAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractAttributeLoadFromListAction.cs new file mode 100644 index 00000000..d75d58d7 --- /dev/null +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractAttributeLoadFromListAction.cs @@ -0,0 +1,25 @@ +using Cpp2IL.Core.Analysis.ResultModels; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Cpp2IL.Core.Analysis.Actions.Base +{ + public abstract class AbstractAttributeLoadFromListAction : BaseAction + { + public LocalDefinition? LocalMade; + public long OffsetInList; + protected TypeDefinition? _attributeType; + + protected AbstractAttributeLoadFromListAction(MethodAnalysis context, T instruction) : base(context, instruction) { } + + public sealed override Instruction[] ToILInstructions(MethodAnalysis context, ILProcessor processor) => throw new System.InvalidOperationException("Should not be attempting to generate IL for this type of instruction!"); + + public sealed override string? ToPsuedoCode() => throw new System.InvalidOperationException("Should not be attempting to generate pseudocode for this type of instruction!"); + + public sealed override string ToTextSummary() => $"[!] Loads the attribute instance at offset {OffsetInList} which is of type {_attributeType}, and stores in new local {LocalMade}"; + + public sealed override bool IsImportant() => false; + + public sealed override bool PseudocodeNeedsLinebreakBefore() => false; + } +} \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/x86/LoadAttributeFromAttributeListAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/LoadAttributeFromAttributeListAction.cs index 6725c609..439289dc 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/LoadAttributeFromAttributeListAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/LoadAttributeFromAttributeListAction.cs @@ -1,44 +1,23 @@ using System.Collections.Generic; using Cpp2IL.Core.Analysis.Actions.Base; using Cpp2IL.Core.Analysis.ResultModels; +using Iced.Intel; using LibCpp2IL; using Mono.Cecil; -using Mono.Cecil.Cil; -using Instruction = Iced.Intel.Instruction; namespace Cpp2IL.Core.Analysis.Actions.x86 { - public class LoadAttributeFromAttributeListAction : BaseAction + public class LoadAttributeFromAttributeListAction : AbstractAttributeLoadFromListAction { - public LocalDefinition? LocalMade; - private string? _destReg; - public TypeDefinition? AttributeType; - public long OffsetInList; - public LoadAttributeFromAttributeListAction(MethodAnalysis context, Instruction instruction, List attributes) : base(context, instruction) { var ptrSize = LibCpp2IlMain.Binary!.is32Bit ? 4 : 8; OffsetInList = instruction.MemoryDisplacement32 / ptrSize; - AttributeType = attributes[(int) OffsetInList]; - - _destReg = Utils.GetRegisterNameNew(instruction.Op0Register); - LocalMade = context.MakeLocal(AttributeType, reg: _destReg); - } - - public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis context, ILProcessor processor) - { - throw new System.NotImplementedException(); - } - - public override string? ToPsuedoCode() - { - throw new System.NotImplementedException(); - } + _attributeType = attributes[(int) OffsetInList]; - public override string ToTextSummary() - { - return $"[!] Loads the attribute instance at offset {OffsetInList} which is of type {AttributeType}, and stores in new local {LocalMade} in {_destReg}"; + var destReg = Utils.GetRegisterNameNew(instruction.Op0Register); + LocalMade = context.MakeLocal(_attributeType, reg: destReg); } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/IAsmAnalyzer.cs b/Cpp2IL.Core/Analysis/IAsmAnalyzer.cs index 169cb49a..d69d2cae 100644 --- a/Cpp2IL.Core/Analysis/IAsmAnalyzer.cs +++ b/Cpp2IL.Core/Analysis/IAsmAnalyzer.cs @@ -1,4 +1,5 @@ using System.Text; +using Cpp2IL.Core.Analysis.Actions.Base; namespace Cpp2IL.Core.Analysis { diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index 1b3fc579..abf737ab 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -4,14 +4,14 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using Cpp2IL.Core.Analysis; +using Cpp2IL.Core.Analysis.Actions.ARM64; using Cpp2IL.Core.Analysis.Actions.Base; using Cpp2IL.Core.Analysis.Actions.x86; using Cpp2IL.Core.Analysis.Actions.x86.Important; using Cpp2IL.Core.Analysis.ResultModels; -using Iced.Intel; +using Cpp2IL.Core.Exceptions; using LibCpp2IL; using LibCpp2IL.BinaryStructures; using LibCpp2IL.Metadata; @@ -81,20 +81,20 @@ internal static void Reset() Initialize(); } - internal static void ApplyCustomAttributesToAllTypesInAssembly(AssemblyDefinition assemblyDefinition, BaseKeyFunctionAddresses? keyFunctionAddresses) + internal static void ApplyCustomAttributesToAllTypesInAssembly(AssemblyDefinition assemblyDefinition, BaseKeyFunctionAddresses? keyFunctionAddresses) { var imageDef = SharedState.ManagedToUnmanagedAssemblies[assemblyDefinition]; foreach (var typeDef in assemblyDefinition.MainModule.Types.Where(t => t.Namespace != AssemblyPopulator.InjectedNamespaceName)) - RestoreAttributesInType(imageDef, typeDef, keyFunctionAddresses); + RestoreAttributesInType(imageDef, typeDef, keyFunctionAddresses); } - private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, TypeDefinition typeDefinition, BaseKeyFunctionAddresses? keyFunctionAddresses) + private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, TypeDefinition typeDefinition, BaseKeyFunctionAddresses? keyFunctionAddresses) { var typeDef = SharedState.ManagedToUnmanagedTypes[typeDefinition]; //Apply custom attributes to type itself - GetCustomAttributesByAttributeIndex(imageDef, typeDef.customAttributeIndex, typeDef.token, typeDefinition.Module, keyFunctionAddresses, typeDef.FullName!) + GetCustomAttributesByAttributeIndex(imageDef, typeDef.customAttributeIndex, typeDef.token, typeDefinition.Module, keyFunctionAddresses, typeDef.FullName!) .ForEach(attribute => typeDefinition.CustomAttributes.Add(attribute)); //Apply custom attributes to fields @@ -102,7 +102,7 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, Type { var fieldDefinition = SharedState.UnmanagedToManagedFields[fieldDef]; - GetCustomAttributesByAttributeIndex(imageDef, fieldDef.customAttributeIndex, fieldDef.token, typeDefinition.Module, keyFunctionAddresses, fieldDefinition.FullName) + GetCustomAttributesByAttributeIndex(imageDef, fieldDef.customAttributeIndex, fieldDef.token, typeDefinition.Module, keyFunctionAddresses, fieldDefinition.FullName) .ForEach(attribute => fieldDefinition.CustomAttributes.Add(attribute)); } @@ -111,7 +111,7 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, Type { var methodDefinition = methodDef.AsManaged(); - GetCustomAttributesByAttributeIndex(imageDef, methodDef.customAttributeIndex, methodDef.token, typeDefinition.Module, keyFunctionAddresses, methodDefinition.FullName) + GetCustomAttributesByAttributeIndex(imageDef, methodDef.customAttributeIndex, methodDef.token, typeDefinition.Module, keyFunctionAddresses, methodDefinition.FullName) .ForEach(attribute => methodDefinition.CustomAttributes.Add(attribute)); } @@ -120,18 +120,18 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, Type { var propertyDefinition = SharedState.UnmanagedToManagedProperties[propertyDef]; - GetCustomAttributesByAttributeIndex(imageDef, propertyDef.customAttributeIndex, propertyDef.token, typeDefinition.Module, keyFunctionAddresses, propertyDefinition.FullName) + GetCustomAttributesByAttributeIndex(imageDef, propertyDef.customAttributeIndex, propertyDef.token, typeDefinition.Module, keyFunctionAddresses, propertyDefinition.FullName) .ForEach(attribute => propertyDefinition.CustomAttributes.Add(attribute)); } //Nested Types foreach (var nestedType in typeDefinition.NestedTypes) { - RestoreAttributesInType(imageDef, nestedType, keyFunctionAddresses); + RestoreAttributesInType(imageDef, nestedType, keyFunctionAddresses); } } - public static List GetCustomAttributesByAttributeIndex(Il2CppImageDefinition imageDef, int attributeIndex, uint token, ModuleDefinition module, BaseKeyFunctionAddresses? keyFunctionAddresses, string warningName) + public static List GetCustomAttributesByAttributeIndex(Il2CppImageDefinition imageDef, int attributeIndex, uint token, ModuleDefinition module, BaseKeyFunctionAddresses? keyFunctionAddresses, string warningName) { var attributes = new List(); @@ -173,10 +173,10 @@ public static List GetCustomAttributesByAttributeIndex(Il2CppIm //If not, just generate those which we can (no params). return GenerateAttributesWithoutAnalysis(attributeConstructors, module, 0, true); - List> actions; + List> actions; try { - actions = GetActionsPerformedByGenerator(keyFunctionAddresses, attributeGeneratorAddress, attributesExpected); + actions = GetActionsPerformedByGenerator(keyFunctionAddresses, attributeGeneratorAddress, attributesExpected); } catch (AnalysisExceptionRaisedException e) { @@ -194,8 +194,8 @@ public static List GetCustomAttributesByAttributeIndex(Il2CppIm //Indexes shared with attributesExpected var localArray = new LocalDefinition?[attributesExpected.Count]; - foreach (var action in actions.Where(a => a is LoadAttributeFromAttributeListAction) - .Cast()) + foreach (var action in actions.Where(a => a is AbstractAttributeLoadFromListAction) + .Cast>()) { localArray[action.OffsetInList] = action.LocalMade; } @@ -228,7 +228,7 @@ public static List GetCustomAttributesByAttributeIndex(Il2CppIm //We have a local - look for constructor calls and/or field writes. var allCtorNames = attr.GetConstructors().Select(c => c.FullName).ToList(); - var matchingCtorCall = (CallManagedFunctionAction?) actions.FirstOrDefault(c => c is CallManagedFunctionAction {ManagedMethodBeingCalled: { } method} cmfa && cmfa.InstanceBeingCalledOn == local && allCtorNames.Contains(method.FullName)); + var matchingCtorCall = (AbstractCallAction?) actions.FirstOrDefault(c => c is AbstractCallAction {ManagedMethodBeingCalled: { } method} cmfa && cmfa.InstanceBeingCalledOn == local && allCtorNames.Contains(method.FullName)); (MethodDefinition potentialCtor, List parameterList)? hardWayResult = null; if (matchingCtorCall?.ManagedMethodBeingCalled == null && noArgCtor == null) @@ -328,12 +328,18 @@ private static List GenerateAttributesWithoutAnalysis(List> GetActionsPerformedByGenerator(BaseKeyFunctionAddresses keyFunctionAddresses, ulong attributeGeneratorAddress, List attributesExpected) + private static List> GetActionsPerformedByGenerator(BaseKeyFunctionAddresses keyFunctionAddresses, ulong attributeGeneratorAddress, List attributesExpected) { - var generatorBody = Utils.GetMethodBodyAtVirtAddressNew(attributeGeneratorAddress, false); - + //Nasty generic casting crap + AsmAnalyzerBase analyzer = (AsmAnalyzerBase) (LibCpp2IlMain.Binary?.InstructionSet switch + { + InstructionSet.X86_32 or InstructionSet.X86_64 => (object) new AsmAnalyzerX86(attributeGeneratorAddress, Utils.GetMethodBodyAtVirtAddressNew(attributeGeneratorAddress, false), keyFunctionAddresses!), + // InstructionSet.ARM32 => (object) new AsmAnalyzerArmV7(attributeGeneratorAddress, FIX_ME, keyFunctionAddresses!), + InstructionSet.ARM64 => (object) new AsmAnalyzerArmV8A(attributeGeneratorAddress, Utils.GetArm64MethodBodyAtVirtualAddress(attributeGeneratorAddress, false), keyFunctionAddresses!), + _ => throw new UnsupportedInstructionSetException() + }); + //Run analysis on this method to get parameters for the various constructors. - var analyzer = new AsmAnalyzerX86(attributeGeneratorAddress, generatorBody, keyFunctionAddresses!); analyzer.AddParameter(DummyTypeDefForAttributeCache, "attributeCache"); analyzer.AttributesForRestoration = attributesExpected; @@ -559,7 +565,7 @@ private static object AllocateArray(AllocatedArray array) } } - private static (MethodDefinition potentialCtor, List parameterList)? TryResolveAttributeConstructorParamsTheHardWay(BaseKeyFunctionAddresses keyFunctionAddresses, TypeDefinition attr, List> actions, LocalDefinition? local) + private static (MethodDefinition potentialCtor, List parameterList)? TryResolveAttributeConstructorParamsTheHardWay(BaseKeyFunctionAddresses keyFunctionAddresses, TypeDefinition attr, List> actions, LocalDefinition? local) { //Try and get mappings for all constructors. var allPotentialCtors = attr.GetConstructors() @@ -570,8 +576,8 @@ private static (MethodDefinition potentialCtor, List pa //And get all field writes on this attribute var allWritesInGeneratorOnThisAttribute = actions - .Where(a => (a is AbstractFieldWriteAction afwa && afwa.InstanceBeingSetOn == local)) - .Cast>() + .Where(a => (a is AbstractFieldWriteAction afwa && afwa.InstanceBeingSetOn == local)) + .Cast>() .ToList(); if (allWritesInGeneratorOnThisAttribute.Any(w => w.FieldWritten?.FinalLoadInChain == null)) @@ -618,26 +624,52 @@ private static (MethodDefinition potentialCtor, List pa if (fieldWrite == null) return null; - if (fieldWrite is ImmediateToFieldAction i) + var destType = parameter.ParameterType.Resolve()?.IsEnum == true ? parameter.ParameterType.Resolve().GetEnumUnderlyingType() : parameter.ParameterType; + switch (fieldWrite) { - var destType = parameter.ParameterType.Resolve()?.IsEnum == true ? parameter.ParameterType.Resolve().GetEnumUnderlyingType() : parameter.ParameterType; - var value = i.ConstantValue; + case ImmediateToFieldAction i: + { + var value = i.ConstantValue; - if (value.GetType().FullName != destType.FullName) - value = Utils.CoerceValue(value, destType); + if (value.GetType().FullName != destType.FullName) + value = Utils.CoerceValue(value, destType); - if (destType.FullName == "System.Object") + if (destType.FullName == "System.Object") + { + //Need to wrap value in another CustomAttributeArgument of the pre-casting type. + value = new CustomAttributeArgument(Utils.TryLookupTypeDefKnownNotGeneric(i.ConstantValue.GetType().FullName), i.ConstantValue); + } + + parameterList.Add(new CustomAttributeArgument(destType, value)); + break; + } + case Arm64ImmediateToFieldAction armI: { - //Need to wrap value in another CustomAttributeArgument of the pre-casting type. - value = new CustomAttributeArgument(Utils.TryLookupTypeDefKnownNotGeneric(i.ConstantValue.GetType().FullName), i.ConstantValue); + var value = (object) armI.ImmValue; + + if (value.GetType().FullName != destType.FullName) + value = Utils.CoerceValue(value, destType); + + if (destType.FullName == "System.Object") + { + //Need to wrap value in another CustomAttributeArgument of the pre-casting type. + value = new CustomAttributeArgument(Utils.TryLookupTypeDefKnownNotGeneric(armI.ImmValue.GetType().FullName), armI.ImmValue); + } + + parameterList.Add(new CustomAttributeArgument(destType, value)); + break; } - - parameterList.Add(new CustomAttributeArgument(destType, value)); + case RegToFieldAction {ValueRead: { }} r: + parameterList.Add(CoerceAnalyzedOpToParameter(r.ValueRead!, parameter)); + break; + case Arm64RegisterToFieldAction { SourceOperand: { } } armR: + { + parameterList.Add(CoerceAnalyzedOpToParameter(armR.SourceOperand!, parameter)); + break; + } + default: + return null; } - else if (fieldWrite is RegToFieldAction {ValueRead: { }} r) - parameterList.Add(CoerceAnalyzedOpToParameter(r.ValueRead!, parameter)); - else - return null; } return (potentialCtor, parameterList); diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index d302f6b8..b279319f 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -9,6 +9,8 @@ using Cpp2IL.Core.Analysis; using Cpp2IL.Core.Analysis.Actions.x86.Important; using Cpp2IL.Core.Exceptions; +using Gee.External.Capstone.Arm; +using Gee.External.Capstone.Arm64; using LibCpp2IL; using LibCpp2IL.Logging; using Mono.Cecil; @@ -268,7 +270,21 @@ public static void RunAttributeRestorationForAssembly(AssemblyDefinition assembl { CheckLibInitialized(); - AttributeRestorer.ApplyCustomAttributesToAllTypesInAssembly(assembly, keyFunctionAddresses); + switch (LibCpp2IlMain.Binary!.InstructionSet) + { + case InstructionSet.X86_32: + case InstructionSet.X86_64: + AttributeRestorer.ApplyCustomAttributesToAllTypesInAssembly(assembly, keyFunctionAddresses); + break; + case InstructionSet.ARM32: + AttributeRestorer.ApplyCustomAttributesToAllTypesInAssembly(assembly, keyFunctionAddresses); + break; + case InstructionSet.ARM64: + AttributeRestorer.ApplyCustomAttributesToAllTypesInAssembly(assembly, keyFunctionAddresses); + break; + default: + throw new UnsupportedInstructionSetException(); + } } public static void GenerateMetadataForAllAssemblies(string rootFolder)