Beispiel #1
0
        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;
                }
            }
        }
Beispiel #2
0
    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;
    }
Beispiel #3
0
        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;
 }
Beispiel #5
0
        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);
        }
Beispiel #7
0
 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;
        }
Beispiel #10
0
 public void Apply(NativeDetourData detour)
 {
     Inner.Apply(detour);
 }
Beispiel #11
0
 public void Free(NativeDetourData detour)
 {
     Inner.Free(detour);
 }
Beispiel #12
0
 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);
 }
Beispiel #13
0
        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}");
            }
        }
Beispiel #14
0
 public void MakeExecutable(NativeDetourData detour)
 {
     // no-op.
 }
Beispiel #15
0
 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);
 }
Beispiel #17
0
 public void Free(NativeDetourData detour)
 {
     // No extra data.
 }