From dc046b97a3e29ab5ae91259024700e95ea770429 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 17 Sep 2021 01:06:56 +0100 Subject: [PATCH] Add Arm64 field writes, post-processors. --- .../ARM64/Arm64FieldReadToRegAction.cs | 4 ++ .../ARM64/Arm64ImmediateToFieldAction.cs | 36 ++++++++++++ .../Actions/ARM64/Arm64NewObjectAction.cs | 2 + .../Actions/ARM64/Arm64OrZeroAndImmAction.cs | 48 +++++++++++++++ .../ARM64/Arm64RegisterToFieldAction.cs | 38 ++++++++++++ .../Arm64ZeroRegisterToRegisterAction.cs | 10 +++- .../Actions/Base/AbstractFieldReadAction.cs | 2 +- .../AsmAnalyzerArmV8A.InstructionChecks.cs | 58 +++++++++++++++++++ Cpp2IL.Core/Analysis/AsmAnalyzerArmV8a.cs | 4 +- Cpp2IL.Core/Analysis/AsmAnalyzerX86.cs | 4 +- .../0RemovedUnusedLocalsPostProcessor.cs | 3 +- .../1RenameLocalsPostProcessor.cs | 16 ++--- .../PostProcessActions/PostProcessor.cs | 3 +- 13 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs create mode 100644 Cpp2IL.Core/Analysis/Actions/ARM64/Arm64OrZeroAndImmAction.cs create mode 100644 Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64FieldReadToRegAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64FieldReadToRegAction.cs index 328b7169..fb5bbafd 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64FieldReadToRegAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64FieldReadToRegAction.cs @@ -19,8 +19,12 @@ public Arm64FieldReadToRegAction(MethodAnalysis context, Arm64 if(FieldRead == null) return; + + RegisterUsedLocal(ReadFrom); LocalWritten = context.MakeLocal(FieldRead.GetFinalType()!, reg: destReg); + + RegisterUsedLocal(LocalWritten); } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs new file mode 100644 index 00000000..5f8ae504 --- /dev/null +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ImmediateToFieldAction.cs @@ -0,0 +1,36 @@ +using Cpp2IL.Core.Analysis.Actions.Base; +using Cpp2IL.Core.Analysis.ResultModels; +using Gee.External.Capstone.Arm64; +using Mono.Cecil.Cil; + +namespace Cpp2IL.Core.Analysis.Actions.ARM64 +{ + public class Arm64ImmediateToFieldAction : AbstractFieldWriteAction + { + private 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; + + if(InstanceBeingSetOn?.Type == null) + return; + + RegisterUsedLocal(InstanceBeingSetOn); + + FieldWritten = FieldUtils.GetFieldBeingAccessed(InstanceBeingSetOn.Type, (ulong)instruction.MemoryOffset(), false); + } + + protected override string? GetValueSummary() => _immValue.ToString(); + + protected override string? GetValuePseudocode() => _immValue.ToString(); + + protected override Instruction[] GetIlToLoadValue(MethodAnalysis context, ILProcessor processor) => new[] + { + processor.Create(OpCodes.Ldc_I4, (int) _immValue), + }; + } +} \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64NewObjectAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64NewObjectAction.cs index ac165d22..5da83f0b 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64NewObjectAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64NewObjectAction.cs @@ -18,6 +18,8 @@ public Arm64NewObjectAction(MethodAnalysis context, Arm64Instr return; LocalReturned = context.MakeLocal(TypeCreated, reg: "x0"); + + RegisterUsedLocal(LocalReturned); } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64OrZeroAndImmAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64OrZeroAndImmAction.cs new file mode 100644 index 00000000..6cab38a9 --- /dev/null +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64OrZeroAndImmAction.cs @@ -0,0 +1,48 @@ +using System; +using Cpp2IL.Core.Analysis.Actions.Base; +using Cpp2IL.Core.Analysis.ResultModels; +using Gee.External.Capstone.Arm64; +using Mono.Cecil.Cil; + +namespace Cpp2IL.Core.Analysis.Actions.ARM64 +{ + public class Arm64OrZeroAndImmAction : BaseAction + { + private readonly string _destReg; + private readonly long _immValue; + private readonly LocalDefinition _localMade; + + public Arm64OrZeroAndImmAction(MethodAnalysis context, Arm64Instruction instruction) : base(context, instruction) + { + _destReg = Utils.GetRegisterNameNew(instruction.Details.Operands[0].Register.Id); + _immValue = instruction.Details.Operands[2].Immediate; + + _localMade = context.MakeLocal(Utils.Int64Reference, reg: _destReg, knownInitialValue: _immValue); + RegisterDefinedLocalWithoutSideEffects(_localMade); + } + + public override Instruction[] ToILInstructions(MethodAnalysis context, ILProcessor processor) + { + if (_localMade.Variable == null) + return Array.Empty(); + + return new[] + { + processor.Create(OpCodes.Ldc_I4, (int) _immValue), + processor.Create(OpCodes.Stloc, _localMade.Variable) + }; + } + + public override string? ToPsuedoCode() + { + return $"{_localMade.Type} {_localMade.Name} = {_immValue}"; + } + + public override string ToTextSummary() + { + return $"Creates new local {_localMade} in {_destReg} by ORing 0 with {_immValue}"; + } + + public override bool IsImportant() => true; + } +} \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs new file mode 100644 index 00000000..2b3c6dc6 --- /dev/null +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64RegisterToFieldAction.cs @@ -0,0 +1,38 @@ +using System; +using Cpp2IL.Core.Analysis.Actions.Base; +using Cpp2IL.Core.Analysis.ResultModels; +using Gee.External.Capstone.Arm64; +using Mono.Cecil.Cil; + +namespace Cpp2IL.Core.Analysis.Actions.ARM64 +{ + public class Arm64RegisterToFieldAction : AbstractFieldWriteAction + { + private IAnalysedOperand? _sourceOperand; + + public Arm64RegisterToFieldAction(MethodAnalysis context, Arm64Instruction instruction) : base(context, instruction) + { + var memReg = Utils.GetRegisterNameNew(instruction.MemoryBase()!.Id); + InstanceBeingSetOn = context.GetLocalInReg(memReg); + + var sourceReg = Utils.GetRegisterNameNew(instruction.Details.Operands[0].Register.Id); + _sourceOperand = context.GetOperandInRegister(sourceReg); + + if(InstanceBeingSetOn?.Type == null) + return; + + RegisterUsedLocal(InstanceBeingSetOn); + + if(_sourceOperand is LocalDefinition l) + RegisterUsedLocal(l); + + FieldWritten = FieldUtils.GetFieldBeingAccessed(InstanceBeingSetOn.Type, (ulong)instruction.MemoryOffset(), sourceReg[0] == 'v'); + } + + protected override string? GetValueSummary() => _sourceOperand?.ToString(); + + protected override string? GetValuePseudocode() => _sourceOperand?.GetPseudocodeRepresentation(); + + 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/ARM64/Arm64ZeroRegisterToRegisterAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ZeroRegisterToRegisterAction.cs index 03581e6a..9f17b9fb 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ZeroRegisterToRegisterAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ZeroRegisterToRegisterAction.cs @@ -1,4 +1,5 @@ -using Cpp2IL.Core.Analysis.Actions.Base; +using System; +using Cpp2IL.Core.Analysis.Actions.Base; using Cpp2IL.Core.Analysis.ResultModels; using Gee.External.Capstone.Arm64; using Mono.Cecil.Cil; @@ -14,13 +15,18 @@ public Arm64ZeroRegisterToRegisterAction(MethodAnalysis contex { _destReg = Utils.GetRegisterNameNew(instruction.Details.Operands[0].Register.Id); _localMade = context.MakeLocal(Utils.Int64Reference, reg: _destReg, knownInitialValue: 0UL); + RegisterDefinedLocalWithoutSideEffects(_localMade); } public override Instruction[] ToILInstructions(MethodAnalysis context, ILProcessor processor) { + if (_localMade.Variable == null) + return Array.Empty(); + return new[] { - processor.Create(OpCodes.Ldc_I4, 0) + processor.Create(OpCodes.Ldc_I4, 0), + processor.Create(OpCodes.Stloc, _localMade.Variable) }; } diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs index 03c3591c..450df906 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs @@ -29,7 +29,7 @@ public override Instruction[] ToILInstructions(MethodAnalysis context, ILProc if (LocalWritten == null || ReadFrom == null || FieldRead == null) throw new TaintedInstructionException(); - var ret = new List(); + var ret = new List(); //Load object ret.AddRange(ReadFrom.GetILToLoad(context, processor)); diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs index edc6d2b7..3e62695f 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs @@ -23,6 +23,9 @@ protected override void PerformInstructionChecks(Arm64Instruction instruction) case 2: CheckForTwoOpInstruction(instruction); break; + case 3: + CheckForThreeOpInstruction(instruction); + break; } } @@ -93,6 +96,8 @@ private void CheckForTwoOpInstruction(Arm64Instruction instruction) var mnemonic = instruction.Mnemonic; if (mnemonic is "ldrb" or "ldrh") mnemonic = "ldr"; + if (mnemonic is "strb" or "strh") + mnemonic = "str"; //The single most annoying part about capstone is that its mnemonics are strings. switch (mnemonic) @@ -175,6 +180,59 @@ private void CheckForTwoOpInstruction(Arm64Instruction instruction) //Move generic analyzed op to another reg Analysis.Actions.Add(new Arm64RegCopyAction(Analysis, instruction)); break; + case "str" when t0 is Arm64OperandType.Register && t1 is Arm64OperandType.Memory && var0 is {} && memVar is LocalDefinition: + //Field write from register. + //Unlike a bunch of other instructions, source is operand 0, destination is operand 1. + Analysis.Actions.Add(new Arm64RegisterToFieldAction(Analysis, instruction)); + break; + case "str" when t0 is Arm64OperandType.Immediate && t1 is Arm64OperandType.Memory && memVar is LocalDefinition: + //Field write from immediate + Analysis.Actions.Add(new Arm64ImmediateToFieldAction(Analysis, instruction)); + break; + } + } + + private void CheckForThreeOpInstruction(Arm64Instruction instruction) + { + var op0 = instruction.Details.Operands[0]!; + var op1 = instruction.Details.Operands[1]!; + var op2 = instruction.Details.Operands[2]!; + + var t0 = op0.Type; + var t1 = op1.Type; + var t2 = op2.Type; + + var r0 = op0.RegisterSafe()?.Id ?? Arm64RegisterId.Invalid; + var r1 = op1.RegisterSafe()?.Id ?? Arm64RegisterId.Invalid; + var r2 = op2.RegisterSafe()?.Id ?? Arm64RegisterId.Invalid; + + var r0Name = Utils.GetRegisterNameNew(r0); + var r1Name = Utils.GetRegisterNameNew(r1); + var r2Name = Utils.GetRegisterNameNew(r2); + + var var0 = Analysis.GetOperandInRegister(r0Name); + var var1 = Analysis.GetOperandInRegister(r1Name); + var var2 = Analysis.GetOperandInRegister(r2Name); + + var imm0 = op0.ImmediateSafe(); + var imm1 = op1.ImmediateSafe(); + var imm2 = op2.ImmediateSafe(); + + var memoryBase = instruction.MemoryBase()?.Id ?? Arm64RegisterId.Invalid; + var memoryOffset = instruction.MemoryOffset(); + var memoryIndex = instruction.MemoryIndex()?.Id ?? Arm64RegisterId.Invalid; + + var memVar = Analysis.GetOperandInRegister(Utils.GetRegisterNameNew(memoryBase)); + + var mnemonic = instruction.Mnemonic; + + switch (mnemonic) + { + case "orr" when r1Name is "xzr" && t2 == Arm64OperandType.Immediate && imm2 != 0: + //ORR dest, xzr, #n + //dest = n, basically. Technically 0 | n, but that's the same. + Analysis.Actions.Add(new Arm64OrZeroAndImmAction(Analysis, instruction)); + break; } } } diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8a.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8a.cs index 30b485eb..18fa9070 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8a.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8a.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Cpp2IL.Core.Analysis.PostProcessActions; using Gee.External.Capstone; using Gee.External.Capstone.Arm64; using LibCpp2IL; @@ -119,7 +120,8 @@ internal override StringBuilder GetAssemblyDump() public override void RunPostProcessors() { - //no-op + new RemovedUnusedLocalsPostProcessor().PostProcess(Analysis); + new RenameLocalsPostProcessor().PostProcess(Analysis); } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerX86.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerX86.cs index fbf51b05..bbf99a9b 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerX86.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerX86.cs @@ -93,8 +93,8 @@ internal override StringBuilder GetAssemblyDump() public override void RunPostProcessors() { - new RemovedUnusedLocalsPostProcessor().PostProcess(Analysis, MethodDefinition!); - new RenameLocalsPostProcessor().PostProcess(Analysis, MethodDefinition!); + new RemovedUnusedLocalsPostProcessor().PostProcess(Analysis); + new RenameLocalsPostProcessor().PostProcess(Analysis); } #if false diff --git a/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs b/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs index 1c0b066e..28e84414 100644 --- a/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs +++ b/Cpp2IL.Core/Analysis/PostProcessActions/0RemovedUnusedLocalsPostProcessor.cs @@ -2,13 +2,12 @@ using System.Linq; using Cpp2IL.Core.Analysis.ResultModels; -using Mono.Cecil; namespace Cpp2IL.Core.Analysis.PostProcessActions { public class RemovedUnusedLocalsPostProcessor : PostProcessor { - public override void PostProcess(MethodAnalysis analysis, MethodDefinition definition) + public override void PostProcess(MethodAnalysis analysis) { var unused = analysis.Locals.Where(l => !analysis.FunctionArgumentLocals.Contains(l) && analysis.Actions.All(a => !a.GetUsedLocals().Contains(l))).ToList(); #if PRINT_UNUSED_LOCAL_DATA diff --git a/Cpp2IL.Core/Analysis/PostProcessActions/1RenameLocalsPostProcessor.cs b/Cpp2IL.Core/Analysis/PostProcessActions/1RenameLocalsPostProcessor.cs index 72e8f4d5..15fd3e49 100644 --- a/Cpp2IL.Core/Analysis/PostProcessActions/1RenameLocalsPostProcessor.cs +++ b/Cpp2IL.Core/Analysis/PostProcessActions/1RenameLocalsPostProcessor.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; +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 Mono.Cecil; namespace Cpp2IL.Core.Analysis.PostProcessActions { - public class RenameLocalsPostProcessor : PostProcessor { + public class RenameLocalsPostProcessor : PostProcessor { - public override void PostProcess(MethodAnalysis analysis, MethodDefinition definition) + public override void PostProcess(MethodAnalysis analysis) { var countDict = new Dictionary(); @@ -18,7 +18,7 @@ public override void PostProcess(MethodAnalysis analysis, MethodDef { LocalDefinition localDefinition; string nameBase; - if (action is FieldToLocalAction {FieldRead: {}, LocalWritten: {}} ftla) + if (action is AbstractFieldReadAction {FieldRead: {}, LocalWritten: {}} ftla) { var field = ftla.FieldRead.GetLast().FinalLoadInChain!; nameBase = field.Name; @@ -27,7 +27,7 @@ public override void PostProcess(MethodAnalysis analysis, MethodDef localDefinition = ftla.LocalWritten; } - else if (action is StaticFieldToRegAction {FieldRead: {}, LocalWritten: {}} sftra) + else if (action is AbstractStaticFieldReadAction {FieldRead: {}, LocalWritten: {}} sftra) { var field = sftra.FieldRead; nameBase = field.Name; @@ -36,7 +36,7 @@ public override void PostProcess(MethodAnalysis analysis, MethodDef localDefinition = sftra.LocalWritten; } - else if (action is BaseX86CallAction {ReturnedLocal: { }, ManagedMethodBeingCalled: {}} aca) + else if (action is AbstractCallAction {ReturnedLocal: { }, ManagedMethodBeingCalled: {}} aca) { if (aca.ManagedMethodBeingCalled.Name.StartsWith("get_")) nameBase = aca.ManagedMethodBeingCalled.Name[4..]; @@ -53,11 +53,11 @@ public override void PostProcess(MethodAnalysis analysis, MethodDef { nameBase = "length"; localDefinition = alptla.LocalMade; - } else if (action is ArrayElementReadToRegAction {LocalMade: { }} aertpa) + } else if (action is AbstractArrayOffsetReadAction {LocalMade: { }} aertpa) { nameBase = aertpa.LocalMade.Type!.Name; localDefinition = aertpa.LocalMade; - } else if (action is AllocateInstanceAction {LocalReturned: { }, TypeCreated: { }} aia) + } else if (action is AbstractNewObjAction {LocalReturned: { }, TypeCreated: { }} aia) { nameBase = aia.TypeCreated.Name; localDefinition = aia.LocalReturned; diff --git a/Cpp2IL.Core/Analysis/PostProcessActions/PostProcessor.cs b/Cpp2IL.Core/Analysis/PostProcessActions/PostProcessor.cs index b3605de1..519ecfee 100644 --- a/Cpp2IL.Core/Analysis/PostProcessActions/PostProcessor.cs +++ b/Cpp2IL.Core/Analysis/PostProcessActions/PostProcessor.cs @@ -1,10 +1,9 @@ using Cpp2IL.Core.Analysis.ResultModels; -using Mono.Cecil; namespace Cpp2IL.Core.Analysis.PostProcessActions { public abstract class PostProcessor { - public abstract void PostProcess(MethodAnalysis analysis, MethodDefinition definition); + public abstract void PostProcess(MethodAnalysis analysis); } } \ No newline at end of file