diff --git a/src/LibProtodec/CilReader.cs b/src/LibProtodec/CilReader.cs new file mode 100644 index 0000000..4c8b8ae --- /dev/null +++ b/src/LibProtodec/CilReader.cs @@ -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 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; + } + } +} \ No newline at end of file diff --git a/src/LibProtodec/LibProtodec.csproj b/src/LibProtodec/LibProtodec.csproj index 8120bb0..de05a5a 100644 --- a/src/LibProtodec/LibProtodec.csproj +++ b/src/LibProtodec/LibProtodec.csproj @@ -20,7 +20,7 @@ - + \ No newline at end of file diff --git a/src/LibProtodec/ProtodecContext.cs b/src/LibProtodec/ProtodecContext.cs index 23c83f0..b82f93a 100644 --- a/src/LibProtodec/ProtodecContext.cs +++ b/src/LibProtodec/ProtodecContext.cs @@ -11,12 +11,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using System.Reflection.Emit; using SystemEx; using CommunityToolkit.Diagnostics; using LibProtodec.Models; using LibProtodec.Models.Fields; using LibProtodec.Models.TopLevels; using LibProtodec.Models.Types; +using SystemEx.Memory; namespace LibProtodec; @@ -70,6 +72,8 @@ public Message ParseMessage(Type messageClass, ParserOptions options = ParserOpt Protobuf protobuf = GetProtobuf(messageClass, message, options); + ParseWriteToMethodTesting(messageClass); + FieldInfo[] idFields = messageClass.GetFields(PublicStatic); PropertyInfo[] properties = messageClass.GetProperties(PublicInstanceDeclared); @@ -128,6 +132,39 @@ public Message ParseMessage(Type messageClass, ParserOptions options = ParserOpt return message; } + private static void ParseWriteToMethodTesting(Type messageClass) + { + MethodInfo writeTo = messageClass.GetMethod("WriteTo", BindingFlags.Public | BindingFlags.Instance)!; + MemoryReader reader = new(writeTo.GetMethodBody()!.GetILAsByteArray()!); + + 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(); + + MethodBase? method = messageClass.Module.ResolveMethod(methodToken); // System.NotSupportedException: 'Resolving tokens is not supported on assemblies loaded by a MetadataLoadContext.' + FieldInfo? field = messageClass.Module.ResolveField(fieldToken); + + //TODO + } + else + { + reader.Position += operandLength; + } + } + } + public Enum ParseEnum(Type enumEnum, ParserOptions options = ParserOptions.None) { Guard.IsTrue(enumEnum.IsEnum);