using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using HarmonyLib; using HarmonyLib.Public.Patching; using Il2CppInterop.Common; using Il2CppInterop.Common.Host; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.Runtime; using Il2CppInterop.Runtime.Runtime.VersionSpecific.MethodInfo; using Il2CppInterop.Runtime.Startup; using Il2CppSystem; using Microsoft.Extensions.Logging; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.Utils; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("BepInEx")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Module to allow using Harmony with Il2CppInterop assemblies")] [assembly: AssemblyFileVersion("1.4.6.0")] [assembly: AssemblyInformationalVersion("1.4.6-ci.389")] [assembly: AssemblyProduct("Il2CppInterop.HarmonySupport")] [assembly: AssemblyTitle("Il2CppInterop.HarmonySupport")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.6.0")] [module: UnverifiableCode] namespace Il2CppInterop.HarmonySupport; public static class HarmonySupport { public static T AddHarmonySupport(this T host) where T : BaseHost { ((BaseHost)host).AddComponent(new HarmonySupportComponent()); return host; } } internal class HarmonySupportComponent : IHostComponent, IDisposable { public void Dispose() { PatchManager.ResolvePatcher -= TryResolve; } public void Start() { PatchManager.ResolvePatcher += TryResolve; } private static void TryResolve(object sender, PatcherResolverEventArgs args) { Type declaringType = args.Original.DeclaringType; if (!(declaringType == null) && !(Il2CppType.From(declaringType, false) == (Type)null) && !ClassInjector.IsManagedTypeInjected(declaringType)) { Il2CppDetourMethodPatcher il2CppDetourMethodPatcher = new Il2CppDetourMethodPatcher(args.Original); if (il2CppDetourMethodPatcher.IsValid) { args.MethodPatcher = (MethodPatcher)(object)il2CppDetourMethodPatcher; } } } } internal class Il2CppDetourMethodPatcher : MethodPatcher { private static readonly MethodInfo IL2CPPToManagedStringMethodInfo = AccessTools.Method(typeof(IL2CPP), "Il2CppStringToManaged", (Type[])null, (Type[])null); private static readonly MethodInfo ManagedToIL2CPPStringMethodInfo = AccessTools.Method(typeof(IL2CPP), "ManagedStringToIl2Cpp", (Type[])null, (Type[])null); private static readonly MethodInfo ObjectBaseToPtrMethodInfo = AccessTools.Method(typeof(IL2CPP), "Il2CppObjectBaseToPtr", (Type[])null, (Type[])null); private static readonly MethodInfo ObjectBaseToPtrNotNullMethodInfo = AccessTools.Method(typeof(IL2CPP), "Il2CppObjectBaseToPtrNotNull", (Type[])null, (Type[])null); private static readonly MethodInfo ReportExceptionMethodInfo = AccessTools.Method(typeof(Il2CppDetourMethodPatcher), "ReportException", (Type[])null, (Type[])null); private static readonly Dictionary StIndOpcodes = new Dictionary { [typeof(byte)] = OpCodes.Stind_I1, [typeof(sbyte)] = OpCodes.Stind_I1, [typeof(bool)] = OpCodes.Stind_I1, [typeof(short)] = OpCodes.Stind_I2, [typeof(ushort)] = OpCodes.Stind_I2, [typeof(int)] = OpCodes.Stind_I4, [typeof(uint)] = OpCodes.Stind_I4, [typeof(long)] = OpCodes.Stind_I8, [typeof(ulong)] = OpCodes.Stind_I8, [typeof(float)] = OpCodes.Stind_R4, [typeof(double)] = OpCodes.Stind_R8 }; private static readonly List DelegateCache = new List(); private INativeMethodInfoStruct modifiedNativeMethodInfo; private IDetour nativeDetour; private INativeMethodInfoStruct originalNativeMethodInfo; internal bool IsValid { get; private set; } public Il2CppDetourMethodPatcher(MethodBase original) : base(original) { Init(); } private unsafe void Init() { try { FieldInfo il2CppMethodInfoPointerFieldForGeneratedMethod = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(((MethodPatcher)this).Original); if (il2CppMethodInfoPointerFieldForGeneratedMethod == null) { if (Il2CppInteropUtils.GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(((MethodPatcher)this).Original) != null) { throw new Exception("Method " + GeneralExtensions.FullDescription(((MethodPatcher)this).Original) + " is a field accessor, it can't be patched."); } } else { originalNativeMethodInfo = UnityVersionHandler.Wrap((Il2CppMethodInfo*)(void*)(IntPtr)il2CppMethodInfoPointerFieldForGeneratedMethod.GetValue(null)); modifiedNativeMethodInfo = UnityVersionHandler.NewMethod(); Buffer.MemoryCopy(((INativeStruct)originalNativeMethodInfo).Pointer.ToPointer(), ((INativeStruct)modifiedNativeMethodInfo).Pointer.ToPointer(), UnityVersionHandler.MethodSize(), UnityVersionHandler.MethodSize()); IsValid = true; } } catch (Exception ex) { Logger.Instance.LogWarning("Failed to init IL2CPP patch backend for {Original}, using normal patch handlers: {ErrorMessage}", GeneralExtensions.FullDescription(((MethodPatcher)this).Original), ex.Message); } } public override DynamicMethodDefinition PrepareOriginal() { return null; } public override MethodBase DetourTo(MethodBase replacement) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (nativeDetour != null) { modifiedNativeMethodInfo.MethodPointer = originalNativeMethodInfo.MethodPointer; ((IDisposable)nativeDetour).Dispose(); } DynamicMethodDefinition val = ((MethodPatcher)this).CopyOriginal(); HarmonyManipulator.Manipulate(val.OriginalMethod, PatchManager.GetPatchInfo(val.OriginalMethod), new ILContext(val.Definition)); MethodInfo methodInfo = val.Generate(); MethodInfo methodInfo2 = GenerateNativeToManagedTrampoline(methodInfo).Generate(); Type delegateType = DelegateTypeFactory.instance.CreateDelegateType(methodInfo2, (CallingConvention?)CallingConvention.Cdecl); Delegate @delegate = methodInfo2.CreateDelegate(delegateType); DelegateCache.Add(@delegate); nativeDetour = Il2CppInteropRuntime.Instance.DetourProvider.Create(originalNativeMethodInfo.MethodPointer, @delegate); nativeDetour.Apply(); modifiedNativeMethodInfo.MethodPointer = nativeDetour.OriginalTrampoline; return methodInfo; } public override DynamicMethodDefinition CopyOriginal() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) DynamicMethodDefinition val = new DynamicMethodDefinition(((MethodPatcher)this).Original); ((MemberReference)val.Definition).Name = "UnhollowedWrapper_" + ((MemberReference)val.Definition).Name; ILCursor val2 = new ILCursor(new ILContext(val.Definition)); FieldReference val3 = default(FieldReference); if (val2.TryGotoNext(new Func[4] { (Instruction x) => ILPatternMatchingExt.MatchLdarg(x, 0), (Instruction x) => ILPatternMatchingExt.MatchCall(x, typeof(IL2CPP), "Il2CppObjectBaseToPtr"), (Instruction x) => ILPatternMatchingExt.MatchLdsfld(x, ref val3), (Instruction x) => ILPatternMatchingExt.MatchCall(x, typeof(IL2CPP), "il2cpp_object_get_virtual_method") })) { val2.RemoveRange(4); } else { val2.Goto(0, (MoveType)0, false).GotoNext(new Func[1] { (Instruction x) => ILPatternMatchingExt.MatchLdsfld(x, Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(((MethodPatcher)this).Original)) }).Remove(); } val2.Emit(OpCodes.Ldc_I8, ((INativeStruct)modifiedNativeMethodInfo).Pointer.ToInt64()).Emit(OpCodes.Conv_I); return val; } private static bool IsReturnBufferNeeded(int size) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (size != 1 && size != 4) { return size != 8; } return false; } if (Environment.Is64BitProcess) { return size > 16; } return true; } private unsafe DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo targetManagedMethodInfo) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown int num = 0; Type returnedType = AccessTools.GetReturnedType(((MethodPatcher)this).Original); Type type = TrampolineHelpers.NativeType(returnedType); int num2 = IntPtr.Size; bool num3 = returnedType.IsSubclassOf(typeof(ValueType)); if (num3) { uint num4 = 0u; num2 = IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.GetNativeClassPointer(returnedType), ref num4); } bool flag = num3 && IsReturnBufferNeeded(num2); if (flag) { type = typeof(IntPtr); num++; } if (!((MethodPatcher)this).Original.IsStatic) { num++; } Type[] array = (from x in ((MethodPatcher)this).Original.GetParameters() select x.ParameterType).ToArray(); Type[] array2 = new Type[array.Length + num + 1]; if (flag) { array2[0] = typeof(IntPtr); } if (!((MethodPatcher)this).Original.IsStatic) { array2[num - 1] = typeof(IntPtr); } array2[^1] = typeof(Il2CppMethodInfo*); Array.Copy(((IEnumerable)array).Select((Func)TrampolineHelpers.NativeType).ToArray(), 0, array2, num, array.Length); DynamicMethodDefinition val = new DynamicMethodDefinition("(il2cpp -> managed) " + ((MethodPatcher)this).Original.Name, type, array2); ILGenerator iLGenerator = val.GetILGenerator(); iLGenerator.BeginExceptionBlock(); LocalBuilder[] array3 = new LocalBuilder[array.Length]; if (!((MethodPatcher)this).Original.IsStatic) { EmitConvertArgumentToManaged(iLGenerator, num - 1, ((MethodPatcher)this).Original.DeclaringType, out var _); } for (int i = 0; i < array.Length; i++) { EmitConvertArgumentToManaged(iLGenerator, i + num, array[i], out array3[i]); } iLGenerator.Emit(OpCodes.Call, targetManagedMethodInfo); LocalBuilder localBuilder = null; if (returnedType != typeof(void)) { localBuilder = iLGenerator.DeclareLocal(returnedType); iLGenerator.Emit(OpCodes.Stloc, localBuilder); } for (int j = 0; j < array.Length; j++) { if (array3[j] != null) { iLGenerator.Emit(OpCodes.Ldarg_S, j + num); iLGenerator.Emit(OpCodes.Ldloc, array3[j]); Type elementType = array[j].GetElementType(); EmitConvertManagedTypeToIL2CPP(iLGenerator, elementType); iLGenerator.Emit(StIndOpcodes.TryGetValue(elementType, out var value) ? value : OpCodes.Stind_I); } } iLGenerator.BeginCatchBlock(typeof(Exception)); iLGenerator.Emit(OpCodes.Call, ReportExceptionMethodInfo); iLGenerator.EndExceptionBlock(); if (localBuilder != null) { if (flag) { iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldloc, localBuilder); iLGenerator.Emit(OpCodes.Call, ObjectBaseToPtrNotNullMethodInfo); EmitUnbox(iLGenerator); iLGenerator.Emit(OpCodes.Ldc_I4, num2); iLGenerator.Emit(OpCodes.Cpblk); iLGenerator.Emit(OpCodes.Ldarg_0); } else { iLGenerator.Emit(OpCodes.Ldloc, localBuilder); EmitConvertManagedTypeToIL2CPP(iLGenerator, returnedType); } } iLGenerator.Emit(OpCodes.Ret); return val; } private unsafe static void EmitUnbox(ILGenerator il) { il.Emit(OpCodes.Ldc_I4_2); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Sizeof, typeof(void*)); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Add); } private static void ReportException(Exception ex) { Logger.Instance.LogError(ex, "During invoking native->managed trampoline"); } private static void EmitConvertManagedTypeToIL2CPP(ILGenerator il, Type returnType) { if (returnType == typeof(string)) { il.Emit(OpCodes.Call, ManagedToIL2CPPStringMethodInfo); } else if (!returnType.IsValueType && returnType.IsSubclassOf(typeof(Il2CppObjectBase))) { il.Emit(OpCodes.Call, ObjectBaseToPtrMethodInfo); } } private static void EmitConvertArgumentToManaged(ILGenerator il, int argIndex, Type managedParamType, out LocalBuilder variable) { variable = null; if (managedParamType.IsSubclassOf(typeof(ValueType))) { il.Emit(OpCodes.Ldc_I8, Il2CppClassPointerStore.GetNativeClassPointer(managedParamType).ToInt64()); il.Emit(OpCodes.Conv_I); il.Emit(Environment.Is64BitProcess ? OpCodes.Ldarg : OpCodes.Ldarga_S, argIndex); il.Emit(OpCodes.Call, AccessTools.Method(typeof(IL2CPP), "il2cpp_value_box", (Type[])null, (Type[])null)); } else { il.Emit(OpCodes.Ldarg_S, argIndex); } if (!managedParamType.IsValueType) { if (managedParamType.IsByRef) { Type elementType = managedParamType.GetElementType(); variable = il.DeclareLocal(elementType); il.Emit(OpCodes.Ldind_I); HandleTypeConversion(elementType); il.Emit(OpCodes.Stloc, variable); il.Emit(OpCodes.Ldloca, variable); } else { HandleTypeConversion(managedParamType); } } void EmitCreateIl2CppObject(Type originalType) { Label label = il.DefineLabel(); Label label2 = il.DefineLabel(); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, label2); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Br_S, label); il.MarkLabel(label2); il.Emit(OpCodes.Call, AccessTools.Method(typeof(Il2CppObjectPool), "Get", (Type[])null, (Type[])null).MakeGenericMethod(originalType)); il.MarkLabel(label); } void HandleTypeConversion(Type originalType) { if (originalType == typeof(string)) { il.Emit(OpCodes.Call, IL2CPPToManagedStringMethodInfo); } else if (originalType.IsSubclassOf(typeof(Il2CppObjectBase))) { EmitCreateIl2CppObject(originalType); } } } }