예제 #1
0
 public Hook(MethodBase method, IntPtr to)
     : this(method, DetourHelper.GenerateNativeProxy(to, method), null)
 {
 }
예제 #2
0
 public Hook(MethodBase method, IntPtr to, HookConfig config)
     : this(method, DetourHelper.GenerateNativeProxy(to, method), null, ref config)
 {
 }
예제 #3
0
        /// <summary>
        /// Generate a new DynamicMethod with which you can invoke the previous state.
        /// If the NativeDetour holds a reference to a managed method, a copy of the original method is returned.
        /// If the NativeDetour holds a reference to a native function, an "undo-call-redo" trampoline with a matching signature is returned.
        /// </summary>
        public MethodBase GenerateTrampoline(MethodBase signature = null)
        {
            MethodBase remoteTrampoline = OnGenerateTrampoline?.InvokeWhileNull <MethodBase>(this, signature);

            if (remoteTrampoline != null)
            {
                return(remoteTrampoline);
            }

            if (_IsFree)
            {
                throw new InvalidOperationException("Free() has been called on this detour.");
            }

            if (_BackupMethod != null)
            {
                // If we're detouring an IL method and have an IL copy, invoke the IL copy.
                // Note that this ignores the passed signature.
                return(_BackupMethod);
            }

            /*
             * if (signature == null)
             *  signature = _BackupMethod;
             */
            if (signature == null)
            {
                throw new ArgumentNullException("A signature must be given if the NativeDetour doesn't hold a reference to a managed method.");
            }

            // Otherwise, undo the detour, call the method and reapply the detour.

            MethodBase methodCallable = Method;

            if (methodCallable == null)
            {
                methodCallable = DetourHelper.GenerateNativeProxy(Data.Method, 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;
            }

            using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(
                       $"Trampoline:Native<{Method?.GetFindableID(simple: true) ?? ((long) Data.Method).ToString("X16")}>?{GetHashCode()}",
                       returnType, argTypes
                       )) {
                ILProcessor il = dmd.GetILProcessor();

                ExceptionHandler eh = new ExceptionHandler(ExceptionHandlerType.Finally);
                il.Body.ExceptionHandlers.Add(eh);

                il.EmitDetourCopy(_BackupNative, Data.Method, Data.Type);

                // Store the return value in a local as we can't preserve the stack across exception block boundaries.
                VariableDefinition localResult = null;
                if (returnType != typeof(void))
                {
                    il.Body.Variables.Add(localResult = new VariableDefinition(il.Import(returnType)));
                }

                // Label blockTry = il.BeginExceptionBlock();
                int instriTryStart = il.Body.Instructions.Count;

                for (int i = 0; i < argTypes.Length; i++)
                {
                    il.Emit(OpCodes.Ldarg, i);
                }

                if (methodCallable is MethodInfo)
                {
                    il.Emit(OpCodes.Call, (MethodInfo)methodCallable);
                }
                else if (methodCallable is ConstructorInfo)
                {
                    il.Emit(OpCodes.Call, (ConstructorInfo)methodCallable);
                }
                else
                {
                    throw new NotSupportedException($"Method type {methodCallable.GetType().FullName} not supported.");
                }

                if (localResult != null)
                {
                    il.Emit(OpCodes.Stloc, localResult);
                }

                il.Emit(OpCodes.Leave, (object)null);
                Instruction instrLeave = il.Body.Instructions[il.Body.Instructions.Count - 1];

                // il.BeginFinallyBlock();
                int instriTryEnd       = il.Body.Instructions.Count;
                int instriFinallyStart = il.Body.Instructions.Count;

                // Reapply the detour even if the method threw an exception.
                il.EmitDetourApply(Data);

                // il.EndExceptionBlock();
                int instriFinallyEnd = il.Body.Instructions.Count;

                Instruction instrLeaveTarget = null;

                if (localResult != null)
                {
                    il.Emit(OpCodes.Ldloc, localResult);
                    instrLeaveTarget = il.Body.Instructions[il.Body.Instructions.Count - 1];
                }

                il.Emit(OpCodes.Ret);
                instrLeaveTarget   = instrLeaveTarget ?? il.Body.Instructions[il.Body.Instructions.Count - 1];
                instrLeave.Operand = instrLeaveTarget;

                // TODO: Are the exception handler indices correct?
                eh.TryStart     = il.Body.Instructions[instriTryStart];
                eh.TryEnd       = il.Body.Instructions[instriTryEnd];
                eh.HandlerStart = il.Body.Instructions[instriTryEnd];
                eh.HandlerEnd   = il.Body.Instructions[instriFinallyEnd];

                return(dmd.Generate().Pin());
            }
        }
예제 #4
0
        /// <summary>
        /// Generate a new DynamicMethod with which you can invoke the previous state.
        /// If the NativeDetour holds a reference to a managed method, a copy of the original method is returned.
        /// If the NativeDetour holds a reference to a native function, an "undo-call-redo" trampoline with a matching signature is returned.
        /// </summary>
        public MethodBase GenerateTrampoline(MethodBase signature = null)
        {
            MethodBase remoteTrampoline = OnGenerateTrampoline?.InvokeWhileNull <MethodBase>(this, signature);

            if (remoteTrampoline != null)
            {
                return(remoteTrampoline);
            }

            if (_IsFree)
            {
                throw new InvalidOperationException("Free() has been called on this detour.");
            }

            if (_BackupMethod != null)
            {
                // If we're detouring an IL method and have an IL copy, invoke the IL copy.
                // Note that this ignores the passed signature.
                return(_BackupMethod);
            }

            if (signature == null)
            {
                signature = _BackupMethod;
            }
            if (signature == null)
            {
                throw new ArgumentNullException("A signature must be given if the NativeDetour doesn't hold a reference to a managed method.");
            }

            // Otherwise, undo the detour, call the method and reapply the detour.

            MethodBase methodCallable = Method;

            if (methodCallable == null)
            {
                methodCallable = DetourHelper.GenerateNativeProxy(Data.Method, 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;
            string        name = $"trampoline_native_{Method?.Name.ToString() ?? ((long) Data.Method).ToString("X16")}_{GetHashCode()}";

            if (Method != null)
            {
                dm = new DynamicMethod(
                    name,
                    returnType, argTypes,
                    Method.DeclaringType,
                    true
                    );
            }
            else
            {
                dm = new DynamicMethod(
                    name,
                    returnType, argTypes,
                    true
                    );
            }
            ILGenerator il = dm.GetILGenerator();

            il.EmitDetourCopy(_BackupNative, Data.Method, Data.Size);

            // Store the return value in a local as we can't preserve the stack across exception block boundaries.
            LocalBuilder localResult = null;

            if (returnType != typeof(void))
            {
                localResult = il.DeclareLocal(returnType);
            }

            Label blockTry = il.BeginExceptionBlock();

            // TODO: Use specialized Ldarg.* if possible; What about ref types?
            for (int i = 0; i < argTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i);
            }

            if (methodCallable is MethodInfo)
            {
                il.Emit(OpCodes.Call, (MethodInfo)methodCallable);
            }
            else if (methodCallable is ConstructorInfo)
            {
                il.Emit(OpCodes.Call, (ConstructorInfo)methodCallable);
            }
            else
            {
                throw new NotSupportedException($"Method type {methodCallable.GetType().FullName} not supported.");
            }

            if (localResult != null)
            {
                il.Emit(OpCodes.Stloc_0);
            }

            il.BeginFinallyBlock();

            // Reapply the detour even if the method threw an exception.
            il.EmitDetourApply(Data);

            il.EndExceptionBlock();

            if (localResult != null)
            {
                il.Emit(OpCodes.Ldloc_0);
            }

            il.Emit(OpCodes.Ret);

            return(dm.Pin());
        }
예제 #5
0
 public Detour(MethodBase method, IntPtr to)
     : this(method, DetourHelper.GenerateNativeProxy(to, method))
 {
 }