Esempio n. 1
0
        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}");
            }
        }
Esempio n. 3
0
        /// <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());
        }
Esempio n. 4
0
        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();
            }
        }
Esempio n. 5
0
        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;
                }
            }
        }
Esempio n. 7
0
        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);
        }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
        /// <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);
        }
Esempio n. 11
0
        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();
        }
Esempio n. 12
0
        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();
        }
Esempio n. 13
0
        /// <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}");
            }
        }
Esempio n. 15
0
        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();
        }
Esempio n. 16
0
        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);
            }
        }
Esempio n. 17
0
 public void Free(NativeDetourData detour)
 {
     // No extra data.
 }
Esempio n. 18
0
 public void MakeWritable(NativeDetourData detour)
 {
     // no-op.
 }
Esempio n. 19
0
 public void MakeExecutable(NativeDetourData detour)
 {
     // no-op.
 }
Esempio n. 20
0
 public void Apply(NativeDetourData detour)
 {
     Inner.Apply(detour);
 }
Esempio n. 21
0
 public void Free(NativeDetourData detour)
 {
     Inner.Free(detour);
 }
Esempio n. 22
0
 public void Free(NativeDetourData detour)
 {
     throw new NotImplementedException();
 }
Esempio n. 23
0
 public void MakeWritable(NativeDetourData detour)
 {
     throw new NotImplementedException();
 }