Ejemplo n.º 1
0
        /// <summary>
        /// Add a hook method to plugin
        /// </summary>
        /// <param name="name">Hook name</param>
        /// <param name="method">Method Info instance of the needed method</param>
        public void AddHookMethod(string name, MethodInfo method)
        {
            if (Hooks.ContainsKey(name))
            {
                Log.Warning($"Plugin {Title} tried to register an existing hook");
                return;
            }

            Hooks[name] = new HookMethod(this, name, method);
        }
        public static void AddPostfix(HookMethod postfix)
        {
            if (Data == null)
            {
                throw new InvalidOperationException("Please call SetupHook before adding postfixes.");
            }

            var invoker = new HookCallbackInvoker(Data.Method, postfix);

            Data.Postfixes.Add(invoker);
            Data.Postfixes.Sort();
        }
Ejemplo n.º 3
0
        protected override object InvokeMethod(HookMethod method, object[] args)
        {
            // TODO: Ignore base_ methods for now
            if (!hookDispatchFallback && !method.IsBaseHook)
            {
                if (args != null && args.Length > 0)
                {
                    ParameterInfo[] parameters = method.Parameters;
                    for (int i = 0; i < args.Length; i++)
                    {
                        object value = args[i];
                        if (value == null)
                        {
                            continue;
                        }

                        Type parameter_type = parameters[i].ParameterType;
                        if (!parameter_type.IsValueType)
                        {
                            continue;
                        }

                        Type argument_type = value.GetType();
                        if (parameter_type != typeof(object) && argument_type != parameter_type)
                        {
                            args[i] = Convert.ChangeType(value, parameter_type);
                        }
                    }
                }
                try
                {
                    if (DirectCallHook(method.Name, out object ret, args))
                    {
                        return(ret);
                    }

                    PrintWarning("Unable to call hook directly: " + method.Name);
                }
                catch (InvalidProgramException ex)
                {
                    Interface.Oxide.LogError("Hook dispatch failure detected, falling back to reflection based dispatch. " + ex);
                    CompilablePlugin compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, Name);
                    if (compilablePlugin?.CompiledAssembly != null)
                    {
                        File.WriteAllBytes(Interface.Oxide.PluginDirectory + "\\" + Name + ".dump", compilablePlugin.CompiledAssembly.PatchedAssembly);
                        Interface.Oxide.LogWarning($"The invalid raw assembly has been dumped to Plugins/{Name}.dump");
                    }
                    hookDispatchFallback = true;
                }
            }

            return(method.Method.Invoke(this, args));
        }
Ejemplo n.º 4
0
        public void ShouldAllowMultipleHooksInaMethod()
        {
            var expected           = GetType().GetMethod("MultiHook").FullyQuallifiedName();
            var beforeScenarioHook =
                new HookMethod("BeforeScenario", GetType().GetMethod("MultiHook"), typeof(Step).Assembly);

            Assert.AreEqual(expected, beforeScenarioHook.Method);

            var beforeSpecHook = new HookMethod("BeforeSpec", GetType().GetMethod("MultiHook"), typeof(Step).Assembly);

            Assert.AreEqual(expected, beforeSpecHook.Method);
        }
        public void ShouldAllowMultipleHooksInaMethod()
        {
            var mockAssemblyLoader = new Mock <IAssemblyLoader>();
            var mockMethod         = new MockMethodBuilder(mockAssemblyLoader)
                                     .WithName("MultipleHookMethod")
                                     .WithFilteredHook(LibType.BeforeScenario)
                                     .WithFilteredHook(LibType.BeforeSpec)
                                     .Build();


            var beforeScenarioHook = new HookMethod(LibType.BeforeScenario, mockMethod, mockAssemblyLoader.Object);

            Assert.AreEqual("MultipleHookMethod", beforeScenarioHook.Method);

            var beforeSpecHook = new HookMethod(LibType.BeforeSpec, mockMethod, mockAssemblyLoader.Object);

            Assert.AreEqual("MultipleHookMethod", beforeSpecHook.Method);
        }
Ejemplo n.º 6
0
        protected override object InvokeMethod(HookMethod method, object[] args)
        {
            //TODO ignore base_ methods for now
            if (!hookDispatchFallback && !method.IsBaseHook)
            {
                if (args != null && args.Length > 0)
                {
                    var parameters = method.Parameters;
                    for (var i = 0; i < args.Length; i++)
                    {
                        var value = args[i];
                        if (value == null) continue;
                        var parameter_type = parameters[i].ParameterType;
                        if (!parameter_type.IsValueType) continue;
                        var argument_type = value.GetType();
                        if (parameter_type != typeof(object) && argument_type != parameter_type)
                            args[i] = Convert.ChangeType(value, parameter_type);
                    }
                }
                try
                {
                    object ret;
                    if (DirectCallHook(method.Name, out ret, args)) return ret;
                    PrintWarning("Unable to call hook directly: " + method.Name);
                }
                catch (InvalidProgramException ex)
                {
                    Interface.Oxide.LogError("Hook dispatch failure detected, falling back to reflection based dispatch. " + ex);
                    var compilable_plugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, Name);
                    if (compilable_plugin != null && compilable_plugin.CompiledAssembly != null)
                    {
                        System.IO.File.WriteAllBytes(Interface.Oxide.PluginDirectory + "\\" + Name + ".dump", compilable_plugin.CompiledAssembly.PatchedAssembly);
                        Interface.Oxide.LogWarning($"The invalid raw assembly has been dumped to Plugins/{Name}.dump");
                    }
                    hookDispatchFallback = true;
                }
            }

            return method.Method.Invoke(this, args);
        }
Ejemplo n.º 7
0
        protected override object InvokeMethod(HookMethod method, object[] args)
        {
            object obj;
            object obj1;
            bool   compiledAssembly;

            if (!this.hookDispatchFallback && !method.IsBaseHook)
            {
                if (args != null && args.Length != 0)
                {
                    ParameterInfo[] parameters = method.Parameters;
                    for (int i = 0; i < (int)args.Length; i++)
                    {
                        object obj2 = args[i];
                        if (obj2 != null)
                        {
                            Type parameterType = parameters[i].ParameterType;
                            if (parameterType.IsValueType)
                            {
                                Type type = obj2.GetType();
                                if (parameterType != typeof(object) && type != parameterType)
                                {
                                    args[i] = Convert.ChangeType(obj2, parameterType);
                                }
                            }
                        }
                    }
                }
                try
                {
                    if (!this.DirectCallHook(method.Name, out obj, args))
                    {
                        this.PrintWarning(string.Concat("Unable to call hook directly: ", method.Name), Array.Empty <object>());
                        return(method.Method.Invoke(this, args));
                    }
                    else
                    {
                        obj1 = obj;
                    }
                }
                catch (InvalidProgramException invalidProgramException1)
                {
                    InvalidProgramException invalidProgramException = invalidProgramException1;
                    Interface.Oxide.LogError(string.Concat("Hook dispatch failure detected, falling back to reflection based dispatch. ", invalidProgramException), Array.Empty <object>());
                    CompilablePlugin compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, base.Name);
                    if (compilablePlugin != null)
                    {
                        compiledAssembly = compilablePlugin.CompiledAssembly;
                    }
                    else
                    {
                        compiledAssembly = false;
                    }
                    if (compiledAssembly)
                    {
                        File.WriteAllBytes(string.Concat(Interface.Oxide.PluginDirectory, "\\", base.Name, ".dump"), compilablePlugin.CompiledAssembly.PatchedAssembly);
                        Interface.Oxide.LogWarning(string.Concat("The invalid raw assembly has been dumped to Plugins/", base.Name, ".dump"), Array.Empty <object>());
                    }
                    this.hookDispatchFallback = true;
                    return(method.Method.Invoke(this, args));
                }
                return(obj1);
            }
            return(method.Method.Invoke(this, args));
        }
Ejemplo n.º 8
0
 public KeyboardHook(HookMethod hooker)
     : base(hooker, HookType.Keyboard)
 {
     KeysDown = new HashSet <Keys>();
 }
        public static void Patch(MethodBase originalMethod, HookMethod prefix, HookMethod postfix)
        {
            // WARNING: This code is in no way guaranteed to work, as it makes some wild assumptions
            //          about how the underlying machine code is being generated during JIT

            // If the code does not work it will likely crash the application immediately with a cryptic or no error message.

            // This hooking implementation works like this:
            // 1. Create a JMP at the start of the hooked method that goes to a method of an identical signature (trampoline function)
            // 2. When the function is entered, rewrite back the old code that was overwritten in step 1
            // 3. Call prefixes, then call original method, then call postfixes
            // 4. Write back in the JMP that was created in step 1

            // This is all very simple. The primary issue this library solves is how to do this through a 'nice'
            // API surface, similar to that of Harmony so that it can be used as a fallback mechanism.

            // The difficult issue is how to navigate to a method of identical signature to the original one
            // if you cannot emit any IL code (netstandard2.0 limitation) because it is difficult to
            // generate a method with the required signature at compile time, that includes types that may
            // be inaccessible.

            // This library pre-defines a set of generic classes that each has an 'Override' method, that will
            // be used as the trampoline function. Each version of the 'Override' method makes different assumptions
            // about the method signature and uses generic parameters defined on the class in order to do so.

            // The weakness of this approach is that any arguments (generated by the JITTER) in relation to generics
            // will *not* be passed to the override method, as it would usually expect. (I think that with the legacy
            // .NET JITTER in 64-bit, these are often passed as an id in the RCX register of the CPU, but this is likely
            // different for Mono and other architectures). This means that if any code is generated in the Override
            // method that requires this information that the application will immediately fail.

            // This has an especially big impact on the JITTER used in Mono, where accessing any generic arguments
            // in the override method will immediately cause application failure. This imposes a big limitation
            // on this approach in that the current implementation is only able to hook methods that return reference
            // types (or void) because it is unable to use a generic argument (TReturn) to determine the size of the variable
            // to send back to the caller (Should it use the stack? Should it use a CPU register? It does not know!).

            // Another limitation of this in Mono is that you cannot use this approach to access static fields on
            // a per-generic class basis (as that would mean using the generic arguments). To overcome this issue
            // a new assembly that maintains state for each hooked method is generated at runtime (by renaming an
            // existing assembly and loading it into memory). This is essentially what this function does.

            // A better approach than this would be to write the trampoline function in machine code. But that
            // would take a whole lot more effort than this. And require knowledge about how each architecture
            // and platform emits machine code.

            // Attempting to create a StackTrace will also break this implementation.

            lock ( Sync )
            {
                var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;

                // check if we have already hooked the specified method
                if (!HookedMethodToTrampolineInitializer.TryGetValue(originalMethod, out var trampolineInitializer))
                {
                    // obtain data for the assembly that contains the hooking logic/trampolines
                    var assemblySize = Resources.b979b301af8b4ef48f224de6dcf2ddf6.Length;

                    // copy the data into a new array
                    var assemblyData = new byte[assemblySize];
                    Buffer.BlockCopy(Resources.b979b301af8b4ef48f224de6dcf2ddf6, 0, assemblyData, 0, assemblySize);

                    // rename the assembly, so it can be loaded multiple times (in Mono)
                    var newName = Guid.NewGuid().ToString("N");
                    FindAndReplace("b979b301af8b4ef48f224de6dcf2ddf6", newName, assemblyData);
                    var assembly = Assembly.Load(assemblyData);

                    // obtain the trampoline initializer type from the dynamically named assembly
                    trampolineInitializer = assembly.GetType("XUnity.RuntimeHooker.Trampolines.TrampolineInitializer");

                    // call the setup hook method with the originalMethod to hook
                    trampolineInitializer.GetMethod("SetupHook", flags).Invoke(null, new object[] { originalMethod });

                    // add tbe method to our hooked methods dictionary, so we do not hook it again
                    HookedMethodToTrampolineInitializer.Add(originalMethod, trampolineInitializer);
                }

                // after hooking the method, lets apply our prefixes/postfixes
                if (prefix != null)
                {
                    trampolineInitializer.GetMethod("AddPrefix", flags).Invoke(null, new object[] { prefix });
                }

                if (postfix != null)
                {
                    trampolineInitializer.GetMethod("AddPostfix", flags).Invoke(null, new object[] { postfix });
                }
            }
        }
Ejemplo n.º 10
0
        private bool HookThunderbird()
        {
            log.Information("Hooking on to Thunderbird...");
            if (thunderbirdProcess != null && thunderbirdMainWindowHandle != IntPtr.Zero)
            {
                log.Information("Already hooked!");
                // Already hooked
                return(true);
            }

            thunderbirdProcess = Process.GetProcessesByName("thunderbird").FirstOrDefault();

            if (thunderbirdProcess == null)
            {
                log.Error("Cannot find the Thunderbird process to hook to.");
                // Hook failure, process does not exist
                return(false);
            }

            thunderbirdMainWindowHandle = FindMainThunderbirdWindow(thunderbirdProcess);

            if (thunderbirdMainWindowHandle == IntPtr.Zero)
            {
                log.Error("Cannot find Thunderbird's main window.");
                // Main window is lost (hidden)
                return(false);
            }
            else
            {
                log.Debug("Hooked on to window handle {@thunderbirdMainWindowHandle}", thunderbirdMainWindowHandle);
            }

            thunderbirdProcess.EnableRaisingEvents = true;
            thunderbirdProcess.Exited += Thunderbird_Exited;
            switch (Properties.Settings.Default.HookMethod)
            {
            case (int)HookMethod.UIAutomation:
                windowStateHook = new UIAutomation();
                break;

            case (int)HookMethod.Polling:
            default:
                windowStateHook = new Polling();
                break;
            }
            currentHookMethod = (HookMethod)Properties.Settings.Default.HookMethod;

            if (!windowStateHook.Hook(thunderbirdMainWindowHandle))
            {
                return(false);
            }

            windowStateHook.WindowStateChange += Thunderbird_VisualStateChanged;

            log.Debug("Attached event handlers for window.");

            // If not already hidden and is currently minimised, hide immediately
            var isIconic = User32.IsIconic(thunderbirdMainWindowHandle);

            if (trayLaunched && Properties.Settings.Default.MinimiseOnStart)
            {
                log.Information("Thunderbird launched with tray application, hiding now. {@thunderbirdShown}.", thunderbirdShown);
                HideThunderbird();
            }
            if (thunderbirdShown && isIconic)
            {
                log.Information("Thunderbird is already minimised, hiding now. {@thunderbirdShown}, {@isIconic}.", thunderbirdShown, isIconic);
                HideThunderbird();
            }

            return(true);
        }
Ejemplo n.º 11
0
 public MouseHook(HookMethod hooker)
     : base(hooker, HookType.Mouse)
 {
 }