This commit is contained in:
Xpl0itR 2024-07-11 06:08:38 +00:00 committed by GitHub
commit 9c9121920a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 160 additions and 3 deletions

View File

@ -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;
}
}
}

View File

@ -6,6 +6,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using CommunityToolkit.Diagnostics;
namespace LibProtodec.Models.Cil.Clr; namespace LibProtodec.Models.Cil.Clr;
@ -35,4 +36,8 @@ public sealed class ClrMethod(MethodInfo clrMethod) : ClrMember(clrMethod), ICil
parameter.ParameterType); parameter.ParameterType);
} }
} }
public byte[] GetMethodBodyILAsByteArray() =>
clrMethod.GetMethodBody()?.GetILAsByteArray()
?? ThrowHelper.ThrowNotSupportedException<byte[]>();
} }

View File

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

View File

@ -31,6 +31,10 @@ public sealed class ClrType : ClrMember, ICilType
public string DeclaringAssemblyName => public string DeclaringAssemblyName =>
_clrType.Assembly.FullName!; _clrType.Assembly.FullName!;
public ICilModule DeclaringModule =>
ClrModule.GetOrCreate(
_clrType.Module);
public ICilType? BaseType => public ICilType? BaseType =>
_clrType.BaseType is null _clrType.BaseType is null
? null ? null

View File

@ -23,4 +23,6 @@ public interface ICilMethod
IList<ICilAttribute> CustomAttributes { get; } IList<ICilAttribute> CustomAttributes { get; }
IEnumerable<ICilType> GetParameterTypes(); IEnumerable<ICilType> GetParameterTypes();
byte[] GetMethodBodyILAsByteArray();
} }

View File

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

View File

@ -15,6 +15,7 @@ public interface ICilType
string? Namespace { get; } string? Namespace { get; }
string DeclaringAssemblyName { get; } string DeclaringAssemblyName { get; }
ICilModule DeclaringModule { get; }
ICilType? DeclaringType { get; } ICilType? DeclaringType { get; }
ICilType? BaseType { get; } ICilType? BaseType { get; }

View File

@ -10,7 +10,9 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection.Emit;
using SystemEx; using SystemEx;
using SystemEx.Memory;
using CommunityToolkit.Diagnostics; using CommunityToolkit.Diagnostics;
using LibProtodec.Models.Cil; using LibProtodec.Models.Cil;
using LibProtodec.Models.Protobuf; using LibProtodec.Models.Protobuf;
@ -69,6 +71,8 @@ public class ProtodecContext
Protobuf protobuf = GetProtobuf(messageClass, message, options); Protobuf protobuf = GetProtobuf(messageClass, message, options);
ParseWriteToMethodTesting(messageClass);
List<ICilField> idFields = messageClass.GetFields() List<ICilField> idFields = messageClass.GetFields()
.Where(static field => field is { IsPublic: true, IsStatic: true, IsLiteral: true }) .Where(static field => field is { IsPublic: true, IsStatic: true, IsLiteral: true })
.ToList(); .ToList();
@ -137,6 +141,39 @@ public class ProtodecContext
return message; 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) public virtual Enum ParseEnum(ICilType enumEnum, ParserOptions options = ParserOptions.None)
{ {
Guard.IsTrue(enumEnum.IsEnum); Guard.IsTrue(enumEnum.IsEnum);