Ejemplo n.º 1
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());
        }
Ejemplo n.º 2
0
        public DynamicMethod CreateCopy(MethodBase method)
        {
            MethodBody body = method.GetMethodBody();

            if (body == null)
            {
                throw new InvalidOperationException("P/Invoke methods cannot be copied!");
            }

            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;
                }
            }

            DynamicMethod dm = new DynamicMethod(
                $"orig_{method.Name}",
                // method.Attributes, method.CallingConvention, // DynamicMethod only supports public, static and standard
                (method as MethodInfo)?.ReturnType ?? typeof(void), argTypes,
                method.DeclaringType,
                false
                );

            ILGenerator il = dm.GetILGenerator();

            // TODO: Move away from using Harmony's ILCopying code in MonoMod...
            List <Label> endLabels = new List <Label>();
            List <Harmony.ILCopying.ExceptionBlock> endBlocks = new List <Harmony.ILCopying.ExceptionBlock>();

            Harmony.ILCopying.MethodCopier copier = new Harmony.ILCopying.MethodCopier(method, il);
            copier.Finalize(endLabels, endBlocks);
            foreach (Label label in endLabels)
            {
                il.MarkLabel(label);
            }
            foreach (Harmony.ILCopying.ExceptionBlock block in endBlocks)
            {
                Harmony.ILCopying.Emitter.MarkBlockAfter(il, block);
            }

            return(dm.Pin());
        }
        public DynamicMethod CreateCopy(MethodBase method)
        {
            MethodBody body;

            try {
                body = method.GetMethodBody();
            } catch (InvalidOperationException) {
                body = null;
            }
            if (body == null)
            {
                throw new InvalidOperationException("P/Invoke methods cannot be copied!");
            }

            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;
                }
            }

            DynamicMethod dm = new DynamicMethod(
                $"orig_{method.Name}",
                // method.Attributes, method.CallingConvention, // DynamicMethod only supports public, static and standard
                (method as MethodInfo)?.ReturnType ?? typeof(void), argTypes,
                method.DeclaringType,
                false
                );

            ILGenerator il = dm.GetILGenerator();

            // TODO: Move away from using Harmony's ILCopying code in MonoMod...
            using (Harmony.ILCopying.MethodCopier copier = new Harmony.ILCopying.MethodCopier(method, il)) {
                copier.Copy();
            }

            return(dm.Pin());
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Generate a new DynamicMethod with which you can invoke the previous state.
        /// </summary>
        public MethodBase GenerateTrampoline(MethodBase signature = null)
        {
            MethodBase remoteTrampoline = OnGenerateTrampoline?.InvokeWhileNull <MethodBase>(this, signature);

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

            if (signature == null)
            {
                signature = Target;
            }

            // Note: It'd be more performant to skip this step and just return the "chained trampoline."
            // Unfortunately, it'd allow a third party to break the Detour trampoline chain, among other things.
            // Instead, we create and return a DynamicMethod calling the "chained trampoline."

            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;
            ILGenerator   il;

            dm = new DynamicMethod(
                $"trampoline_{Method.Name}_{GetHashCode()}",
                returnType, argTypes,
                Method.DeclaringType,
                true
                );

            il = dm.GetILGenerator();

            for (int i = 0; i < 10; i++)
            {
                // Prevent old Unity mono from inlining the DynamicMethod.
                il.Emit(OpCodes.Nop);
            }
            il.Emit(OpCodes.Jmp, _ChainedTrampoline);

            return(dm.Pin());
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Emit an inline delegate reference and invocation. Note that the delegates "leak" unless you use HookILCursor.FreeReference(id).
        /// </summary>
        public int EmitDelegate <T>(T cb) where T : Delegate
        {
            Instruction instrPrev = Instr;
            int         id        = EmitDelegatePush(cb);

            // Create a DynamicMethod that shifts the stack around a little.

            Type       delType       = References[id].GetType();
            MethodInfo delInvokeOrig = delType.GetMethod("Invoke");

            ParameterInfo[] args     = delInvokeOrig.GetParameters();
            Type[]          argTypes = new Type[args.Length + 1];
            for (int i = 0; i < args.Length; i++)
            {
                argTypes[i] = args[i].ParameterType;
            }
            argTypes[args.Length] = delType;

            DynamicMethod dmInvoke = new DynamicMethod(
                "HookIL:Invoke:" + delInvokeOrig.DeclaringType.FullName,
                delInvokeOrig.ReturnType, argTypes,
                true // If any random errors pop up, try setting this to false first.
                );
            ILGenerator il = dmInvoke.GetILGenerator();

            // Load the delegate reference first.
            il.Emit(SREmit.OpCodes.Ldarg, args.Length);
            // Load any other arguments on top of that.
            for (int i = 0; i < args.Length; i++)
            {
                il.Emit(SREmit.OpCodes.Ldarg, i);
            }
            // Invoke the delegate and return its result.
            il.Emit(SREmit.OpCodes.Callvirt, delInvokeOrig);
            il.Emit(SREmit.OpCodes.Ret);

            dmInvoke = dmInvoke.Pin();

            // Invoke the DynamicMethod.
            DelegateInvokers[id] = dmInvoke;
            Emit(OpCodes.Call, dmInvoke); // DynamicMethodDefinition should pass it through.

            return(id);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Generate a new DynamicMethod with which you can invoke the previous state.
        /// </summary>
        public MethodBase GenerateTrampoline(MethodBase signature = null)
        {
            if (signature == null)
            {
                signature = Target;
            }

            // Note: It'd be more performant to skip this step and just return the "chained trampoline."
            // Unfortunately, it'd allow a third party to break the Detour trampoline chain, among other things.
            // Instead, we create and return a DynamicMethod calling the "chained trampoline."

            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;
            ILGenerator   il;

            dm = new DynamicMethod(
                $"trampoline_{Method.Name}_{GetHashCode()}",
                returnType, argTypes,
                Method.DeclaringType,
                true
                );

            il = dm.GetILGenerator();

            // 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, _ChainedTrampoline);
            il.Emit(OpCodes.Ret);

            return(dm.Pin());
        }
Ejemplo n.º 7
0
Archivo: Hook.cs Proyecto: qipa/MonoMod
        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;

            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 (origType != null)
            {
                _RefTrampoline = il.EmitReference <Delegate>(null);
            }

            // 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 (origType != null)
            {
                DynamicMethodHelper.SetReference(_RefTrampoline.Value, GenerateTrampoline(origInvoke).CreateDelegate(origType));
            }
        }
Ejemplo n.º 8
0
        public Hook(MethodBase from, MethodInfo to, object target)
        {
            Method = from;
            _Hook  = to;

            if (!(OnDetour?.InvokeWhileTrue(this, from, to, target) ?? true))
            {
                return;
            }

            // Check if hook ret -> method ret is valid. Don't check for method ret -> hook ret, as that's too strict.
            Type returnType = (from as MethodInfo)?.ReturnType ?? typeof(void);

            if (_Hook.ReturnType != returnType && !_Hook.ReturnType.IsCompatible(returnType))
            {
                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.GetThisParamType();
                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))
            {
                _OrigDelegateType = 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.IsCompatible(argHook))
                {
                    throw new InvalidOperationException($"Parameter #{i} of hook for {from} doesn't match, must be {argMethod.FullName} or related");
                }
            }

            MethodInfo origInvoke = _OrigDelegateInvoke = origType?.GetMethod("Invoke");
            // TODO: Check origType Invoke arguments.

            DynamicMethod dm;
            ILGenerator   il;

            dm = new DynamicMethod(
                $"Hook<{Method}>?{GetHashCode()}",
                (Method as MethodInfo)?.ReturnType ?? typeof(void), argTypes,
                Method.DeclaringType,
                true
                );
            il = dm.GetILGenerator();

            if (target != null)
            {
                _RefTarget = il.EmitReference(target);
            }

            if (origType != null)
            {
                _RefTrampoline = il.EmitReference <Delegate>(null);
            }

            // 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();

            // Temporarily provide a trampoline that waits for the proper trampoline.
            if (origType != null)
            {
                ParameterInfo[] origArgs     = origInvoke.GetParameters();
                Type[]          origArgTypes = new Type[origArgs.Length];
                for (int i = 0; i < origArgs.Length; i++)
                {
                    origArgTypes[i] = origArgs[i].ParameterType;
                }

                dm = new DynamicMethod(
                    $"Chain:TMP<{Method}>?{GetHashCode()}",
                    (origInvoke as MethodInfo)?.ReturnType ?? typeof(void), origArgTypes,
                    Method.DeclaringType,
                    true
                    );
                il = dm.GetILGenerator();

                // while (ref == null) { }
                Label lblStart = il.DefineLabel();
                il.MarkLabel(lblStart);
                _RefTrampolineTmp = il.EmitReference <Delegate>(null);
                il.Emit(OpCodes.Brfalse, lblStart);

                // Invoke the generated delegate.
                il.EmitGetReference <Delegate>(_RefTrampolineTmp.Value);

                // 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.Callvirt, origInvoke);

                il.Emit(OpCodes.Ret);

                DynamicMethodHelper.SetReference(_RefTrampoline.Value, dm.CreateDelegate(origType));
            }

            _Detour = new Detour(Method, Target);

            UpdateOrig(null);
        }