예제 #1
0
        public void TestDetoursExt()
        {
            lock (TestObject.Lock) {
                // The following use cases are not meant to be usage examples.
                // Please take a look at DetourTest and HookTest instead.

                using (NativeDetour d = new NativeDetour(
                           // .GetNativeStart() to enforce a native detour.
                           typeof(TestObject).GetMethod("TestStaticMethod").Pin().GetNativeStart(),
                           typeof(DetourExtTest).GetMethod("TestStaticMethod_A")
                           )) {
                    int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3);
                    Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                    Assert.Equal(6, staticResult);

                    staticResult = TestObject.TestStaticMethod(2, 3);
                    Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                    Assert.Equal(12, staticResult);
                }

                // We can't create a backup for this.
                MethodBase dm;
                using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(TestObject).GetMethod("TestStaticMethod"))) {
                    dm = dmd.Generate();
                }
                using (NativeDetour d = new NativeDetour(
                           dm,
                           typeof(DetourExtTest).GetMethod("TestStaticMethod_A")
                           )) {
                    int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3);
                    Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                    Assert.Equal(6, staticResult);

                    // FIXME: dm.Invoke can fail with a release build in mono 5.X!
                    // staticResult = (int) dm.Invoke(null, new object[] { 2, 3 });
                    staticResult = ((Func <int, int, int>)dm.CreateDelegate <Func <int, int, int> >())(2, 3);
                    Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                    Assert.Equal(12, staticResult);
                }

                // This was provided by Chicken Bones (tModLoader).
                // GetEncoding behaves differently on .NET Core and even between .NET Framework versions,
                // which is why this test only applies to Mono, preferably on Linux to verify if flagging
                // regions of code as read-writable and then read-executable works for AOT'd code.
#if false
                using (Hook h = new Hook(
                           typeof(Encoding).GetMethod("GetEncoding", new Type[] { typeof(string) }),
                           new Func <Func <string, Encoding>, string, Encoding>((orig, name) => {
                    if (name == "IBM437")
                    {
                        return(null);
                    }
                    return(orig(name));
                })
                           )) {
                    Assert.Null(Encoding.GetEncoding("IBM437"));
                }
#endif
            }
        }
예제 #2
0
        /// <summary>
        /// Returns an instruction to call the specified delegate.
        /// </summary>
        /// <typeparam name="T">The delegate type to emit.</typeparam>
        /// <param name="action">The delegate to emit.</param>
        /// <returns>The instruction to </returns>
        public static CodeInstruction EmitDelegate <T>(T action) where T : Delegate
        {
            if (action.Method.IsStatic && action.Target == null)
            {
                return(new CodeInstruction(OpCodes.Call, action.Method));
            }

            var paramTypes = action.Method.GetParameters().Select(x => x.ParameterType).ToArray();

            var dynamicMethod = new DynamicMethodDefinition(action.Method.Name,
                                                            action.Method.ReturnType,
                                                            paramTypes);

            var il = dynamicMethod.GetILGenerator();

            var targetType = action.Target.GetType();

            var preserveContext = action.Target != null && targetType.GetFields().Any(x => !x.IsStatic);

            if (preserveContext)
            {
                var currentDelegateCounter = _delegateCounter++;

                _delegateCache[currentDelegateCounter] = action;

                var cacheField = AccessTools.Field(typeof(Transpilers), nameof(_delegateCache));

                var getMethod = AccessTools.Method(typeof(Dictionary <int, Delegate>), "get_Item");

                il.Emit(OpCodes.Ldsfld, cacheField);
                il.Emit(OpCodes.Ldc_I4, currentDelegateCounter);
                il.Emit(OpCodes.Callvirt, getMethod);
            }
            else
            {
                if (action.Target == null)
                {
                    il.Emit(OpCodes.Ldnull);
                }
                else
                {
                    il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.GetParameters().Length == 0 && !x.IsStatic));
                }

                il.Emit(OpCodes.Ldftn, action.Method);
                il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) }));
            }


            for (var i = 0; i < paramTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_S, (short)i);
            }

            il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("Invoke"));

            il.Emit(OpCodes.Ret);

            return(new CodeInstruction(OpCodes.Call, dynamicMethod.Generate()));
        }
예제 #3
0
 internal void DetourILDetourTarget(bool force = false)
 {
     ILProxyDetour?.Dispose();
     ILProxyDetour = null;
     if (!force && ILList.Count == 0)
     {
         return;
     }
     try {
         ILProxyDetour = new NativeDetour(ILCopy, DMD.Generate());
     } catch (Exception e) {
         StringBuilder builder = new StringBuilder();
         if (DMD.Definition?.Body?.Instructions != null)
         {
             builder.AppendLine("IL hook failed for:");
             foreach (Instruction i in DMD.Definition.Body.Instructions)
             {
                 builder.AppendLine(i?.ToString() ?? "NULL!");
             }
         }
         else
         {
             builder.AppendLine("IL hook failed, no instructions found");
         }
         throw new InvalidProgramException(builder.ToString(), e);
     }
 }
예제 #4
0
        /// <summary>Creates a field reference</summary>
        /// <typeparam name="T">The class the field is defined in or "object" if type cannot be accessed at compile time</typeparam>
        /// <typeparam name="U">The type of the field</typeparam>
        /// <param name="fieldInfo">FieldInfo for the field</param>
        /// <returns>A read and writable field reference</returns>
        ///
        public static FieldRef <T, U> FieldRefAccess <T, U>(FieldInfo fieldInfo)
        {
            if (fieldInfo == null)
            {
                throw new ArgumentNullException(nameof(fieldInfo));
            }
            if (!typeof(U).IsAssignableFrom(fieldInfo.FieldType))
            {
                throw new ArgumentException("FieldInfo type does not match FieldRefAccess return type.");
            }
            if (typeof(T) != typeof(object))
            {
                if (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T)))
                {
                    throw new MissingFieldException(typeof(T).Name, fieldInfo.Name);
                }
            }

            var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_fi_{fieldInfo.Name}", typeof(U).MakeByRefType(), new[] { typeof(T) });

            var il = dm.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldflda, fieldInfo);
            il.Emit(OpCodes.Ret);

            return((FieldRef <T, U>)dm.Generate().CreateDelegate <FieldRef <T, U> >());
        }
        public override MethodBase DetourTo(MethodBase replacement)
        {
            DebugCheck();

            DynamicMethodDefinition newreplacementdmd = CopyOriginal();

            HarmonyManipulator.Manipulate(Original, Original.GetPatchInfo(), new ILContext(newreplacementdmd.Definition));
            MethodInfo newreplacement = newreplacementdmd.Generate();

            MethodInfo il2CppShim             = CreateIl2CppShim(newreplacement).Generate();
            Type       il2CppShimDelegateType = DelegateTypeFactory.instance.CreateDelegateType(il2CppShim, CallingConvention.Cdecl);
            Delegate   il2CppShimDelegate     = il2CppShim.CreateDelegate(il2CppShimDelegateType);
            IntPtr     il2CppShimDelegatePtr  = il2CppShimDelegate.GetFunctionPointer();

            if (methodDetourPointer != IntPtr.Zero)
            {
                MelonUtils.NativeHookDetach(copiedMethodInfoPointer, methodDetourPointer);
            }
            MelonUtils.NativeHookAttach(copiedMethodInfoPointer, il2CppShimDelegatePtr);
            methodDetourPointer = il2CppShimDelegatePtr;

            PatchTools_RememberObject(Original, new LemonTuple <MethodInfo, MethodInfo, Delegate> {
                Item1 = newreplacement, Item2 = il2CppShim, Item3 = il2CppShimDelegate
            });

            return(newreplacement);
        }
예제 #6
0
        public static MethodInfo Prefix(MethodBase method)
        {
            var dynamicMethod = new DynamicMethodDefinition(method.Name + "_Class11Patch_Prefix", typeof(bool),
                                                            new[] { typeof(string).MakeByRefType(), typeof(int) });

            dynamicMethod.Definition.Parameters[0].Name = "__result";
            dynamicMethod.Definition.Parameters[1].Name = "dummy";

            var il = dynamicMethod.GetILGenerator();

            //load "patched" into __result
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldstr, "patched");
            il.Emit(OpCodes.Stind_Ref);

            //set prefixed to true
            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Ldc_I4_1);
            il.Emit(OpCodes.Stfld, typeof(Class11Patch).GetField(nameof(prefixed)));

            //return false
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Ret);

            return(dynamicMethod.Generate());
        }
예제 #7
0
      public static Func<T, F> CreateFastFieldGetter<T, F>( FieldInfo fieldInfo )
      {
         if( fieldInfo == null )
            throw new ArgumentNullException( nameof( fieldInfo ) );
         if( !typeof( F ).IsAssignableFrom( fieldInfo.FieldType ) )
            throw new ArgumentException( "FieldInfo type does not match return type." );
         if( typeof( T ) != typeof( object ) )
            if( fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom( typeof( T ) ) )
               throw new MissingFieldException( typeof( T ).Name, fieldInfo.Name );

         var name = $"FastReflection<{typeof( T ).FullName}.Get_{fieldInfo.Name}>";

         var dm = new DynamicMethodDefinition( name, typeof( F ), new[] { typeof( T ) } );

         var il = dm.GetILProcessor();
         if( !fieldInfo.IsStatic )
         {
            il.Emit( OpCodes.Ldarg_0 );
            il.Emit( OpCodes.Castclass, fieldInfo.DeclaringType );
         }

         il.Emit( fieldInfo.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, fieldInfo );
         if( fieldInfo.FieldType.IsValueType != typeof( F ).IsValueType )
         {
            il.Emit( OpCodes.Box, fieldInfo.FieldType );
         }
         il.Emit( OpCodes.Ret );

         return (Func<T, F>)dm.Generate().CreateDelegate( typeof( Func<T, F> ) );
      }
예제 #8
0
        /*
         * Fix for detour jump overriding the method that's right next in memory
         * See https://github.com/MonoMod/MonoMod.Common/blob/58702d64645aba613ad16275c0b78278ff0d2055/RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs#L77
         *
         * It happens for any very small method, not just virtual, but it just so happens that
         * virtual methods are usually the only empty ones. The problem is with the detour code
         * overriding the method that's right next in memory.
         *
         * The 64bit absolute jump detour requires 14 bytes but on Linux an empty method is just
         * 10 bytes. On Windows, due to prologue differences, an empty method is exactly 14 bytes
         * as required.
         *
         * Now, the small code size wouldn't be a problem if it wasn't for the way Mono compiles
         * trampolines. Usually methods on x64 are 16 bytes aligned, so no actual code could get
         * overriden by the detour, but trampolines don't follow this rule and a trampoline generated
         * for the dynamic method from the patch is placed right after the method being detoured.
         *
         * The detour code then overrides the beggining of the trampoline and that leads to a
         * segfault on execution.
         *
         * There's also the fact that Linux seems to allocate the detour code far away in memory
         * so it uses the 64 bit jump in the first place. On Windows with Mono usually the 32 bit
         * smaller jumps suffice.
         *
         * The fix changes the order in which methods are JITted so that no trampoline is placed
         * after the detoured method (or at least the trampoline that causes this specific crash)
         */
        internal static void PadShortMethods(MethodBase method)
        {
            if (isWindows)
            {
                return;
            }
            var count = method.GetMethodBody()?.GetILAsByteArray()?.Length ?? 0;

            if (count == 0)
            {
                return;
            }

            // the 16 here is arbitrary but high enough to prevent the jitted code from getting under 14 bytes
            // and high enough to not generate too many fix methods
            if (count >= 16)
            {
                return;
            }

            var methodDef = new DynamicMethodDefinition($"PadMethod-{Guid.NewGuid()}", typeof(void), new Type[0]);

            methodDef.GetILGenerator().Emit(OpCodes.Ret);

            // invoke the method so that it generates a trampoline that will later get overridden by the detour code
            _ = methodDef.Generate().Invoke(null, null);
        }
예제 #9
0
        internal static FieldRef <T, F> FieldRefAccess <T, F>(FieldInfo fieldInfo, bool needCastclass)
        {
            ValidateFieldType <F>(fieldInfo);
            var delegateInstanceType = typeof(T);
            var declaringType        = fieldInfo.DeclaringType;

            var dm = new DynamicMethodDefinition($"__refget_{delegateInstanceType.Name}_fi_{fieldInfo.Name}",
                                                 typeof(F).MakeByRefType(), new[] { delegateInstanceType });

            var il = dm.GetILGenerator();

            // Backwards compatibility: This supports static fields, even those defined in structs.
            if (fieldInfo.IsStatic)
            {
                // ldarg.0 + ldflda actually works for static fields, but the potential castclass (and InvalidCastException) below must be avoided
                // so might as well use the singular ldsflda for static fields.
                il.Emit(OpCodes.Ldsflda, fieldInfo);
            }
            else
            {
                il.Emit(OpCodes.Ldarg_0);
                // The castclass is needed when T is a parent class or interface of declaring type (e.g. if T is object),
                // since there's no guarantee the instance passed to the delegate is actually of the declaring type.
                // In such a situation, the castclass will throw an InvalidCastException and thus prevent undefined behavior.
                if (needCastclass)
                {
                    il.Emit(OpCodes.Castclass, declaringType);
                }
                il.Emit(OpCodes.Ldflda, fieldInfo);
            }
            il.Emit(OpCodes.Ret);

            return((FieldRef <T, F>)dm.Generate().CreateDelegate(typeof(FieldRef <T, F>)));
        }
예제 #10
0
        /*
         * Fix for detour jump overriding the method that's right next in memory
         * See https://github.com/MonoMod/MonoMod.Common/blob/58702d64645aba613ad16275c0b78278ff0d2055/RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs#L77
         *
         * It happens for any very small method, not just virtual, but it just so happens that
         * virtual methods are usually the only empty ones. The problem is with the detour code
         * overriding the method that's right next in memory.
         *
         * The 64bit absolute jump detour requires 14 bytes but on Linux an empty method is just
         * 10 bytes. On Windows, due to prologue differences, an empty method is exactly 14 bytes
         * as required.
         *
         * Now, the small code size wouldn't be a problem if it wasn't for the way Mono compiles
         * trampolines. Usually methods on x64 are 16 bytes aligned, so no actual code could get
         * overriden by the detour, but trampolines don't follow this rule and a trampoline generated
         * for the dynamic method from the patch is placed right after the method being detoured.
         *
         * The detour code then overrides the beggining of the trampoline and that leads to a
         * segfault on execution.
         *
         * There's also the fact that Linux seems to allocate the detour code far away in memory
         * so it uses the 64 bit jump in the first place. On Windows with Mono usually the 32 bit
         * smaller jumps suffice.
         *
         * The fix changes the order in which methods are JITted so that no trampoline is placed
         * after the detoured method (or at least the trampoline that causes this specific crash)
         */
        internal static void PadShortMethods(MethodBase method)
        {
            if (isWindows)
            {
                return;
            }
            var count = method.GetMethodBody()?.GetILAsByteArray()?.Length ?? 0;

            if (count == 0)
            {
                return;
            }

            // the 16 here is arbitrary but high enough to prevent the jitted code from getting under 14 bytes
            // and high enough to not generate too many fix methods
            if (count >= 16)
            {
                return;
            }

            var methodDef = new DynamicMethodDefinition($"PadMethod-{Guid.NewGuid()}", typeof(void), new Type[0]);

            methodDef.GetILGenerator().Emit(OpCodes.Ret);
            _ = GetMethodStart(methodDef.Generate(), out var _);             // trigger allocation/generation of jitted assembler
        }
예제 #11
0
        public MethodBase GetDetourTarget(MethodBase from, MethodBase to) {
            Type context = to.DeclaringType;

            MethodInfo dm = null;

            if (GlueThiscallStructRetPtr != GlueThiscallStructRetPtrOrder.Original &&
                from is MethodInfo fromInfo && !from.IsStatic &&
                to is MethodInfo toInfo && to.IsStatic &&
                fromInfo.ReturnType == toInfo.ReturnType &&
                fromInfo.ReturnType.IsValueType) {

                int size = fromInfo.ReturnType.GetManagedSize();
                if (size == 3 || size == 5 || size == 6 || size == 7 || size >= 9) {
                    Type thisType = from.GetThisParamType();
                    Type retType = fromInfo.ReturnType.MakeByRefType(); // Refs are shiny pointers.

                    int thisPos = 0;
                    int retPos = 1;

                    if (GlueThiscallStructRetPtr == GlueThiscallStructRetPtrOrder.RetThisArgs) {
                        thisPos = 1;
                        retPos = 0;
                    }

                    List<Type> argTypes = new List<Type> {
                        thisType
                    };
                    argTypes.Insert(retPos, retType);

                    argTypes.AddRange(from.GetParameters().Select(p => p.ParameterType));

                    using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(
                        $"Glue:ThiscallStructRetPtr<{from.GetID(simple: true)},{to.GetID(simple: true)}>",
                        typeof(void), argTypes.ToArray()
                    )) {
                        ILProcessor il = dmd.GetILProcessor();

                        // Load the return buffer address.
                        il.Emit(OpCodes.Ldarg, retPos);

                        // Invoke the target method with all remaining arguments.
                        {
                            il.Emit(OpCodes.Ldarg, thisPos);
                            for (int i = 2; i < argTypes.Count; i++)
                                il.Emit(OpCodes.Ldarg, i);
                            il.Emit(OpCodes.Call, il.Body.Method.Module.ImportReference(to));
                        }

                        // Store the returned object to the return buffer.
                        il.Emit(OpCodes.Stobj, il.Body.Method.Module.ImportReference(fromInfo.ReturnType));
                        il.Emit(OpCodes.Ret);

                        dm = dmd.Generate();
                    }
                }
            }

            return dm ?? to;
        }
예제 #12
0
        public MethodInfo CreateCopy(MethodBase method) {
            if (method == null || (method.GetMethodImplementationFlags() & (MethodImplAttributes.OPTIL | MethodImplAttributes.Native | MethodImplAttributes.Runtime)) != 0) {
                throw new InvalidOperationException($"Uncopyable method: {method?.ToString() ?? "NULL"}");
            }

            using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(method))
                return dmd.Generate();
        }
예제 #13
0
        private static string DetourMethod(MethodBase original, MethodBase replacement) {
            if (replacement == null) {
                replacement = _LastWrapperDMD.Generate();
                _LastWrapperDMD.Dispose();
                _LastWrapperDMD = null;
            }

            _Detours.Add(new Detour(original, replacement, ref _DetourConfig));
            return null;
        }
예제 #14
0
        public static void RegisterDelegate(DynamicMethodDefinition dynamicMethod, string delegateFullName)
        {
            MethodBase method = dynamicMethod.Generate();

            switch (method.Name)
            {
            case "UseItemEffect":
                UseItemEffect.TryAdd(delegateFullName, (Action <Player, Rectangle>)method.CreateDelegate <Action <Player, Rectangle> >());
                break;
            }
        }
예제 #15
0
        static ILHookExtensions()
        {
            var detourGetter = new DynamicMethodDefinition("GetDetour", typeof(Detour), new[] { typeof(ILHook) });
            var il           = detourGetter.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, AccessTools.PropertyGetter(typeof(ILHook), "_Ctx"));
            il.Emit(OpCodes.Ldfld, AccessTools.Field(AccessTools.Inner(typeof(ILHook), "Context"), "Detour"));
            il.Emit(OpCodes.Ret);
            GetAppliedDetour = detourGetter.Generate().CreateDelegate <Func <ILHook, Detour> >() as Func <ILHook, Detour>;
        }
예제 #16
0
 internal static void Initialize()
 {
     foreach (var field in AccessTools.GetDeclaredFields(typeof(OS)).Where(x => x.FieldType == typeof(Color)))
     {
         var dynMethod = new DynamicMethodDefinition(field.Name, typeof(Color).MakeByRefType(), new Type[] { typeof(OS) });
         var p         = dynMethod.GetILProcessor();
         p.Emit(OpCodes.Ldarg_0);
         p.Emit(OpCodes.Ldflda, field);
         p.Emit(OpCodes.Ret);
         OSColorFieldsFast.Add(field.Name, dynMethod.Generate().CreateDelegate <RefColorFieldDelegate>());
     }
 }
예제 #17
0
        public void TestDetours()
        {
            // The following use cases are not meant to be usage examples.
            // Please take a look at DetourTest and HookTest instead.

            using (NativeDetour d = new NativeDetour(
                       // .GetNativeStart() to enforce a native detour.
                       typeof(TestObject).GetMethod("TestStaticMethod").GetNativeStart(),
                       typeof(DetourExtTest).GetMethod("TestStaticMethod_A")
                       )) {
                int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3);
                Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                Assert.Equal(6, staticResult);

                staticResult = TestObject.TestStaticMethod(2, 3);
                Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                Assert.Equal(12, staticResult);
            }

            // We can't create a backup for this.
            MethodBase dm;

            using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(TestObject).GetMethod("TestStaticMethod"))) {
                dm = dmd.Generate();
            }
            using (NativeDetour d = new NativeDetour(
                       dm,
                       typeof(DetourExtTest).GetMethod("TestStaticMethod_A")
                       )) {
                int staticResult = d.GenerateTrampoline <Func <int, int, int> >()(2, 3);
                Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                Assert.Equal(6, staticResult);

                staticResult = (int)dm.Invoke(null, new object[] { 2, 3 });
                Console.WriteLine($"TestStaticMethod(2, 3): {staticResult}");
                Assert.Equal(12, staticResult);
            }

            // This was provided by Chicken Bones (tModLoader).
            using (Hook h = new Hook(
                       typeof(Encoding).GetMethod("GetEncoding", new Type[] { typeof(string) }),
                       new Func <Func <string, Encoding>, string, Encoding>((orig, name) => {
                if (name == "IBM437")
                {
                    return(null);
                }
                return(orig(name));
            })
                       )) {
                Assert.Null(Encoding.GetEncoding("IBM437"));
            }
        }
예제 #18
0
        public static InstantiationHandler CreateInstantiationHandler(Type type)
        {
            var constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);

            if (constructorInfo is null)
            {
                throw new ApplicationException(string.Format("The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).", type));
            }
            var dynamicMethod = new DynamicMethodDefinition($"InstantiateObject_{type.Name}", type, null);
            var generator     = dynamicMethod.GetILGenerator();

            generator.Emit(OpCodes.Newobj, constructorInfo);
            generator.Emit(OpCodes.Ret);
            return((InstantiationHandler)dynamicMethod.Generate().CreateDelegate(typeof(InstantiationHandler)));
        }
예제 #19
0
        internal static StructFieldRef <T, F> StructFieldRefAccess <T, F>(FieldInfo fieldInfo) where T : struct
        {
            ValidateFieldType <F>(fieldInfo);

            var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_struct_fi_{fieldInfo.Name}",
                                                 typeof(F).MakeByRefType(), new[] { typeof(T).MakeByRefType() });

            var il = dm.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldflda, fieldInfo);
            il.Emit(OpCodes.Ret);

            return((StructFieldRef <T, F>)dm.Generate().CreateDelegate(typeof(StructFieldRef <T, F>)));
        }
예제 #20
0
        /// <summary>Returns an instruction to call the specified closure</summary>
        /// <typeparam name="T">The delegate type to emit</typeparam>
        /// <param name="closure">The closure that defines the method to call</param>
        /// <returns>A <see cref="CodeInstruction"/> that calls the closure as a method</returns>
        ///
        public static CodeInstruction CallClosure <T>(T closure) where T : Delegate
        {
            if (closure.Method.IsStatic && closure.Target == null)
            {
                return(new CodeInstruction(OpCodes.Call, closure.Method));
            }

            var parameters    = closure.Method.GetParameters().Select(x => x.ParameterType).ToArray();
            var closureMethod = new DynamicMethodDefinition(closure.Method.Name, closure.Method.ReturnType, parameters);

            var il         = closureMethod.GetILGenerator();
            var targetType = closure.Target.GetType();

            var preserveContext = closure.Target != null && targetType.GetFields().Any(x => !x.IsStatic);

            if (preserveContext)
            {
                var n = State.closureCache.Count;
                State.closureCache[n] = closure;
                il.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(Transpilers), nameof(State.closureCache)));
                il.Emit(OpCodes.Ldc_I4, n);
                il.Emit(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Dictionary <int, Delegate>), "Item"));
            }
            else
            {
                if (closure.Target == null)
                {
                    il.Emit(OpCodes.Ldnull);
                }
                else
                {
                    il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.IsStatic == false && x.GetParameters().Length == 0));
                }

                il.Emit(OpCodes.Ldftn, closure.Method);
                il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) }));
            }

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

            il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(T), nameof(Action.Invoke)));
            il.Emit(OpCodes.Ret);

            return(new CodeInstruction(OpCodes.Call, closureMethod.Generate()));
        }
예제 #21
0
        private static Delegate CreateGetStaticFieldRefDelegate <TField>(FieldInfo fi)
        {
            var dm = new DynamicMethodDefinition
                     (
                "__ReturnByRef" + fi.DeclaringType?.Name + fi.Name,
                fi.FieldType.MakeByRefType(),
                new Type[0]
                     );

            ILGenerator gen = dm.GetILGenerator();

            gen.Emit(OpCodes.Ldsflda, fi);
            gen.Emit(OpCodes.Ret);

            return(dm.Generate().CreateDelegate(typeof(FuncByRef <TField>)));
        }
예제 #22
0
        private static Delegate CreateGetStaticFieldDelegate <TField>(FieldInfo fi)
        {
            var dm = new DynamicMethodDefinition
                     (
                "__FieldAccess" + fi.DeclaringType?.Name + fi.Name,
                typeof(TField),
                new Type[0]
                     );

            ILGenerator gen = dm.GetILGenerator();

            gen.Emit(OpCodes.Ldsfld, fi);
            gen.Emit(OpCodes.Ret);

            return(dm.Generate().CreateDelegate(typeof(Func <TField>)));
        }
예제 #23
0
        private static Delegate CreateGetFieldRefDelegate <TClass, TField>(FieldInfo info)
        {
            var dm = new DynamicMethodDefinition
                     (
                "__ReturnByRef" + info.DeclaringType?.Name + info.Name,
                typeof(TField).MakeByRefType(),
                new[] { typeof(TClass) }
                     );

            ILGenerator gen = dm.GetILGenerator();

            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldflda, info);
            gen.Emit(OpCodes.Ret);

            return(dm.Generate().CreateDelegate(typeof(FuncByRef <TClass, TField>)));
        }
예제 #24
0
        static int SizeOf(Type type)
        {
            if (sizes.TryGetValue(type, out var size))
            {
                return(size);
            }

            var dm = new DynamicMethodDefinition("SizeOfType", typeof(int), new Type[0]);
            var il = dm.GetILGenerator();

            il.Emit(OpCodes.Sizeof, type);
            il.Emit(OpCodes.Ret);
            size = (int)dm.Generate().Invoke(null, null);

            sizes.Add(type, size);
            return(size);
        }
예제 #25
0
        private static Delegate CreateSetStaticFieldDelegate <TField>(FieldInfo fi)
        {
            var dm = new DynamicMethodDefinition
                     (
                "__FieldSet" + fi.DeclaringType?.Name + fi.Name,
                typeof(void),
                new[] { typeof(TField) }
                     );

            ILGenerator gen = dm.GetILGenerator();

            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Stsfld, fi);
            gen.Emit(OpCodes.Ret);

            return(dm.Generate().CreateDelegate(typeof(Action <TField>)));
        }
예제 #26
0
        public override void Apply()
        {
            // The process to patch native methods is as follows:
            // 1. Create a managed proxy method that calls NativeDetour's trampoline (we need to cache it
            //    because we don't know the trampoline method when generating the DMD).
            // 2. Pass the proxy to the normal Harmony manipulator to apply prefixes, postfixes, transpilers, etc.
            // 3. NativeDetour the method to the managed proxy
            // 4. Cache the NativeDetour's trampoline (technically we wouldn't need to, this is just a workaround
            //    for MonoMod's API.

            if (IsRunningOnDotNetCore)
            {
                Logger.Log(Logger.LogChannel.Warn, () => $"Patch target {Original.GetID()} is marked as extern. " +
                           "Extern methods may not be patched because of inlining behaviour of coreclr (refer to https://github.com/dotnet/coreclr/pull/8263)." +
                           "If you need to patch externs, consider using pure NativeDetour instead.");
            }


            var prevDmd = _dmd;

            _nativeDetour?.Dispose();

            _dmd = GenerateManagedOriginal();
            var ctx = new ILContext(_dmd.Definition);

            HarmonyManipulator.Manipulate(Original, Original.GetPatchInfo(), ctx);

            var target = _dmd.Generate();

            _nativeDetour = new NativeDetour(Original, target, new NativeDetourConfig
            {
                ManualApply = true
            });

            lock (TrampolineCache)
            {
                if (prevDmd != null)
                {
                    TrampolineCache.Remove(prevDmd.GetHashCode());
                }

                TrampolineCache[_dmd.GetHashCode()] = CreateDelegate(_trampolineDelegateType, _nativeDetour.GenerateTrampoline(_invokeTrampolineMethod));
            }

            _nativeDetour.Apply();
        }
예제 #27
0
        /// <summary>Creates a static field reference delegate</summary>
        /// <typeparam name="F">The type of the field</typeparam>
        /// <param name="fieldInfo">FieldInfo for the field</param>
        /// <returns>A read and writable field reference delegate</returns>
        ///
        public static FieldRef <F> StaticFieldRefAccess <F>(FieldInfo fieldInfo)
        {
            if (fieldInfo == null)
            {
                throw new ArgumentNullException(nameof(fieldInfo));
            }
            var t = fieldInfo.DeclaringType;

            var dm = new DynamicMethodDefinition($"__refget_{t.Name}_static_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new Type[0]);

            var il = dm.GetILGenerator();

            il.Emit(OpCodes.Ldsflda, fieldInfo);
            il.Emit(OpCodes.Ret);

            return((FieldRef <F>)dm.Generate().CreateDelegate <FieldRef <F> >());
        }
예제 #28
0
        internal static FieldRef <F> StaticFieldRefAccess <F>(FieldInfo fieldInfo)
        {
            if (fieldInfo.IsStatic is false)
            {
                throw new ArgumentException("Field must be static");
            }
            Tools.ValidateFieldType <F>(fieldInfo);

            var dm = new DynamicMethodDefinition($"__refget_{fieldInfo.DeclaringType?.Name ?? "null"}_static_fi_{fieldInfo.Name}",
                                                 typeof(F).MakeByRefType(), new Type[0]);

            var il = dm.GetILGenerator();

            il.Emit(OpCodes.Ldsflda, fieldInfo);
            il.Emit(OpCodes.Ret);

            return((FieldRef <F>)dm.Generate().CreateDelegate(typeof(FieldRef <F>)));
        }
예제 #29
0
        private static void HookSystemRuntimeTypeGetMethodBase()
        {
            var systemRuntimeTypeType = typeof(Type).Assembly.GetType("System.RuntimeType");

            var getMethodBase1 = systemRuntimeTypeType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
                                 .Where(m => m.Name == "GetMethodBase")
                                 .Where(m => m.GetParameters().Length == 2)
                                 .FirstOrDefault(m =>
                                                 m.GetParameters().First().ParameterType == systemRuntimeTypeType &&
                                                 m.GetParameters().Last().ParameterType.Name == "IRuntimeMethodInfo");

            var getMethodBase2 = systemRuntimeTypeType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
                                 .FirstOrDefault(m => m.Name == "GetMethodBase" && m.GetParameters().Length == 1);

            var myMethod = typeof(Program).GetMethod(nameof(Hook5), BindingFlags.Static | BindingFlags.Public);

            var replacementMethod = new DynamicMethodDefinition(
                getMethodBase2.Name,
                getMethodBase2.ReturnType,
                getMethodBase2.GetParameters().Select(x => x.ParameterType).ToArray()
                )
            {
                OwnerType = getMethodBase1.DeclaringType
            };

            var iLGenerator = replacementMethod.GetILGenerator();

            iLGenerator.DeclareLocal(typeof(MethodBase), false);

            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldnull);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_0);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldnull);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Call, getMethodBase1);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_0);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldloca, 0);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Call, myMethod);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
            iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ret);

            var replacementMethodInfo = replacementMethod.Generate();

            Memory.SimpleHook(getMethodBase2, replacementMethodInfo);
        }
예제 #30
0
파일: FastAccess.cs 프로젝트: riQQ/HarmonyX
        /// <summary>Creates an instantiation delegate</summary>
        /// <typeparam name="T">Type that constructor creates</typeparam>
        /// <returns>The new instantiation delegate</returns>
        ///
        public static InstantiationHandler <T> CreateInstantiationHandler <T>()
        {
            var constructorInfo =
                typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null,
                                         new Type[0], null);

            if (constructorInfo == null)
            {
                throw new ApplicationException(
                          $"The type {typeof(T)} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).");
            }

            var dmd = new DynamicMethodDefinition($"InstantiateObject_{typeof(T).Name}", typeof(T), null);
            var il  = dmd.GetILGenerator();

            il.Emit(OpCodes.Newobj, constructorInfo);
            il.Emit(OpCodes.Ret);
            return((InstantiationHandler <T>)dmd.Generate().CreateDelegate <InstantiationHandler <T> >());
        }