diff --git a/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperand.cs b/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperand.cs index 813f4d0..c3751cc 100644 --- a/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperand.cs +++ b/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperand.cs @@ -1,4 +1,6 @@ -namespace EazyDevirt.Core.Architecture.InlineOperands; +using EazyDevirt.Devirtualization; + +namespace EazyDevirt.Core.Architecture.InlineOperands; // thank you to saneki @@ -85,15 +87,20 @@ public VMInlineOperand(ValueType valueType, int value) ValueType = valueType; Value = value; } - - public VMInlineOperand(BinaryReader reader) + + public VMInlineOperand(ValueType valueType, VMInlineOperandData data) { - ValueType = (ValueType)reader.ReadByte(); + ValueType = valueType; + Data = data; + } + public static VMInlineOperand ReadInternal(DevirtualizationContext ctx, BinaryReader reader) + { + var ValueType = ctx.OperandReadOrder[reader.ReadByte()]; if (ValueType == ValueType.Token) - Value = reader.ReadInt32(); + return new VMInlineOperand(ValueType, reader.ReadInt32()); else - Data = VMInlineOperandData.Read(reader); + return new VMInlineOperand(ValueType, VMInlineOperandData.Read(ctx, reader)); } public static VMInlineOperand ReadInternal(BinaryReader reader) => new(ValueType.Position, reader.ReadInt32()); diff --git a/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperandData.cs b/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperandData.cs index 936cdcd..8879ec1 100644 --- a/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperandData.cs +++ b/src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperandData.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using EazyDevirt.Devirtualization; +using System.Reflection; namespace EazyDevirt.Core.Architecture.InlineOperands; @@ -19,10 +20,10 @@ internal abstract record VMInlineOperandData(VMInlineOperandType Type) /// /// BinaryReader /// InlineOperandData - public static VMInlineOperandData Read(BinaryReader reader) + public static VMInlineOperandData Read(DevirtualizationContext ctx, BinaryReader reader) { var operandType = reader.ReadByte(); - return (VMInlineOperandType)operandType switch + return ctx.VMOperandTypeOrder[operandType] switch { VMInlineOperandType.Type => new VMTypeData(reader), VMInlineOperandType.Field => new VMFieldData(reader), diff --git a/src/EazyDevirt/Core/Architecture/VMMethod.cs b/src/EazyDevirt/Core/Architecture/VMMethod.cs index b1da28e..26ce0a5 100644 --- a/src/EazyDevirt/Core/Architecture/VMMethod.cs +++ b/src/EazyDevirt/Core/Architecture/VMMethod.cs @@ -1,6 +1,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; using AsmResolver.PE.DotNet.Cil; +using EazyDevirt.Devirtualization; namespace EazyDevirt.Core.Architecture; @@ -46,21 +47,42 @@ internal record VMMethodInfo public ITypeDefOrRef DeclaringType { get; set; } public ITypeDefOrRef ReturnType { get; set; } - - public VMMethodInfo(BinaryReader reader) + + public VMMethodInfo(DevirtualizationContext ctx, BinaryReader reader) { - VMDeclaringType = reader.ReadInt32(); - Name = reader.ReadString(); - BindingFlags = reader.ReadByte(); - VMReturnType = reader.ReadInt32(); + foreach (VMMethodField field in ctx.VMMethodReadOrder) + { + switch (field) + { + case VMMethodField.VMDeclaringType: + VMDeclaringType = reader.ReadInt32(); + break; + + case VMMethodField.Name: + Name = reader.ReadString(); + break; + + case VMMethodField.BindingFlags: + BindingFlags = reader.ReadByte(); + break; + + case VMMethodField.ReturnType: + VMReturnType = reader.ReadInt32(); + break; - VMLocals = new List(reader.ReadInt16()); - for (var i = 0; i < VMLocals.Capacity; i++) - VMLocals.Add(new VMLocal(reader.ReadInt32())); - - VMParameters = new List(reader.ReadInt16()); - for (var i = 0; i < VMParameters.Capacity; i++) - VMParameters.Add(new VMParameter(reader.ReadInt32(), reader.ReadBoolean())); + case VMMethodField.Locals: + VMLocals = new List(reader.ReadInt16()); + for (var i = 0; i < VMLocals.Capacity; i++) + VMLocals.Add(new VMLocal(reader.ReadInt32())); + break; + + case VMMethodField.Parameters: + VMParameters = new List(reader.ReadInt16()); + for (var i = 0; i < VMParameters.Capacity; i++) + VMParameters.Add(new VMParameter(reader.ReadInt32(), reader.ReadBoolean())); + break; + } + } } public override string ToString() => @@ -70,6 +92,16 @@ public override string ToString() => $"DeclaringType: {DeclaringType.FullName} | ReturnType: {ReturnType.FullName}"; } +internal enum VMMethodField +{ + VMDeclaringType, + Name, + BindingFlags, + ReturnType, + Locals, + Parameters +} + internal record VMLocal(int VMType) { public int VMType { get; } = VMType; diff --git a/src/EazyDevirt/Core/IO/Resolver.cs b/src/EazyDevirt/Core/IO/Resolver.cs index e2d7195..f4f90cb 100644 --- a/src/EazyDevirt/Core/IO/Resolver.cs +++ b/src/EazyDevirt/Core/IO/Resolver.cs @@ -1,6 +1,7 @@ using AsmResolver.DotNet; using AsmResolver.DotNet.Signatures; using AsmResolver.DotNet.Signatures.Types; +using AsmResolver.DotNet.Signatures.Types.Parsing; using EazyDevirt.Core.Architecture; using EazyDevirt.Core.Architecture.InlineOperands; using EazyDevirt.Devirtualization; @@ -40,10 +41,16 @@ private TypeSignature ApplySigModifiers(TypeSignature baseTypeSig, Stack { Ctx.VMResolverStream.Seek(position, SeekOrigin.Begin); - var inlineOperand = new VMInlineOperand(VMStreamReader); + var inlineOperand = VMInlineOperand.ReadInternal(this.Ctx, VMStreamReader); if (inlineOperand.IsToken) return Ctx.Module.LookupMember(inlineOperand.Token); - + + if (inlineOperand.Data is VMUserStringData strData) + { + //attempt to resolve type from string (mabye not present in eaz 2020 and above) + return TypeNameParser.Parse(this.Ctx.Module, strData.Value).ToTypeDefOrRef(); + } + if (!inlineOperand.HasData || inlineOperand.Data is not VMTypeData data) throw new Exception("VM inline operand expected to have type data!"); @@ -108,7 +115,7 @@ private TypeSignature ApplySigModifiers(TypeSignature baseTypeSig, Stack { Ctx.VMResolverStream.Seek(position, SeekOrigin.Begin); - var inlineOperand = new VMInlineOperand(VMStreamReader); + var inlineOperand = VMInlineOperand.ReadInternal(this.Ctx, VMStreamReader); if (inlineOperand.IsToken) return Ctx.Module.LookupMember(inlineOperand.Token); @@ -191,7 +198,7 @@ x.First.ParameterType is GenericParameterSignature or GenericInstanceTypeSignatu { Ctx.VMResolverStream.Seek(position, SeekOrigin.Begin); - var inlineOperand = new VMInlineOperand(VMStreamReader); + var inlineOperand = VMInlineOperand.ReadInternal(this.Ctx, VMStreamReader); if (inlineOperand.IsToken) return Ctx.Module.LookupMember(inlineOperand.Token); @@ -319,7 +326,7 @@ x.First.ParameterType is GenericParameterSignature or GenericInstanceTypeSignatu { Ctx.VMResolverStream.Seek(position, SeekOrigin.Begin); - var inlineOperand = new VMInlineOperand(VMStreamReader); + var inlineOperand = VMInlineOperand.ReadInternal(this.Ctx, VMStreamReader); if (inlineOperand.IsToken) { var member = Ctx.Module.LookupMember(inlineOperand.Token); @@ -362,7 +369,7 @@ x.First.ParameterType is GenericParameterSignature or GenericInstanceTypeSignatu { Ctx.VMResolverStream.Seek(position, SeekOrigin.Begin); - var methodInfo = new VMMethodInfo(VMStreamReader); + var methodInfo = new VMMethodInfo(this.Ctx, VMStreamReader); var declaringType = ResolveType(methodInfo.VMDeclaringType); if (declaringType is null) @@ -389,7 +396,7 @@ public string ResolveString(int position) { Ctx.VMResolverStream.Seek(position, SeekOrigin.Begin); - var inlineOperand = new VMInlineOperand(VMStreamReader); + var inlineOperand = VMInlineOperand.ReadInternal(this.Ctx, VMStreamReader); if (inlineOperand.IsToken) return Ctx.Module.LookupString(inlineOperand.Token); diff --git a/src/EazyDevirt/Devirtualization/DevirtualizationContext.cs b/src/EazyDevirt/Devirtualization/DevirtualizationContext.cs index 6f118a2..4aed8b7 100644 --- a/src/EazyDevirt/Devirtualization/DevirtualizationContext.cs +++ b/src/EazyDevirt/Devirtualization/DevirtualizationContext.cs @@ -1,6 +1,7 @@ using AsmResolver.DotNet; using AsmResolver.PE.DotNet.Metadata.Tables; using EazyDevirt.Core.Architecture; +using EazyDevirt.Core.Architecture.InlineOperands; using EazyDevirt.Core.IO; using EazyDevirt.Devirtualization.Options; using EazyDevirt.Logging; @@ -42,6 +43,9 @@ internal record DevirtualizationContext public TypeDefinition VMDeclaringType { get; set; } public VMCipherStream VMStream { get; set; } public VMCipherStream VMResolverStream { get; set; } + public Dictionary VMOperandTypeOrder { get; set; } + public List VMMethodReadOrder { get; set; } + public Dictionary OperandReadOrder { get; set; } public int PositionCryptoKey { get; set; } public int MethodCryptoKey { get; set; } diff --git a/src/EazyDevirt/Devirtualization/Devirtualizer.cs b/src/EazyDevirt/Devirtualization/Devirtualizer.cs index d84e565..4537186 100644 --- a/src/EazyDevirt/Devirtualization/Devirtualizer.cs +++ b/src/EazyDevirt/Devirtualization/Devirtualizer.cs @@ -13,6 +13,7 @@ public Devirtualizer(DevirtualizationContext ctx) new ResourceParsing(ctx), new OpCodeMapping(ctx), new MethodDiscovery(ctx), + new ReadOrderAnalyzer(ctx), new MethodDevirtualizer(ctx), }; } diff --git a/src/EazyDevirt/Devirtualization/Pipeline/MethodDevirtualizer.cs b/src/EazyDevirt/Devirtualization/Pipeline/MethodDevirtualizer.cs index dc7aeec..5263222 100644 --- a/src/EazyDevirt/Devirtualization/Pipeline/MethodDevirtualizer.cs +++ b/src/EazyDevirt/Devirtualization/Pipeline/MethodDevirtualizer.cs @@ -24,7 +24,6 @@ public override bool Run() foreach (var vmMethod in Ctx.VMMethods) { VMStream.Seek(vmMethod.MethodKey, SeekOrigin.Begin); - ReadVMMethod(vmMethod); if (Ctx.Options.VeryVerbose) @@ -37,7 +36,7 @@ public override bool Run() private void ReadVMMethod(VMMethod vmMethod) { - vmMethod.MethodInfo = new VMMethodInfo(VMStreamReader); + vmMethod.MethodInfo = new VMMethodInfo(this.Ctx, VMStreamReader); ReadExceptionHandlers(vmMethod); diff --git a/src/EazyDevirt/Devirtualization/Pipeline/ReadOrderAnalyzer.cs b/src/EazyDevirt/Devirtualization/Pipeline/ReadOrderAnalyzer.cs new file mode 100644 index 0000000..4a2f313 --- /dev/null +++ b/src/EazyDevirt/Devirtualization/Pipeline/ReadOrderAnalyzer.cs @@ -0,0 +1,270 @@ +using AsmResolver.DotNet.Serialized; +using EazyDevirt.Core.Abstractions; +using EazyDevirt.PatternMatching.Patterns; +using EazyDevirt.PatternMatching; +using AsmResolver.DotNet; +using EazyDevirt.Core.Architecture; +using AsmResolver.PE.DotNet.Cil; + +using ValueType = EazyDevirt.Core.Architecture.InlineOperands.ValueType; +using EazyDevirt.Util; +using EazyDevirt.Core.Architecture.InlineOperands; + +namespace EazyDevirt.Devirtualization +{ + internal class ReadOrderAnalyzer : StageBase + { + public ReadOrderAnalyzer(DevirtualizationContext ctx) : base(ctx) + { + } + + //Set field function order is the same accross different versions + VMMethodField[] FieldOrder = + { + VMMethodField.Locals, + VMMethodField.Parameters, + VMMethodField.Name, + VMMethodField.VMDeclaringType, + VMMethodField.ReturnType, + VMMethodField.BindingFlags + }; + + public override bool Run() + { + //find type resolver order + this.Ctx.OperandReadOrder = AnalyzeTypeResolverOrder(this.Ctx); + if (this.Ctx.OperandReadOrder.Count == 0) + { + Ctx.Console.Error($"Failed to find Correct Reading order for Type Resolver Reader!"); + return false; + } + + Ctx.Console.Success("Found Correct Type Resolver Read Order!"); + + var opDataBaseTdef = FindOperandDataBase(this.Ctx); + if (opDataBaseTdef == null) + { + Ctx.Console.Error($"Failed to find VM Operand Base"); + return false; + } + + //get all types that inherits the OperandDataBase + var inheritedTypes = opDataBaseTdef.GetAllInheriting(opDataBaseTdef); + if (inheritedTypes.Count == 0) + { + Ctx.Console.Error($"Failed to get inherited types of {opDataBaseTdef.FullName}"); + return false; + } + + Ctx.VMOperandTypeOrder = AnalyzeOperandTypes(inheritedTypes); + Ctx.Console.Success("Found Correct Operand Type Order!"); + + //Analyze VM Data Read Order + MethodDefinition? vmFuncReader = FindMethodReadOrderFunction(this.Ctx); + if (vmFuncReader == null) + { + Ctx.Console.Error($"Failed to find VM Method Reader"); + return false; + } + + var vmDataReadOrder = AnalyzeVMDataReadOrder(vmFuncReader); + + if (vmDataReadOrder.Count != FieldOrder.Length) + { + Ctx.Console.Error($"Failed to analyze VMMethod Read Order {vmFuncReader?.MetadataToken}"); + return false; + } + + string orderStrFormat = ": " + string.Join(", ", vmDataReadOrder.Select((d, i) => string.Format("[{0}] {1}", i + 1, d))); + + if (Ctx.Options.VeryVerbose) + Ctx.Console.InfoStr(orderStrFormat, "Correct VMMethod Read Order"); + Ctx.Console.Success("Found Correct Method Read Order!"); + + this.Ctx.VMMethodReadOrder = vmDataReadOrder; + return true; + } + + private Dictionary AnalyzeOperandTypes(List opTypes) + { + Dictionary opTypesOrder = new(); + + //constants + string Int32Type = "System.Int32"; + string BooleanType = "System.Boolean"; + string ByteType = "System.Byte"; + string StringType = "System.String"; + + foreach(var opType in opTypes) + { + int? typeConstant = GetVMOperandTypeCode(opType); + if (typeConstant == null) + throw new Exception("Operand Type Code should not be null!"); + + if (opType.Fields.Count == 1 && Utils.GetFieldCountFromRetType(opType, StringType) == 1) + opTypesOrder[typeConstant.Value] = VMInlineOperandType.UserString; + else if (opType.Fields.Count == 2 && Utils.GetFieldCountFromRetType(opType, Int32Type) == 2) + opTypesOrder[typeConstant.Value] = VMInlineOperandType.EazCall; + else if (opType.Fields.Count == 3 && Utils.GetFieldCountFromRetType(opType, StringType) == 1 && Utils.GetFieldCountFromRetType(opType, BooleanType) == 1) + opTypesOrder[typeConstant.Value] = VMInlineOperandType.Field; + else if (opType.Fields.Count == 6) + { + if (Utils.GetFieldCountFromRetType(opType, StringType) == 1 && Utils.GetFieldCountFromRetType(opType, ByteType) == 1) + opTypesOrder[typeConstant.Value] = VMInlineOperandType.Method; + else if (Utils.GetFieldCountFromRetType(opType, StringType) == 1 && Utils.GetFieldCountFromRetType(opType, BooleanType) == 2 && Utils.GetFieldCountFromRetType(opType, Int32Type) == 2) + opTypesOrder[typeConstant.Value] = VMInlineOperandType.Type; + } + } + + return opTypesOrder; + } + + private int? GetVMOperandTypeCode(TypeDefinition t) + { + foreach(var m in t.Methods) + { + if (!m.IsPublic || !m.IsVirtual || !m.IsSpecialName) + continue; + + if (!(m.Signature != null && m.CilMethodBody != null && m.Signature.ReturnsValue && m.Signature.ReturnType.ToString() == "System.Byte")) + continue; + + return m.CilMethodBody.Instructions[0].GetLdcI4Constant(); + } + + return null; + } + + private TypeDefinition? FindOperandDataBase(DevirtualizationContext ctx) + { + foreach (var t in ctx.Module.GetAllTypes()) + { + /* internal abstract class OperandBase + { + protected OperandBase() + { + } + + //Operand Code value corresponds to the switch value + public abstract byte GetOperandCode(); + } + */ + if (!t.IsAbstract || !t.IsNotPublic || t.Methods.Count != 2) + continue; + + //there's only one method in the type that's not a constructor (GetOperandCode) + MethodDefinition getOperandCodeFunc = t.Methods.First(g => !g.IsConstructor); + + //Need to confirm if the type is actually OperandDataBase + if (getOperandCodeFunc.IsAbstract && + getOperandCodeFunc.Signature is not null && getOperandCodeFunc.Signature.ReturnsValue && + getOperandCodeFunc.Signature.ReturnType.ToString() == "System.Byte") + return t; + } + + return null; + } + + private List AnalyzeVMDataReadOrder(MethodDefinition readFunc) + { + List readOrder = new(); + var funcRetType = readFunc?.Signature?.ReturnType.Resolve(); + + //find all set function inside VMMethod Type (order is same across different versions) + var methodsToAnalyze = funcRetType?.Methods + .Where(m => m.Parameters.Count == 1) + .OrderBy(z => z.MetadataToken.ToInt32()) + .Zip(FieldOrder, (fieldType, readerSetFunc) => (readerSetFunc, fieldType)) + .ToDictionary(x => x.fieldType, x => x.readerSetFunc); + + var instructions = readFunc?.CilMethodBody?.Instructions; + int i, j; + for (i = 0, j = 0; j < methodsToAnalyze?.Count; i++) + { + if (instructions?[i].OpCode == CilOpCodes.Callvirt && instructions[i].Operand is SerializedMethodDefinition smd + && methodsToAnalyze.ContainsKey(smd)) + { + readOrder.Add(methodsToAnalyze[smd]); + j++; + + } + } + + return readOrder; + } + + private Dictionary AnalyzeTypeResolverOrder(DevirtualizationContext ctx) + { + Dictionary order = new(); + foreach (var t in ctx.Module.GetAllTypes()) + { + foreach (var method in t.Methods) + { + if (!method.HasMethodBody || method?.CilMethodBody == null || method?.CilMethodBody?.Instructions.Count == 0 || method?.Parameters.Count != 1) + continue; + + var matchedInstrs = PatternMatcher.GetAllMatchingInstructions(new OperandResolverPattern(), method); + if (matchedInstrs.Count > 0) + { + var lastIndex = method.CilMethodBody.Instructions.GetIndexByOffset(matchedInstrs.First().Last().Offset); + var currInstr = method.CilMethodBody.Instructions[lastIndex + 1]; + var secInstr = method.CilMethodBody.Instructions[lastIndex + 2]; + + if (currInstr.IsLdcI4() && secInstr.IsConditionalBranch()) //2021 sample + { + if (currInstr.GetLdcI4Constant() == 1) + { + order[0] = ValueType.Position; + order[1] = ValueType.Token; + return order; + } + else + { + //from older sample + order[0] = ValueType.Position; + order[1] = ValueType.Token; + return order; + } + } + else + { + order[0] = ValueType.Token; + order[1] = ValueType.Position; + return order; + } + } + + } + } + + return order; + } + + private MethodDefinition? FindMethodReadOrderFunction(DevirtualizationContext ctx) + { + foreach (var t in ctx.Module.GetAllTypes()) + { + foreach (var method in t.Methods) + { + if (!method.HasMethodBody || method?.CilMethodBody?.Instructions.Count == 0 || method?.Parameters.Count != 3) + continue; + + if (method.Parameters[0].ParameterType.FullName == "System.IO.Stream" + && method.Parameters[1].ParameterType.FullName == "System.Int64" + && method.Parameters[2].ParameterType.FullName == "System.String") + { + var matchedInstrs = PatternMatcher.GetAllMatchingInstructions(new ReadVMMethodPattern(), method); + if (matchedInstrs.Count > 0) + { + var ReadVMFunc = matchedInstrs.First()[4].Operand as SerializedMethodDefinition; + return ReadVMFunc; + } + } + + } + } + + return null; + } + } +} diff --git a/src/EazyDevirt/PatternMatching/Patterns/OperandResolverPattern.cs b/src/EazyDevirt/PatternMatching/Patterns/OperandResolverPattern.cs new file mode 100644 index 0000000..a877239 --- /dev/null +++ b/src/EazyDevirt/PatternMatching/Patterns/OperandResolverPattern.cs @@ -0,0 +1,28 @@ +using AsmResolver.PE.DotNet.Cil; +using EazyDevirt.Core.Abstractions.Interfaces; + +namespace EazyDevirt.PatternMatching.Patterns +{ + internal class OperandResolverPattern : IPattern + { + public IList Pattern => new List() + { + CilOpCodes.Callvirt, //17 002E callvirt instance int64 '\u000e\u2004'::'\u000e\u2004\u2000\u2000\u2009\u2009\u0002'(int64, int32) + CilOpCodes.Pop, //18 0033 pop + CilOpCodes.Newobj, //19 0034 newobj instance void '\u000e\u2000'::.ctor() + CilOpCodes.Stloc_1, //20 0039 stloc.1 + CilOpCodes.Ldloc, //21 003A ldloc.1 + CilOpCodes.Ldarg_0, //22 003B ldarg.0 + CilOpCodes.Ldfld, //23 003C ldfld class '\u000f\u2002' '\b\u2008'::'\b\u2000' + CilOpCodes.Callvirt, //24 0041 callvirt instance uint8 '\u000f\u2002'::'\u0002'() + CilOpCodes.Callvirt, //25 0046 callvirt instance void '\u000e\u2000'::'\u0002'(uint8) + CilOpCodes.Ldloc_1, //26 004B ldloc.1 + CilOpCodes.Callvirt //27 004C callvirt instance uint8 '\u000e\u2000'::'\u0002'() + // .... + }; + + public bool InterchangeStlocOpCodes => true; + public bool InterchangeLdlocOpCodes => true; + public bool MatchEntireBody => false; + } +} diff --git a/src/EazyDevirt/PatternMatching/Patterns/ReadVMMethodPattern.cs b/src/EazyDevirt/PatternMatching/Patterns/ReadVMMethodPattern.cs new file mode 100644 index 0000000..cae118f --- /dev/null +++ b/src/EazyDevirt/PatternMatching/Patterns/ReadVMMethodPattern.cs @@ -0,0 +1,28 @@ +using AsmResolver.DotNet.Code.Cil; +using AsmResolver.DotNet.Serialized; +using AsmResolver.PE.DotNet.Cil; +using EazyDevirt.Core.Abstractions.Interfaces; + +namespace EazyDevirt.PatternMatching.Patterns +{ + internal class ReadVMMethodPattern : IPattern + { + public IList Pattern => new List + { + CilOpCodes.Ldarg_0, //34 0052 ldarg.0 + CilOpCodes.Ldarg_0, //35 0053 ldarg.0 + CilOpCodes.Ldarg_0, //36 0054 ldarg.0 + CilOpCodes.Ldfld, //37 0055 ldfld class EazBinaryReader VMRuntime::'\b' + CilOpCodes.Call, //38 005A call instance class '\b\u2001' VMRuntime::'\u0002'(class EazBinaryReader) + CilOpCodes.Stfld //39 005F stfld class '\b\u2001' VMRuntime::'\b\u2000' + // ... + }; + + public bool MatchEntireBody => false; + public bool Verify(CilInstructionCollection instructions, int index = 0) + { + var resolveVMTypeCodeMethod = instructions[index + 4].Operand as SerializedMethodDefinition; + return resolveVMTypeCodeMethod?.Parameters.Count == 1; + } + } +} diff --git a/src/EazyDevirt/Util/SwitchAnalyzer.cs b/src/EazyDevirt/Util/SwitchAnalyzer.cs new file mode 100644 index 0000000..125823f --- /dev/null +++ b/src/EazyDevirt/Util/SwitchAnalyzer.cs @@ -0,0 +1,57 @@ +using AsmResolver.DotNet; +using AsmResolver.PE.DotNet.Cil; +using Echo.Platforms.AsmResolver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EazyDevirt.Util +{ + internal class SwitchAnalyzer + { + public MethodDefinition CurrentMethod { get; private set; } + public CilInstruction SwitchInstruction { get; private set; } + + bool _read; + List caseCilInstructions { get; } + public int CaseCount => caseCilInstructions.Count; + + + public SwitchAnalyzer(MethodDefinition currentMethod, CilInstruction switchInstruction) + { + if (switchInstruction.OpCode != CilOpCodes.Switch) + throw new Exception("Invalid instruction! Instruction must have a switch opcode!"); + + if (switchInstruction.Operand == null) + throw new Exception("Invalid instruction! Switch instruction must have an operand!"); + + CurrentMethod = currentMethod; + SwitchInstruction = switchInstruction; + caseCilInstructions = new List(); + _read = false; + } + + public bool Read() + { + if (_read) + return false; + + var graph = this.CurrentMethod?.CilMethodBody?.ConstructStaticFlowGraph(); + var switchNode = graph?.Nodes.FirstOrDefault(node => node?.Contents.Footer == this.SwitchInstruction, null); + + //it is impossible for the function to not have a switch opcode + if (switchNode == null) + return false; + + foreach (var adjNode in switchNode.ConditionalEdges) + caseCilInstructions.Add(adjNode.Target.Contents.Instructions.ToArray()); + + return true; + } + + public CilInstruction[] GetCase(int caseIndex) + => caseCilInstructions[caseIndex]; + } +} diff --git a/src/EazyDevirt/Util/TypeDefinitionExtensions.cs b/src/EazyDevirt/Util/TypeDefinitionExtensions.cs new file mode 100644 index 0000000..1488da7 --- /dev/null +++ b/src/EazyDevirt/Util/TypeDefinitionExtensions.cs @@ -0,0 +1,27 @@ +using AsmResolver.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EazyDevirt.Util +{ + internal static class TypeDefinitionExtensions + { + public static List GetAllInheriting(this TypeDefinition typeDefinition, TypeDefinition typeToFind) + { + List inheritedTypes = new(); + var module = typeDefinition.Module; + + if(module == null) + return inheritedTypes; + + foreach (var t in module.GetAllTypes()) + if (t.BaseType != null && t.BaseType.MetadataToken == typeToFind.MetadataToken) + inheritedTypes.Add(t); + + return inheritedTypes; + } + } +} diff --git a/src/EazyDevirt/Util/Utils.cs b/src/EazyDevirt/Util/Utils.cs new file mode 100644 index 0000000..82cac32 --- /dev/null +++ b/src/EazyDevirt/Util/Utils.cs @@ -0,0 +1,17 @@ +using AsmResolver.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EazyDevirt.Util +{ + internal class Utils + { + public static int GetFieldCountFromRetType(TypeDefinition t, string typeName) + { + return t.Fields.Count(x => x.Signature?.FieldType.ToString() == typeName); + } + } +}