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

Implement method body parsing #2

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/LibProtodec/CilReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright © 2024 Xpl0itR
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using SystemEx.Memory;

namespace LibProtodec;

public static class CilReader
{
// Temporary AOT incompatible method to fill the dictionary, TODO: replace with a source generator
private static readonly Dictionary<int, OpCode> OpCodeLookup =
typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static)
.Select(field => (OpCode)field.GetValue(null)!)
.ToDictionary(opCode => (int)(ushort)opCode.Value);

public static OpCode ReadCilOpCode(this ref MemoryReader reader, out int operandLength)
{
byte opCodeByte = reader.ReadByte();
int opCodeInt = opCodeByte == OpCodes.Prefix1.Value
? (opCodeByte << 8) | reader.ReadByte()
: opCodeByte;

OpCode opCode = OpCodeLookup[opCodeInt];
operandLength = SizeOf(opCode.OperandType);

return opCode;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int SizeOf(OperandType operandType)
{
switch (operandType)
{
case OperandType.ShortInlineBrTarget:
case OperandType.ShortInlineI:
case OperandType.ShortInlineVar:
return 1;
case OperandType.InlineVar:
return 2;
case OperandType.InlineBrTarget:
case OperandType.InlineField:
case OperandType.InlineI:
case OperandType.InlineMethod:
case OperandType.InlineSig:
case OperandType.InlineString:
case OperandType.InlineSwitch:
case OperandType.InlineTok:
case OperandType.InlineType:
case OperandType.ShortInlineR:
return 4;
case OperandType.InlineI8:
case OperandType.InlineR:
return 8;
case OperandType.InlineNone:
default:
return 0;
}
}
}
5 changes: 5 additions & 0 deletions src/LibProtodec/Models/Cil/Clr/ClrMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System.Collections.Generic;
using System.Reflection;
using CommunityToolkit.Diagnostics;

namespace LibProtodec.Models.Cil.Clr;

Expand Down Expand Up @@ -35,4 +36,8 @@ public IEnumerable<ICilType> GetParameterTypes()
parameter.ParameterType);
}
}

public byte[] GetMethodBodyILAsByteArray() =>
clrMethod.GetMethodBody()?.GetILAsByteArray()
?? ThrowHelper.ThrowNotSupportedException<byte[]>();
}
27 changes: 27 additions & 0 deletions src/LibProtodec/Models/Cil/Clr/ClrModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Concurrent;
using System.Reflection;

namespace LibProtodec.Models.Cil.Clr;

public sealed class ClrModule : ICilModule
{
private readonly Module _module;

private ClrModule(Module module) =>
_module = module;

public string ResolveFieldName(int token) =>
_module.ResolveField(token).Name;

public string ResolveMethodName(int token) =>
_module.ResolveMethod(token).Name;


private static readonly ConcurrentDictionary<string, ClrModule> ModuleLookup = [];

public static ICilModule GetOrCreate(Module clrModule) =>
ModuleLookup.GetOrAdd(
clrModule.FullyQualifiedName,
static (_, clrModule) => new ClrModule(clrModule),
clrModule);
}
4 changes: 4 additions & 0 deletions src/LibProtodec/Models/Cil/Clr/ClrType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ private ClrType(Type clrType) : base(clrType) =>
public string DeclaringAssemblyName =>
_clrType.Assembly.FullName!;

public ICilModule DeclaringModule =>
ClrModule.GetOrCreate(
_clrType.Module);

public ICilType? BaseType =>
_clrType.BaseType is null
? null
Expand Down
2 changes: 2 additions & 0 deletions src/LibProtodec/Models/Cil/ICilMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ public interface ICilMethod
IList<ICilAttribute> CustomAttributes { get; }

IEnumerable<ICilType> GetParameterTypes();

byte[] GetMethodBodyILAsByteArray();
}
14 changes: 14 additions & 0 deletions src/LibProtodec/Models/Cil/ICilModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright © 2024 Xpl0itR
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

namespace LibProtodec.Models.Cil;

public interface ICilModule
{
string ResolveFieldName(int token);

string ResolveMethodName(int token);
}
7 changes: 4 additions & 3 deletions src/LibProtodec/Models/Cil/ICilType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ public interface ICilType
string FullName { get; }
string? Namespace { get; }

string DeclaringAssemblyName { get; }
ICilType? DeclaringType { get; }
ICilType? BaseType { get; }
string DeclaringAssemblyName { get; }
ICilModule DeclaringModule { get; }
ICilType? DeclaringType { get; }
ICilType? BaseType { get; }

bool IsAbstract { get; }
bool IsClass { get; }
Expand Down
37 changes: 37 additions & 0 deletions src/LibProtodec/ProtodecContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Emit;
using SystemEx;
using SystemEx.Memory;
using CommunityToolkit.Diagnostics;
using LibProtodec.Models.Cil;
using LibProtodec.Models.Protobuf;
Expand Down Expand Up @@ -69,6 +71,8 @@ public virtual Message ParseMessage(ICilType messageClass, ParserOptions options

Protobuf protobuf = GetProtobuf(messageClass, message, options);

ParseWriteToMethodTesting(messageClass);

List<ICilField> idFields = messageClass.GetFields()
.Where(static field => field is { IsPublic: true, IsStatic: true, IsLiteral: true })
.ToList();
Expand Down Expand Up @@ -137,6 +141,39 @@ public virtual Message ParseMessage(ICilType messageClass, ParserOptions options
return message;
}

private static void ParseWriteToMethodTesting(ICilType messageClass)
{
ICilMethod writeTo = messageClass.GetMethods().Single(static method => method.Name == "WriteTo");
MemoryReader reader = new(writeTo.GetMethodBodyILAsByteArray());

int fieldToken = 0;
while (reader.Remaining > 0)
{
OpCode opCode = reader.ReadCilOpCode(out int operandLength);
if (opCode == OpCodes.Ret) // DummyDLL from il2cppdumper will only have ret in method body
return;

if (opCode == OpCodes.Ldfld)
{
fieldToken = reader.ReadInt32LittleEndian();
}
else if (opCode == OpCodes.Call)
{
Guard.IsNotEqualTo(fieldToken, 0);
int methodToken = reader.ReadInt32LittleEndian();

string methodName = messageClass.DeclaringModule.ResolveMethodName(methodToken); // System.NotSupportedException: 'Resolving tokens is not supported on assemblies loaded by a MetadataLoadContext.'
string fieldName = messageClass.DeclaringModule.ResolveFieldName(fieldToken);

//TODO
}
else
{
reader.Position += operandLength;
}
}
}

public virtual Enum ParseEnum(ICilType enumEnum, ParserOptions options = ParserOptions.None)
{
Guard.IsTrue(enumEnum.IsEnum);
Expand Down