/// <summary> /// Generate a DynamicMethod to easily call the given native function from another DynamicMethod. /// </summary> /// <param name="target">The pointer to the native function to call.</param> /// <param name="signature">A MethodBase with the target function's signature.</param> /// <returns>The detoured DynamicMethod.</returns> public static DynamicMethod GenerateNativeProxy(IntPtr target, MethodBase signature) { Type returnType = (signature as MethodInfo)?.ReturnType ?? typeof(void); ParameterInfo[] args = signature.GetParameters(); Type[] argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } DynamicMethod dm = new DynamicMethod( $"native_{((long) target).ToString("X16")}", returnType, argTypes, true ).StubCriticalDetour(); // Detour the new DynamicMethod into the target. NativeDetourData detour = Native.Create(dm.GetNativeStart(), target); Native.MakeWritable(detour); Native.Apply(detour); Native.MakeExecutable(detour); Native.Free(detour); return(dm.Pin()); }
public Detour(MethodBase from, MethodBase to) { Method = from; Target = to; // TODO: Check target method arguments. if (!_BackupMethods.ContainsKey(Method)) { _BackupMethods[Method] = Method.CreateILCopy(); } // Generate a "chained trampoline" DynamicMethod. ParameterInfo[] args = Method.GetParameters(); Type[] argTypes; if (!Method.IsStatic) { argTypes = new Type[args.Length + 1]; argTypes[0] = Method.DeclaringType; for (int i = 0; i < args.Length; i++) { argTypes[i + 1] = args[i].ParameterType; } } else { argTypes = new Type[args.Length]; for (int i = 0; i < args.Length; i++) { argTypes[i] = args[i].ParameterType; } } _ChainedTrampoline = new DynamicMethod( $"chain_{Method.Name}_{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes, Method.DeclaringType, false // Otherwise just ret is invalid for whatever reason. ).StubCriticalDetour().Pin(); // Add the detour to the detour map. List <Detour> detours; lock (_DetourMap) { if (!_DetourMap.TryGetValue(Method, out detours)) { _DetourMap[Method] = detours = new List <Detour>(); } } lock (detours) { // New Detour instances are always on the top. if (detours.Count > 0) { detours[detours.Count - 1]._TopUndo(); } _TopApply(); // Do the initial "chained trampoline" setup. NativeDetourData link; if (detours.Count > 0) { // If a previous Detour exists in the chain, detour our "chained trampoline" to it, link = DetourManager.Native.Create( _ChainedTrampoline.GetNativeStart(), detours[detours.Count - 1].Target.GetNativeStart() ); } else { // If this is the first Detour in the chain, detour our "chained trampoline" to the original method. link = DetourManager.Native.Create( _ChainedTrampoline.GetNativeStart(), _BackupMethods[Method].GetNativeStart() ); } DetourManager.Native.MakeWritable(link); DetourManager.Native.Apply(link); DetourManager.Native.MakeExecutable(link); DetourManager.Native.Free(link); detours.Add(this); } }