public NativeDetour(MethodBase method, IntPtr from, IntPtr to, ref NativeDetourConfig config) { Method = method; if (!(OnDetour?.InvokeWhileTrue(this, method, from, to) ?? true)) { return; } IsValid = true; Data = DetourHelper.Native.Create(from, to, null); // Backing up the original function only needs to happen once. if (!config.SkipILCopy) { method?.TryCreateILCopy(out _BackupMethod); } // BackupNative is required even if BackupMethod is present to undo the detour. _BackupNative = DetourHelper.Native.MemAlloc(Data.Size); if (!config.ManualApply) { Apply(); } }
public void Apply(NativeDetourData detour) { int offs = 0; // TODO: Find possibly better ARM detours. switch ((DetourSize)detour.Size) { case DetourSize.Arm32: // LDR PC, [PC, #-4] detour.Method.Write(ref offs, (byte)0x04); detour.Method.Write(ref offs, (byte)0xF0); detour.Method.Write(ref offs, (byte)0x1F); detour.Method.Write(ref offs, (byte)0xE5); detour.Method.Write(ref offs, (uint)detour.Target); break; case DetourSize.Arm64: // LDR PC, [PC, #-4] detour.Method.Write(ref offs, (byte)0x04); detour.Method.Write(ref offs, (byte)0xF0); detour.Method.Write(ref offs, (byte)0x1F); detour.Method.Write(ref offs, (byte)0xE5); detour.Method.Write(ref offs, (ulong)detour.Target); break; default: throw new NotSupportedException($"Unknown ARM detour size {detour.Size}"); } }
/// <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 NativeDetour(MethodBase method, IntPtr from, IntPtr to, ref NativeDetourConfig config) { if (from == to) { throw new InvalidOperationException($"Cannot detour from a location to itself! (from: {from:X16} to: {to:X16} method: {method})"); } method = method?.GetIdentifiable(); Method = method; if (!(OnDetour?.InvokeWhileTrue(this, method, from, to) ?? true)) { return; } IsValid = true; _Data = DetourHelper.Native.Create(from, to, null); // Backing up the original function only needs to happen once. if (!config.SkipILCopy) { method?.TryCreateILCopy(out _BackupMethod); } // BackupNative is required even if BackupMethod is present to undo the detour. _BackupNative = DetourHelper.Native.MemAlloc(_Data.Size); if (!config.ManualApply) { Apply(); } }
public void Apply(NativeDetourData detour) { int offs = 0; if (Is64BitTarget(detour.Target)) { // PUSH can only push 32-bit values and MOV RAX, <to>; JMP RAX voids RAX. // Registerless JMP [rip+0] + data "absolute jump." // JMP [rip+0] detour.Method.Write(ref offs, (byte)0xFF); detour.Method.Write(ref offs, (byte)0x25); detour.Method.Write(ref offs, (uint)0x00000000); // <to> detour.Method.Write(ref offs, (ulong)detour.Target); return; } // Registerless PUSH + RET "absolute jump." // PUSH <to> detour.Method.Write(ref offs, (byte)0x68); detour.Method.Write(ref offs, (uint)detour.Target); // RET detour.Method.Write(ref offs, (byte)0xC3); }
public DetourRuntimeILPlatform() { // Perform a selftest if this runtime requires special handling for instance methods returning structs. // This is documented behavior for coreclr, but can implicitly affect all other runtimes (including mono!) as well. // Specifically, this should affect all __thiscalls MethodInfo selftest = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetStruct", BindingFlags.NonPublic | BindingFlags.Instance); Pin(selftest); MethodInfo selftestHook = typeof(DetourRuntimeILPlatform).GetMethod("_SelftestGetStructHook", BindingFlags.NonPublic | BindingFlags.Static); Pin(selftestHook); NativeDetourData detour = DetourHelper.Native.Create( GetNativeStart(selftest), GetNativeStart(selftestHook) ); DetourHelper.Native.MakeWritable(detour); DetourHelper.Native.Apply(detour); DetourHelper.Native.MakeExecutable(detour); DetourHelper.Native.Free(detour); // No need to undo the detour. _SelftestStruct s; // Make sure that the selftest isn't optimized away. try { } finally { s = _SelftestGetStruct(IntPtr.Zero, IntPtr.Zero); unsafe { *&s = s; } } }
public NativeDetourData Create(IntPtr from, IntPtr to) { NativeDetourData detour = new NativeDetourData { Method = from, Target = to }; detour.Size = (int)GetDetourSize(from, to); return(detour); }
public NativeDetourData Create(IntPtr from, IntPtr to) { NativeDetourData detour = new NativeDetourData { Method = from, Target = to }; detour.Size = (int)(IntPtr.Size == 4 ? DetourSize.Arm32 : DetourSize.Arm64); return(detour); }
public void MakeExecutable(NativeDetourData detour) { Protection oldProtection; if (!VirtualProtect(detour.Method, (IntPtr)detour.Size, Protection.PAGE_EXECUTE_READWRITE, out oldProtection)) { throw new System.ComponentModel.Win32Exception(); } Inner.MakeExecutable(detour); }
/// <summary> /// Changed the target of this native detour to a new target. /// </summary> /// <param name="newTarget">The new target address.</param> public void ChangeTarget(IntPtr newTarget) { if (!IsValid) { throw new ObjectDisposedException(nameof(NativeDetour)); } NativeDetourData oldData = _Data; _Data = DetourHelper.Native.Create(_Data.Method, newTarget); IsApplied = false; Apply(); DetourHelper.Native.Free(oldData); }
public NativeDetour(MethodBase method, IntPtr from, IntPtr to) { Data = DetourManager.Native.Create(from, to); Method = method; // Backing up the original function only needs to happen once. if (Method != null && Method.GetMethodBody() != null) { _BackupMethod = method.CreateILCopy(); } // BackupNative is required even if BackupMethod is present to undo the detour. _BackupNative = DetourManager.Native.MemAlloc(Data.Size); DetourManager.Native.Copy(Data.Method, _BackupNative, Data.Size); Apply(); }
public NativeDetour(MethodBase method, IntPtr from, IntPtr to) { if (!(OnDetour?.InvokeWhileTrue(this, method, from, to) ?? true)) { return; } Data = DetourHelper.Native.Create(from, to); Method = method; // Backing up the original function only needs to happen once. method?.TryCreateILCopy(out _BackupMethod); // BackupNative is required even if BackupMethod is present to undo the detour. _BackupNative = DetourHelper.Native.MemAlloc(Data.Size); DetourHelper.Native.Copy(Data.Method, _BackupNative, Data.Size); Apply(); }
/// <summary> /// Emit a call to DetourManager.Native.Apply using a copy of the given data. /// </summary> public static void EmitDetourApply(this ILGenerator il, NativeDetourData data) { // Load NativePlatform instance. il.Emit(OpCodes.Ldsfld, _Native); // Fill stack with data values. il.Emit(OpCodes.Ldc_I8, (long)data.Method); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Ldc_I8, (long)data.Target); il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Ldc_I4, data.Size); il.Emit(OpCodes.Ldc_I8, (long)data.Extra); il.Emit(OpCodes.Conv_I); // Put values in stack into NativeDetourData. il.Emit(OpCodes.Call, _ToNativeDetourData); // Apply. il.Emit(OpCodes.Callvirt, _Apply); }
public void Apply(NativeDetourData detour) { int offs = 0; // Console.WriteLine($"Detour {((ulong) detour.Method):X16} -> {((ulong) detour.Target):X16}, {((DetourSize) detour.Size)}"); switch ((DetourSize)detour.Size) { case DetourSize.Rel32: // JMP DeltaNextInstr detour.Method.Write(ref offs, (byte)0xE9); detour.Method.Write(ref offs, (uint)(int)( (long)detour.Target - ((long)detour.Method + offs + sizeof(uint)) )); break; case DetourSize.Abs32: // Registerless PUSH + RET "absolute jump." // PUSH <to> detour.Method.Write(ref offs, (byte)0x68); detour.Method.Write(ref offs, (uint)detour.Target); // RET detour.Method.Write(ref offs, (byte)0xC3); break; case DetourSize.Abs64: // PUSH can only push 32-bit values and MOV RAX, <to>; JMP RAX voids RAX. // Registerless JMP [rip+0] + data "absolute jump." // JMP [rip+0] detour.Method.Write(ref offs, (byte)0xFF); detour.Method.Write(ref offs, (byte)0x25); detour.Method.Write(ref offs, (uint)0x00000000); // <to> detour.Method.Write(ref offs, (ulong)detour.Target); break; default: throw new NotSupportedException($"Unknown X86 detour size {detour.Size}"); } }
public NativeDetour(MethodBase method, IntPtr from, IntPtr to) { Data = DetourManager.Native.Create(from, to); Method = method; // Backing up the original function only needs to happen once. MethodBody body; try { body = Method?.GetMethodBody(); } catch (InvalidOperationException) { body = null; } if (body != null) { _BackupMethod = method.CreateILCopy(); } // BackupNative is required even if BackupMethod is present to undo the detour. _BackupNative = DetourManager.Native.MemAlloc(Data.Size); DetourManager.Native.Copy(Data.Method, _BackupNative, Data.Size); Apply(); }
public Hook(MethodBase from, MethodInfo to, object target) { Method = from; _Hook = to; if (_Hook.ReturnType != ((from as MethodInfo)?.ReturnType ?? typeof(void))) { throw new InvalidOperationException($"Return type of hook for {from} doesn't match, must be {((from as MethodInfo)?.ReturnType ?? typeof(void)).FullName}"); } if (target == null && !to.IsStatic) { throw new InvalidOperationException($"Hook for method {from} must be static, or you must pass a target instance."); } ParameterInfo[] hookArgs = _Hook.GetParameters(); // Check if the parameters match. // If the delegate has got an extra first parameter that itself is a delegate, it's the orig trampoline passthrough. 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; } } Type origType = null; if (hookArgs.Length == argTypes.Length + 1 && typeof(Delegate).IsAssignableFrom(hookArgs[0].ParameterType)) { origType = hookArgs[0].ParameterType; } else if (hookArgs.Length != argTypes.Length) { throw new InvalidOperationException($"Parameter count of hook for {from} doesn't match, must be {argTypes.Length}"); } for (int i = 0; i < argTypes.Length; i++) { Type argMethod = argTypes[i]; Type argHook = hookArgs[i + (origType == null ? 0 : 1)].ParameterType; if (!argMethod.IsAssignableFrom(argHook) && !argHook.IsAssignableFrom(argMethod)) { throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related"); } } MethodInfo origInvoke = origType?.GetMethod("Invoke"); // TODO: Check origType Invoke arguments. DynamicMethod dm; ILGenerator il; DynamicMethod trampoline = null; if (origType != null) { trampoline = new DynamicMethod( $"trampoline_{Method.Name}_{GetHashCode()}", _Hook.ReturnType, argTypes, Method.DeclaringType, false // Otherwise just ret is invalid for whatever reason. ).StubCriticalDetour().Pin(); } dm = new DynamicMethod( $"hook_{Method.Name}_{GetHashCode()}", (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes, Method.DeclaringType, true ); il = dm.GetILGenerator(); if (target != null) { _RefTarget = il.EmitReference(target); } if (trampoline != null) { _RefTrampoline = il.EmitReference(trampoline.CreateDelegate(origType)); } // TODO: Use specialized Ldarg.* if possible; What about ref types? for (int i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, _Hook); il.Emit(OpCodes.Ret); Target = dm.Pin(); _Detour = new Detour(Method, Target); if (trampoline != null) { NativeDetourData link = DetourManager.Native.Create( trampoline.GetNativeStart(), GenerateTrampoline(origInvoke).GetNativeStart() ); DetourManager.Native.MakeWritable(link); DetourManager.Native.Apply(link); DetourManager.Native.MakeExecutable(link); DetourManager.Native.Free(link); } }
public void Free(NativeDetourData detour) { // No extra data. }
public void MakeWritable(NativeDetourData detour) { // no-op. }
public void MakeExecutable(NativeDetourData detour) { // no-op. }
public void Apply(NativeDetourData detour) { Inner.Apply(detour); }
public void Free(NativeDetourData detour) { Inner.Free(detour); }
public void Free(NativeDetourData detour) { throw new NotImplementedException(); }
public void MakeWritable(NativeDetourData detour) { throw new NotImplementedException(); }