Ejemplo n.º 1
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()));
        }
Ejemplo n.º 2
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> >());
        }
Ejemplo n.º 3
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
            }
        }
Ejemplo n.º 4
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>)));
        }
Ejemplo n.º 5
0
        private DynamicMethodDefinition GenerateManagedOriginal()
        {
            // Here we generate the "managed" version of the native method
            // It simply calls the trampoline generated by MonoMod
            // As a result, we can pass the managed original to HarmonyManipulator like a normal method

            var orig = Original;

            var dmd = new DynamicMethodDefinition($"NativeDetour<{orig.GetID(simple: true)}>", _returnType, _argTypes);

            dmd.Definition.Name += $"?{dmd.GetHashCode()}";

            var def = dmd.Definition;

            for (var i = 0; i < _argTypeNames.Length; i++)
            {
                def.Parameters[i].Name = _argTypeNames[i];
            }

            var il = dmd.GetILGenerator();

            il.Emit(OpCodes.Ldc_I4, dmd.GetHashCode());
            il.Emit(OpCodes.Call, GetTrampolineMethod);
            for (var i = 0; i < _argTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i);
            }
            il.Emit(OpCodes.Call, _invokeTrampolineMethod);
            il.Emit(OpCodes.Ret);

            return(dmd);
        }
Ejemplo n.º 6
0
        public void RtDynamicMethod()
        {
            var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location);

            DynamicMethod generateDynamicMethod = TDynamicMethod.GenerateDynamicMethod();

            var rtDynamicMethod = generateDynamicMethod.GetType().GetField("m_dynMethod", (BindingFlags)(-1))?.GetValue(generateDynamicMethod);

            var dynamicMethodDefinition = new DynamicMethodDefinition(module, rtDynamicMethod);

            Assert.NotNull(dynamicMethodDefinition);

            Assert.NotEmpty(dynamicMethodDefinition.CilMethodBody.Instructions);

            Assert.Equal(dynamicMethodDefinition.CilMethodBody.Instructions.Select(q => q.OpCode), new []
            {
                CilOpCodes.Ldarg_0,
                CilOpCodes.Call,
                CilOpCodes.Ldarg_1,
                CilOpCodes.Ret
            });
            Assert.Equal(dynamicMethodDefinition.Parameters.Select(q => q.ParameterType), new TypeSignature[]
            {
                module.CorLibTypeFactory.String,
                module.CorLibTypeFactory.Int32
            });
        }
Ejemplo n.º 7
0
        /// <inheritdoc />
        public override DynamicMethodDefinition CopyOriginal()
        {
            var dmd = new DynamicMethodDefinition(Original);

            dmd.Definition.Name = "UnhollowedWrapper_" + dmd.Definition.Name;

            var cursor = new ILCursor(new ILContext(dmd.Definition));


            // Remove il2cpp_object_get_virtual_method

            if (cursor.TryGotoNext(x => x.MatchLdarg(0),
                                   x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.Il2CppObjectBaseToPtr)),
                                   x => x.MatchLdsfld(out _),
                                   x => x.MatchCall(typeof(UnhollowerBaseLib.IL2CPP), nameof(UnhollowerBaseLib.IL2CPP.il2cpp_object_get_virtual_method))))
            {
                cursor.RemoveRange(4);
            }
            else
            {
                cursor.Goto(0)
                .GotoNext(x => x.MatchLdsfld(UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original)))
                .Remove();
            }

            // Replace original IL2CPPMethodInfo pointer with a modified one that points to the trampoline

            cursor
            .Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, ((IntPtr)modifiedNativeMethodInfo).ToInt64())
            .Emit(Mono.Cecil.Cil.OpCodes.Conv_I);

            return(dmd);
        }
Ejemplo n.º 8
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> ) );
      }
Ejemplo n.º 9
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());
        }
Ejemplo n.º 10
0
        /// <summary>A low level way to read the body of a method. Used for quick searching in methods</summary>
        /// <param name="method">The original method</param>
        /// <returns>All instructions as opcode/operand pairs</returns>
        ///
        public static IEnumerable <KeyValuePair <OpCode, object> > ReadMethodBody(MethodBase method)
        {
            var dummyMethod = new DynamicMethodDefinition($"{method.Name}_Dummy{Guid.NewGuid()}", typeof(void), new Type[0]);

            return(MethodBodyReader.GetInstructions(dummyMethod.GetILGenerator(), method)
                   .Select(instr => new KeyValuePair <OpCode, object>(instr.opcode, instr.operand)));
        }
Ejemplo n.º 11
0
        private void ApplyReversePatch(ILContext ctx)
        {
            // Make a cecil copy of the original method for convenience sake
            var dmd = new DynamicMethodDefinition(original);

            var manipulator = new ILManipulator(dmd.Definition.Body);

            // Copy over variables from the original code
            ctx.Body.Variables.Clear();
            foreach (var variableDefinition in dmd.Definition.Body.Variables)
            {
                ctx.Body.Variables.Add(new VariableDefinition(ctx.Module.ImportReference(variableDefinition.VariableType)));
            }

            var transpiler = GetTranspiler(standin);

            if (transpiler != null)
            {
                manipulator.AddTranspiler(transpiler);
            }

            manipulator.WriteTo(ctx.Body, standin);

            // Write a ret in case it got removed (wrt. HarmonyManipulator)
            ctx.IL.Emit(OpCodes.Ret);
        }
Ejemplo n.º 12
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);
        }
Ejemplo n.º 13
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
        }
        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);
        }
        public override DynamicMethodDefinition CopyOriginal()
        {
            DynamicMethodDefinition method = Original.ToNewDynamicMethodDefinition();

            method.Definition.Name += "_wrapper";
            ILContext      ilcontext          = new ILContext(method.Definition);
            ILCursor       ilcursor           = new ILCursor(ilcontext);
            FieldReference tempfieldreference = null;

            if (ilcursor.TryGotoNext(x => x.MatchLdsfld(out tempfieldreference), x => x.MatchCall(UnhollowerSupport.IL2CPPType, "il2cpp_object_get_virtual_method")))
            {
                // Virtual method: Replace the sequence
                // - ldarg.0
                // - call native int[UnhollowerBaseLib] UnhollowerBaseLib.IL2CPP::Il2CppObjectBaseToPtr(class [UnhollowerBaseLib] UnhollowerBaseLib.Il2CppObjectBase)
                // - ldsfld native int SomeClass::NativeMethodInfoPtr_Etc
                // - call native int[UnhollowerBaseLib] UnhollowerBaseLib.IL2CPP::il2cpp_object_get_virtual_method(native int, native int)

                ilcursor.Index -= 2;
                ilcursor.RemoveRange(4);
            }
            else if (ilcursor.TryGotoNext(x => x.MatchLdsfld(UnhollowerSupport.MethodBaseToIl2CppFieldInfo(Original))))
            {
                ilcursor.Remove();
            }
            else
            {
                MelonLogger.Error("Harmony Patcher could not rewrite Il2Cpp Unhollowed Method. Expect a stack overflow.");
                return(method);
            }
            ilcursor.Emit(Mono.Cecil.Cil.OpCodes.Ldc_I8, copiedMethodInfoPointer.ToInt64());
            ilcursor.Emit(Mono.Cecil.Cil.OpCodes.Conv_I);
            return(method);
        }
Ejemplo n.º 16
0
        private static void DecryptMethods(MethodDefinition methodDefinition, MethodBase invokeMethod,
                                           object fieldInstance)
        {
            if (methodDefinition.CilMethodBody == null)
            {
                return;
            }
            var instructions = methodDefinition.CilMethodBody.Instructions;

            if (instructions.Count < 9)
            {
                return;
            }
            if (instructions[0].OpCode.Code != CilCode.Ldsfld)
            {
                return;
            }
            if (((FieldDefinition)instructions[0].Operand).FullName != "i <Module>::Invoke")
            {
                return;
            }
            ToRemove.Add(instructions[3].Operand);
            Hooks.MethodBase = _assembly.ManifestModule.ResolveMethod(methodDefinition.MetadataToken.ToInt32());
            var index            = instructions[1].GetLdcI4Constant();
            var dynamicMethodDef =
                new DynamicMethodDefinition(_module, invokeMethod.Invoke(fieldInstance, new object[] { index }));

            methodDefinition.CilMethodBody = dynamicMethodDef.CilMethodBody;
        }
        public void Load()
        {
            // Optional: Disable achievements, stats and terminal.

            // Before hooking Stats.Increment, check if the method is empty.
            // Hooking empty methods causes issues on Linux versions notably, and Stats.Increment is empty in non-Steam versions of the game.
            using (DynamicMethodDefinition statsDmd = new DynamicMethodDefinition(typeof(Stats).GetMethod("Increment"))) {
                int instructionCount = statsDmd.Definition.Body.Instructions.Count;
                if (instructionCount > 1)
                {
                    // the method has more than a lonely "ret", so hook it.
                    On.Celeste.Stats.Increment += Stats_Increment;
                }
            }

            // Before hooking Achievements.Register, check the size of the method.
            // If it is 4 instructions long, hooking it is unnecessary and even causes issues.
            using (DynamicMethodDefinition statsDmd = new DynamicMethodDefinition(typeof(Achievements).GetMethod("Register"))) {
                int instructionCount = statsDmd.Definition.Body.Instructions.Count;
                if (instructionCount > 4)
                {
                    On.Celeste.Achievements.Register += Achievements_Register;
                }
            }
        }
Ejemplo n.º 18
0
        internal MethodPatcher(MethodBase original, MethodBase source, string harmonyInstanceID, List <MethodInfo> prefixes, List <MethodInfo> postfixes, List <MethodInfo> transpilers, List <MethodInfo> finalizers)
        {
            if (original == null)
            {
                throw new ArgumentNullException(nameof(original));
            }

            this.original          = original;
            this.source            = source;
            this.harmonyInstanceID = harmonyInstanceID;
            this.prefixes          = prefixes;
            this.postfixes         = postfixes;
            this.transpilers       = transpilers;
            this.finalizers        = finalizers;

            Memory.MarkForNoInlining(original);

            if (Harmony.DEBUG)
            {
                FileLog.LogBuffered($"### Patch {original.FullDescription()}");
                FileLog.FlushBuffer();
            }

            idx = prefixes.Count() + postfixes.Count() + finalizers.Count();
            firstArgIsReturnBuffer = NativeThisPointer.NeedsNativeThisPointerFix(original);
            returnType             = AccessTools.GetReturnedType(original);
            patch = CreateDynamicMethod(original, $"_Patch{idx}");
            if (patch == null)
            {
                throw new Exception("Could not create dynamic method");
            }

            il      = patch.GetILGenerator();
            emitter = new Emitter(il);
        }
Ejemplo n.º 19
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;
        }
Ejemplo n.º 20
0
        public void TestILDasmToString()
        {
            var testObject = typeof(ILDasmToStringObject);
            var testMethod = testObject.GetMethod(nameof(ILDasmToStringObject.Test));
            var dmd        = new DynamicMethodDefinition(testMethod);

            Assert.AreEqual(expected.Replace("\r\n", "\n"), dmd.Definition.Body.ToILDasmString().Replace("\r\n", "\n"));
        }
Ejemplo n.º 21
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();
        }
Ejemplo n.º 22
0
		/// <summary>Returns the methods unmodified list of CodeInstructions</summary>
		/// <param name="original">The original method</param>
		/// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param>
		/// <returns>A list containing all the original CodeInstructions</returns>
		public static List<CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator)
		{
			// Create a copy
			var dmd = new DynamicMethodDefinition(original);
			// Create a manipulator to obtain the instructions
			var manipulator = new ILManipulator(dmd.Definition.Body);
			generator = new CecilILGenerator(dmd.GetILProcessor()).GetProxy();
			return manipulator.GetInstructions(generator);
		}
Ejemplo n.º 23
0
        /// <summary>Returns the methods unmodified list of code instructions</summary>
        /// <param name="original">The original method/constructor</param>
        /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param>
        /// <returns>A list containing all the original <see cref="CodeInstruction"/></returns>
        ///
        public static List <CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator)
        {
            var method = new DynamicMethodDefinition($"{original.Name}_Dummy{Guid.NewGuid()}", typeof(void), new Type[0]);

            generator = method.GetILGenerator();
            var reader = MethodBodyReader.GetInstructions(generator, original);

            return(reader.Select(ins => ins.GetCodeInstruction()).ToList());
        }
Ejemplo n.º 24
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;
        }
Ejemplo n.º 25
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>;
        }
Ejemplo n.º 26
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;
            }
        }
Ejemplo n.º 27
0
        private bool RedirectDMD(MethodBase methodBase, Delegate callback)
        {
            var endPoint = GetHookEndPoint(methodBase);

            var dmd = new DynamicMethodDefinition(methodBase);

            dmd.Reload(null, true);
            ReplaceDMD(endPoint, dmd);

            return(true);
        }
Ejemplo n.º 28
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>());
     }
 }
Ejemplo n.º 29
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"));
            }
        }
Ejemplo n.º 30
0
        internal static DynamicMethodDefinition CreateDynamicMethod(MethodBase original, string suffix)
        {
            if (original == null)
            {
                throw new ArgumentNullException(nameof(original));
            }
            var patchName = original.Name + suffix;

            patchName = patchName.Replace("<>", "");

            var parameters     = original.GetParameters();
            var parameterTypes = parameters.Types().ToList();

            if (original.IsStatic == false)
            {
                if (AccessTools.IsStruct(original.DeclaringType))
                {
                    parameterTypes.Insert(0, original.DeclaringType.MakeByRefType());
                }
                else
                {
                    parameterTypes.Insert(0, original.DeclaringType);
                }
            }

            var firstArgIsReturnBuffer = NativeThisPointer.NeedsNativeThisPointerFix(original);

            if (firstArgIsReturnBuffer)
            {
                parameterTypes.Insert(0, typeof(IntPtr));
            }
            var returnType = firstArgIsReturnBuffer ? typeof(void) : AccessTools.GetReturnedType(original);

            var method = new DynamicMethodDefinition(
                patchName,
                returnType,
                parameterTypes.ToArray()
                );

#if NETSTANDARD2_0 || NETCOREAPP2_0
#else
            var offset = (original.IsStatic ? 0 : 1) + (firstArgIsReturnBuffer ? 1 : 0);
            for (var i = 0; i < parameters.Length; i++)
            {
                var param = method.Definition.Parameters[i + offset];
                param.Attributes = (Mono.Cecil.ParameterAttributes)parameters[i].Attributes;
                param.Name       = parameters[i].Name;
            }
#endif

            return(method);
        }