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), null ); 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; } } }
readonly NativeDetourData m_detour; // Detour data public Hook(Delegate target, Delegate hook) { // Cannot hook a function with a different signature if (target.GetType() != hook.GetType()) { Valid = false; return; } // Literally don't understand wtf this does, but it does something good i think... RuntimeHelpers.PrepareMethod(hook.Method.MethodHandle); RuntimeHelpers.PrepareMethod(target.Method.MethodHandle); // Get pointers to the functions m_targetPtr = target.Method.MethodHandle.GetFunctionPointer(); var hookPtr = hook.Method.MethodHandle.GetFunctionPointer(); // Create a native detour. Writes a trampoline to the method at m_targetPtr, and makes execution jump directly to hookPtr m_detour = DetourHelper.Native.Create(m_targetPtr, hookPtr); // Backup the original assembly code m_original = new byte[m_detour.Size]; Marshal.Copy(m_targetPtr, m_original, 0, m_original.Length); // Success! We have a valid hook Valid = true; }
public void MakeExecutable(NativeDetourData detour) { if (!VirtualProtect(detour.Method, (IntPtr)detour.Size, Protection.PAGE_EXECUTE_READWRITE, out Protection oldProtection)) { throw new System.ComponentModel.Win32Exception(); } Inner.MakeExecutable(detour); }
private static NativeDetourData CreateNativeTrampolineTo(IntPtr target) { IntPtr mem = DetourHelper.Native.MemAlloc(64); // 64 bytes should be enough on all platforms NativeDetourData data = DetourHelper.Native.Create(mem, target); DetourHelper.Native.MakeWritable(data); DetourHelper.Native.Apply(data); DetourHelper.Native.MakeExecutable(data); DetourHelper.Native.FlushICache(data); return data; }
public void MakeWritable(NativeDetourData detour) { // PAGE_READWRITE causes an AccessViolationException / TargetInvocationException. if (!VirtualProtect(detour.Method, (IntPtr)detour.Size, Protection.PAGE_EXECUTE_READWRITE, out Protection oldProtection)) { throw new System.ComponentModel.Win32Exception(); } Inner.MakeWritable(detour); }
public NativeDetourData Create(IntPtr from, IntPtr to, byte?type) { NativeDetourData detour = new NativeDetourData { Method = (IntPtr)((long)from & ~0x1), Target = (IntPtr)((long)to & ~0x1) }; detour.Size = DetourSizes[detour.Type = type ?? (byte)GetDetourType(from, to)]; // Console.WriteLine($"{nameof(DetourNativeARMPlatform)} create: {(DetourType) detour.Type} 0x{detour.Method.ToString("X16")} + 0x{detour.Size.ToString("X8")} -> 0x{detour.Target.ToString("X16")}"); return(detour); }
private void _HookSelftest(MethodInfo from, MethodInfo to) { Pin(from); Pin(to); NativeDetourData detour = DetourHelper.Native.Create( GetNativeStart(from), GetNativeStart(to), null ); DetourHelper.Native.MakeWritable(detour); DetourHelper.Native.Apply(detour); DetourHelper.Native.MakeExecutable(detour); DetourHelper.Native.FlushICache(detour); DetourHelper.Native.Free(detour); // No need to undo the detour. }
public void Apply(NativeDetourData detour) { int offs = 0; // Console.WriteLine($"{nameof(DetourNativeX86Platform)} apply: {(DetourType) detour.Type} 0x{detour.Method.ToString("X16")} -> 0x{detour.Target.ToString("X16")}"); switch ((DetourType)detour.Type) { case DetourType.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 DetourType.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 DetourType.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 detour type {detour.Type}"); } }
protected override unsafe void InstallJitHooks(IntPtr jit) { SetupJitHookHelpers(); real_compileMethod = GetCompileMethod(jit); our_compileMethod = CompileMethodHook; IntPtr our_compileMethodPtr = Marshal.GetFunctionPointerForDelegate(our_compileMethod); // Create a native trampoline to pre-JIT the hook itself { NativeDetourData trampolineData = CreateNativeTrampolineTo(our_compileMethodPtr); d_compileMethod trampoline = trampolineData.Method.AsDelegate<d_compileMethod>(); trampoline(IntPtr.Zero, IntPtr.Zero, new CORINFO_METHOD_INFO(), 0, out _, out _); FreeNativeTrampoline(trampolineData); } // Make sure we run the cctor before the hook to avoid wierdness _ = hookEntrancy; // Install the JIT hook IntPtr* vtableEntry = GetVTableEntry(jit, VTableIndex_ICorJitCompiler_compileMethod); DetourHelper.Native.MakeWritable((IntPtr) vtableEntry, (uint)IntPtr.Size); real_compileMethodPtr = *vtableEntry; *vtableEntry = our_compileMethodPtr; }
public void Apply(NativeDetourData detour) { Inner.Apply(detour); }
public void Free(NativeDetourData detour) { Inner.Free(detour); }
public void MakeExecutable(NativeDetourData detour) { // RWX because old versions of mono always use RWX. SetMemPerms(detour.Method, detour.Size, MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC); Inner.MakeExecutable(detour); }
public void Apply(NativeDetourData detour) { int offs = 0; // Console.WriteLine($"{nameof(DetourNativeARMPlatform)} apply: {(DetourType) detour.Type} 0x{detour.Method.ToString("X16")} -> 0x{detour.Target.ToString("X16")}"); switch ((DetourType)detour.Type) { case DetourType.Thumb: // Note: PC is 4 bytes ahead // LDR.W PC, [PC, #0] detour.Method.Write(ref offs, (byte)0xDF); detour.Method.Write(ref offs, (byte)0xF8); detour.Method.Write(ref offs, (byte)0x00); detour.Method.Write(ref offs, (byte)0xF0); // <to> | 0x1 (-> Thumb) detour.Method.Write(ref offs, (uint)detour.Target | 0x1); break; case DetourType.ThumbBX: // FIXME: This fails on dotnet for arm running on aarch64. // Burn a register to stay safe. // Note: PC is 4 bytes ahead // LDR.W R10, [PC, #4] detour.Method.Write(ref offs, (byte)0xDF); detour.Method.Write(ref offs, (byte)0xF8); detour.Method.Write(ref offs, (byte)0x04); detour.Method.Write(ref offs, (byte)0xA0); // BX R10 detour.Method.Write(ref offs, (byte)0x50); detour.Method.Write(ref offs, (byte)0x47); // NOP detour.Method.Write(ref offs, (byte)0x00); detour.Method.Write(ref offs, (byte)0xBF); // <to> | 0x0 (-> ARM) detour.Method.Write(ref offs, (uint)detour.Target | 0x0); break; case DetourType.AArch32: // FIXME: This was never tested. // Note: PC is 8 bytes ahead // 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); // <to> | 0x0 (-> ARM) detour.Method.Write(ref offs, (uint)detour.Target | 0x0); break; case DetourType.AArch32BX: // FIXME: This was never tested. // Burn a register. Required to use BX to change state. // Note: PC is 4 bytes ahead // LDR R8, [PC, #0] detour.Method.Write(ref offs, (byte)0x00); detour.Method.Write(ref offs, (byte)0x80); detour.Method.Write(ref offs, (byte)0x9F); detour.Method.Write(ref offs, (byte)0xE5); // BX R8 detour.Method.Write(ref offs, (byte)0x18); detour.Method.Write(ref offs, (byte)0xFF); detour.Method.Write(ref offs, (byte)0x2F); detour.Method.Write(ref offs, (byte)0xE1); // <to> | 0x1 (-> Thumb) detour.Method.Write(ref offs, (uint)detour.Target | 0x1); break; case DetourType.AArch64: // PC isn't available on arm64. // We need to burn a register and branch instead. // LDR X15, .+8 detour.Method.Write(ref offs, (byte)0x4F); detour.Method.Write(ref offs, (byte)0x00); detour.Method.Write(ref offs, (byte)0x00); detour.Method.Write(ref offs, (byte)0x58); // BR X15 detour.Method.Write(ref offs, (byte)0xE0); detour.Method.Write(ref offs, (byte)0x01); detour.Method.Write(ref offs, (byte)0x1F); detour.Method.Write(ref offs, (byte)0xD6); // <to> detour.Method.Write(ref offs, (ulong)detour.Target); break; default: throw new NotSupportedException($"Unknown detour type {detour.Type}"); } }
public void MakeExecutable(NativeDetourData detour) { // no-op. }
public void MakeWritable(NativeDetourData detour) { // no-op. }
private static void FreeNativeTrampoline(NativeDetourData data) { DetourHelper.Native.MakeWritable(data); DetourHelper.Native.MemFree(data.Method); DetourHelper.Native.Free(data); }
public void Free(NativeDetourData detour) { // No extra data. }