Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support to automatically detect read order #16

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
19 changes: 13 additions & 6 deletions src/EazyDevirt/Core/Architecture/InlineOperands/VMInlineOperand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace EazyDevirt.Core.Architecture.InlineOperands;
using EazyDevirt.Devirtualization;

namespace EazyDevirt.Core.Architecture.InlineOperands;

// thank you to saneki

Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using EazyDevirt.Devirtualization;
using System.Reflection;

namespace EazyDevirt.Core.Architecture.InlineOperands;

Expand All @@ -19,10 +20,10 @@ internal abstract record VMInlineOperandData(VMInlineOperandType Type)
/// </summary>
/// <param name="reader">BinaryReader</param>
/// <returns>InlineOperandData</returns>
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),
Expand Down
58 changes: 45 additions & 13 deletions src/EazyDevirt/Core/Architecture/VMMethod.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.PE.DotNet.Cil;
using EazyDevirt.Devirtualization;

namespace EazyDevirt.Core.Architecture;

Expand Down Expand Up @@ -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<VMLocal>(reader.ReadInt16());
for (var i = 0; i < VMLocals.Capacity; i++)
VMLocals.Add(new VMLocal(reader.ReadInt32()));

VMParameters = new List<VMParameter>(reader.ReadInt16());
for (var i = 0; i < VMParameters.Capacity; i++)
VMParameters.Add(new VMParameter(reader.ReadInt32(), reader.ReadBoolean()));
case VMMethodField.Locals:
VMLocals = new List<VMLocal>(reader.ReadInt16());
for (var i = 0; i < VMLocals.Capacity; i++)
VMLocals.Add(new VMLocal(reader.ReadInt32()));
break;

case VMMethodField.Parameters:
VMParameters = new List<VMParameter>(reader.ReadInt16());
for (var i = 0; i < VMParameters.Capacity; i++)
VMParameters.Add(new VMParameter(reader.ReadInt32(), reader.ReadBoolean()));
break;
}
}
}

public override string ToString() =>
Expand All @@ -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;
Expand Down
21 changes: 14 additions & 7 deletions src/EazyDevirt/Core/IO/Resolver.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -40,10 +41,16 @@ private TypeSignature ApplySigModifiers(TypeSignature baseTypeSig, Stack<string>
{
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<ITypeDefOrRef>(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!");

Expand Down Expand Up @@ -108,7 +115,7 @@ private TypeSignature ApplySigModifiers(TypeSignature baseTypeSig, Stack<string>
{
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<IFieldDescriptor>(inlineOperand.Token);

Expand Down Expand Up @@ -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<IMethodDescriptor>(inlineOperand.Token);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions src/EazyDevirt/Devirtualization/DevirtualizationContext.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -42,6 +43,9 @@ internal record DevirtualizationContext
public TypeDefinition VMDeclaringType { get; set; }
public VMCipherStream VMStream { get; set; }
public VMCipherStream VMResolverStream { get; set; }
public Dictionary<int, VMInlineOperandType> VMOperandTypeOrder { get; set; }
public List<VMMethodField> VMMethodReadOrder { get; set; }
public Dictionary<int, EazyDevirt.Core.Architecture.InlineOperands.ValueType> OperandReadOrder { get; set; }
public int PositionCryptoKey { get; set; }
public int MethodCryptoKey { get; set; }

Expand Down
1 change: 1 addition & 0 deletions src/EazyDevirt/Devirtualization/Devirtualizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public Devirtualizer(DevirtualizationContext ctx)
new ResourceParsing(ctx),
new OpCodeMapping(ctx),
new MethodDiscovery(ctx),
new ReadOrderAnalyzer(ctx),
new MethodDevirtualizer(ctx),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);

Expand Down
Loading