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);
+ }
+ }
+}