123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- using System;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Runtime.InteropServices;
- using BepInEx.Logging;
- using HarmonyLib;
- using HarmonyLib.Public.Patching;
- using MonoMod.Cil;
- using MonoMod.RuntimeDetour;
- using MonoMod.Utils;
- using UnhollowerBaseLib;
- using UnhollowerBaseLib.Runtime;
- namespace BepInEx.IL2CPP.Hook
- {
- public unsafe class IL2CPPDetourMethodPatcher : MethodPatcher
- {
- private static readonly MethodInfo IL2CPPToManagedStringMethodInfo
- = AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.Il2CppStringToManaged));
- private static readonly MethodInfo ManagedToIL2CPPStringMethodInfo
- = AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.ManagedStringToIl2Cpp));
- private static readonly MethodInfo ObjectBaseToPtrMethodInfo
- = AccessTools.Method(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.Il2CppObjectBaseToPtr));
- private static readonly MethodInfo ReportExceptionMethodInfo
- = AccessTools.Method(typeof(IL2CPPDetourMethodPatcher), nameof(ReportException));
- private static readonly ManualLogSource DetourLogger = Logger.CreateLogSource("Detour");
- private FastNativeDetour nativeDetour;
- private Il2CppMethodInfo* originalNativeMethodInfo;
- private Il2CppMethodInfo* modifiedNativeMethodInfo;
-
-
-
-
- public IL2CPPDetourMethodPatcher(MethodBase original) : base(original)
- {
- Init();
- }
- private void Init()
- {
-
- originalNativeMethodInfo = (Il2CppMethodInfo*)
- (IntPtr)UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original).GetValue(null);
-
- var trampolinePtr = DetourGenerator.CreateTrampolineFromFunction(originalNativeMethodInfo->methodPointer, out _, out _);
-
- modifiedNativeMethodInfo = (Il2CppMethodInfo*)Marshal.AllocHGlobal(Marshal.SizeOf<Il2CppMethodInfo>());
- Marshal.StructureToPtr(*originalNativeMethodInfo, (IntPtr)modifiedNativeMethodInfo, false);
- modifiedNativeMethodInfo->methodPointer = trampolinePtr;
- }
-
- public override DynamicMethodDefinition PrepareOriginal()
- {
- return null;
- }
-
- public override MethodBase DetourTo(MethodBase replacement)
- {
-
- nativeDetour?.Dispose();
-
- var copiedDmd = CopyOriginal();
- HarmonyManipulator.Manipulate(copiedDmd.OriginalMethod, copiedDmd.OriginalMethod.GetPatchInfo(), new ILContext(copiedDmd.Definition));
-
- var managedHookedMethod = copiedDmd.Generate();
- var unmanagedTrampolineMethod = GenerateNativeToManagedTrampoline(managedHookedMethod).Generate();
-
- var unmanagedDelegateType = DelegateTypeFactory.instance.CreateDelegateType(unmanagedTrampolineMethod,
- CallingConvention.Cdecl);
- var detourPtr = Marshal.GetFunctionPointerForDelegate(unmanagedTrampolineMethod.CreateDelegate(unmanagedDelegateType));
- nativeDetour = new FastNativeDetour(originalNativeMethodInfo->methodPointer, detourPtr);
- nativeDetour.Apply();
-
-
- return managedHookedMethod;
- }
-
- public override DynamicMethodDefinition CopyOriginal()
- {
- var dmd = new DynamicMethodDefinition(Original);
- dmd.Definition.Name = "UnhollowedWrapper_" + dmd.Definition.Name;
- var cursor = new ILCursor(new ILContext(dmd.Definition));
-
- if (cursor.TryGotoNext(x => x.MatchLdarg(0),
- x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.Il2CppObjectBaseToPtr)),
- x => x.MatchLdsfld(out _),
- x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.il2cpp_object_get_virtual_method))))
- {
- cursor.RemoveRange(4);
- }
- else
- {
- cursor.Goto(0)
- .GotoNext(x => x.MatchLdsfld(UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original)))
- .Remove();
- }
-
- cursor
- .Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, ((IntPtr)modifiedNativeMethodInfo).ToInt64())
- .Emit(Mono.Cecil.Cil.OpCodes.Conv_I);
- return dmd;
- }
-
-
-
-
-
-
-
-
- public static void TryResolve(object sender, PatchManager.PatcherResolverEventArgs args)
- {
- if (args.Original.DeclaringType?.IsSubclassOf(typeof(Il2CppObjectBase)) == true)
- args.MethodPatcher = new IL2CPPDetourMethodPatcher(args.Original);
- }
- private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo targetManagedMethodInfo)
- {
-
-
- var managedParams = Original.GetParameters().Select(x => x.ParameterType).ToArray();
- var unmanagedParams = new Type[managedParams.Length + 2];
-
- unmanagedParams[0] = typeof(IntPtr);
- unmanagedParams[unmanagedParams.Length - 1] = typeof(Il2CppMethodInfo*);
- Array.Copy(managedParams.Select(ConvertManagedTypeToIL2CPPType).ToArray(), 0,
- unmanagedParams, 1, managedParams.Length);
- var managedReturnType = AccessTools.GetReturnedType(Original);
- var unmanagedReturnType = ConvertManagedTypeToIL2CPPType(managedReturnType);
- var dmd = new DynamicMethodDefinition("(il2cpp -> managed) " + Original.Name,
- unmanagedReturnType,
- unmanagedParams
- );
- var il = dmd.GetILGenerator();
- il.BeginExceptionBlock();
-
-
- LocalBuilder[] indirectVariables = new LocalBuilder[managedParams.Length];
- if (!Original.IsStatic)
- {
-
- il.Emit(OpCodes.Ldarg_0);
- EmitConvertArgumentToManaged(il, Original.DeclaringType, out _);
- }
- for (int i = 0; i < managedParams.Length; ++i)
- {
- il.Emit(OpCodes.Ldarg_S, i + 1);
- EmitConvertArgumentToManaged(il, managedParams[i], out indirectVariables[i]);
- }
-
- il.Emit(OpCodes.Call, targetManagedMethodInfo);
-
- LocalBuilder managedReturnVariable = null;
- if (managedReturnType != typeof(void))
- {
- managedReturnVariable = il.DeclareLocal(managedReturnType);
- il.Emit(OpCodes.Stloc, managedReturnVariable);
- }
-
- for (int i = 0; i < managedParams.Length; ++i)
- {
- if (indirectVariables[i] == null)
- continue;
- il.Emit(OpCodes.Ldarg_S, i + 1);
- il.Emit(OpCodes.Ldloc, indirectVariables[i]);
- EmitConvertManagedTypeToIL2CPP(il, managedParams[i].GetElementType());
- il.Emit(OpCodes.Stind_I);
- }
-
- il.BeginCatchBlock(typeof(Exception));
- il.Emit(OpCodes.Call, ReportExceptionMethodInfo);
- il.EndExceptionBlock();
-
- if (managedReturnVariable != null)
- {
- il.Emit(OpCodes.Ldloc, managedReturnVariable);
- EmitConvertManagedTypeToIL2CPP(il, managedReturnType);
- }
- il.Emit(OpCodes.Ret);
- return dmd;
- }
- private static void ReportException(Exception ex)
- {
- DetourLogger.LogError(ex.ToString());
- }
- private static Type ConvertManagedTypeToIL2CPPType(Type managedType)
- {
- if (managedType.IsByRef)
- {
- Type directType = managedType.GetElementType();
- if (directType == typeof(string) || directType.IsSubclassOf(typeof(Il2CppObjectBase)))
- {
- return typeof(IntPtr*);
- }
- }
- else if (managedType == typeof(string) || managedType.IsSubclassOf(typeof(Il2CppObjectBase)))
- {
- return typeof(IntPtr);
- }
- return managedType;
- }
- 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, Type managedParamType, out LocalBuilder variable)
- {
- variable = null;
- if (managedParamType.IsValueType)
- return;
- void EmitCreateIl2CppObject()
- {
- Label endLabel = il.DefineLabel();
- Label notNullLabel = il.DefineLabel();
- il.Emit(OpCodes.Dup);
- il.Emit(OpCodes.Brtrue_S, notNullLabel);
- il.Emit(OpCodes.Pop);
- il.Emit(OpCodes.Ldnull);
- il.Emit(OpCodes.Br_S, endLabel);
- il.MarkLabel(notNullLabel);
- il.Emit(OpCodes.Newobj, AccessTools.DeclaredConstructor(managedParamType, new[] { typeof(IntPtr) }));
- il.MarkLabel(endLabel);
- }
- void HandleTypeConversion(Type originalType)
- {
- if (originalType == typeof(string))
- {
- il.Emit(OpCodes.Call, IL2CPPToManagedStringMethodInfo);
- }
- else if (originalType.IsSubclassOf(typeof(Il2CppObjectBase)))
- {
- EmitCreateIl2CppObject();
- }
- }
- if (managedParamType.IsByRef)
- {
- Type directType = managedParamType.GetElementType();
- variable = il.DeclareLocal(directType);
- il.Emit(OpCodes.Ldind_I);
- HandleTypeConversion(directType);
- il.Emit(OpCodes.Stloc, variable);
- il.Emit(OpCodes.Ldloca, variable);
- }
- else
- {
- HandleTypeConversion(managedParamType);
- }
- }
- }
- }
|