纯C#实现Hook功能详解

C#实现Hook功能详解

什么是Hook

在计算机编程领域,Hook是指拦截某个操作,加入自定义的操作或者修改已有操作的过程。

Hook的种类

Windows系统中可用的Hook种类有三种:

  1. 全局钩子(Global Hook)

全局钩子会影响整个操作系统,可以拦截鼠标、键盘、消息、Shell等等所有操作,需要管理员权限安装和使用,且有一定的性能损失。

  1. 线程钩子(Thread Hook)

线程钩子只会钩住单个线程,不会影响整个操作系统,可以拦截鼠标、键盘等操作。

  1. 应用程序钩子(Application Hook)

应用程序钩子只会钩住单个应用程序,通过DLL注入的方式实现。可拦截路径、文件等。

C#实现Hook

C#语言可以通过托管代码结合C++ dll的方式实现Hook。

  1. 创建一个Class Library项目,将Hook功能封装在其中

  2. 在该项目中添加一个C++ CLR Class

在项目中添加一个类,选择C++/CLR语言,作为Hook操作的实现。在其中定义好相关的钩子事件,如键盘事件等。

  1. 在Hook类中增加Hook函数

Hook函数主要实现了Hook种类、DLL注入等。

  1. 在Hook类中增加钩子事件

定义钩子事件,该事件会在接收到需要Hook的操作时被触发。

  1. 在Hook类中增加处理函数

处理函数主要实现了Hook收到事件时需要采取的操作,如输出日志等。

示例1:全局钩子

下面是一个全局钩子示例代码:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace GlobalHook
{
    public class GlobalKeyboardHook : IDisposable
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, HookCallBack lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        delegate IntPtr HookCallBack(int nCode, IntPtr wParam, IntPtr lParam);

        const int WH_KEYBOARD_LL = 13;
        const int WM_KEYDOWN = 0x100;

        private IntPtr hookId = IntPtr.Zero;
        private HookCallBack hookProc;
        private EventHandler<KeyEventArgs> keyPressed;
        private bool disposed = false;

        public GlobalKeyboardHook()
        {
            hookProc = new HookCallBack(HookCallback);
            hookId = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, IntPtr.Zero, 0);
            keyPressed = new EventHandler<KeyEventArgs>(OnKeyPressed);
        }

        public event EventHandler<KeyEventArgs> KeyPressed
        {
            add { keyPressed += value; }
            remove { keyPressed -= value; }
        }

        protected virtual void OnKeyPressed(object sender, KeyEventArgs e)
        {
            if (keyPressed != null)
            {
                keyPressed(this, e);
            }
        }

        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                OnKeyPressed(this, new KeyEventArgs((Keys)vkCode));
            }
            return CallNextHookEx(hookId, nCode, wParam, lParam);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (keyPressed != null)
                    {
                        foreach (EventHandler<KeyEventArgs> handler in keyPressed.GetInvocationList())
                        {
                            keyPressed -= handler;
                        }
                    }
                }
                UnhookWindowsHookEx(hookId);
                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~GlobalKeyboardHook()
        {
            Dispose(false);
        }

    }
}

示例2:线程钩子

下面是一个线程钩子示例代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ThreadHookDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        [DllImport("user32.dll")]
        public static extern int SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll")]
        public static extern int CallNextHookEx(int hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern int UnhookWindowsHookEx(int hhk);

        public static int WH_MOUSE_LL = 14;
        public static int WH_KEYBOARD_LL = 13;

        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        IntPtr hookId;
        HookProc hook = MouseHookProc;

        private void button1_Click(object sender, EventArgs e)
        {
            if (hookId == IntPtr.Zero)
            {
                IntPtr hInstance = Marshal.GetHINSTANCE(this.GetType().Module);
                hookId = (IntPtr)SetWindowsHookEx(WH_MOUSE_LL, hook, hInstance, 0);
                if(hookId == IntPtr.Zero)
                {
                    MessageBox.Show("Failed to hook the mouse");
                }
                else
                {
                    button1.Text = "Unhook";
                }
            }
            else
            {
                UnhookWindowsHookEx((int)hookId);
                hookId = IntPtr.Zero;
                button1.Text = "Hook";
            }
        }

        private static int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode == 0)
            {
                int x = Marshal.ReadInt32(lParam);
                int y = Marshal.ReadInt32(lParam + 4);

                Debug.Print("X: {0}, Y: {1}", x, y);
            }
            return CallNextHookEx(0, nCode, wParam, lParam);
        }
    }
}

总结

Hook是一种非常重要的技术,可以拦截并修改操作系统的行为,改善用户的操作体验。通过上述示例代码,我们可以更深入理解Hook的实现方法,同时也可以据此开发类似的功能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:纯C#实现Hook功能详解 - Python技术站

(0)
上一篇 2023年6月6日
下一篇 2023年6月6日

相关文章

  • C#利用时间和随即字符串创建唯一的订单编号

    创建唯一的订单编号通常需要使用时间戳和随机字符串,这种方式可以避免重复订单的产生。下面我们来讲解一下如何利用C#实现这种方法。 利用时间戳生成订单编号 时间戳是指从1970年1月1日00时00分00秒到现在的总秒数。我们可以将当前时间转换为时间戳,并将时间戳作为订单编号的一部分,从而保证每个订单编号都是唯一的。 下面是一个示例代码: // 获取当前时间的时间…

    C# 2023年6月1日
    00
  • Unity实现多平台二维码扫描

    下面我就来详细讲解一下“Unity实现多平台二维码扫描”的完整攻略。 步骤一:安装生成二维码工具 本方案使用ZXing工具包,ZXing是一个基于Java的支持多格式条形码的开源工具库,支持的格式包括:一维码、二维码、QR码等常见条码格式。我们需要先安装ZXing工具包。 ZXing的安装很简单,直接在Unity项目中的Asset Store中搜索ZXing…

    C# 2023年5月31日
    00
  • AspectCore和MSDI 实现Name注册以及解析对象

    AspectCore 在注册服务这块比较简单,默认是无法根据Name去注册和解析对象,这边做一下这块的扩展 大致原理是根据自定义Name去生成对应的动态类型,然后使用委托或者对象的方式,进行注册 tips:由于底层原理的原因,无法支持Type的方式进行注册   定义好动态类型的接口,以及相关实现 1 public interface INamedServic…

    C# 2023年4月24日
    00
  • .NET 实现启动时重定向程序运行路径及 Windows 服务运行模式部署的方法

    以下是“.NET实现启动时重定向程序运行路径及Windows服务运行模式部署的方法”的完整攻略: 什么是“.NET实现启动时重定向程序运行路径及Windows服务运行模式部署的方法” “.NET实现启动时重定向程序运行路径及Windows服务运行模式部署的方法”是一种机制,帮助开发人员在.NET应用程序中实现启动时重定向程序运行路径,并在Windows服务运…

    C# 2023年5月12日
    00
  • C#窗口实现单例模式的方法

    实现单例模式的目的是确保在系统中只有一个实例可以被创建和访问。C#语言针对这个问题提供了多种解决方案,其中窗口实现单例模式的方法是一种常用的方式。 实现方法 方案一:静态实例 在C#中,使用静态字段来保存唯一一个实例,确保这个实例可以被共享。同时为了限制实例化次数,将构造函数修饰为私有的。 示例代码: public class SingletonWindow…

    C# 2023年6月6日
    00
  • C#简单实现发送socket字符串

    首先我们需要了解什么是Socket。Socket是用于网络通信的一种机制,可以实现进程之间的通信,也可以实现不同计算机之间的通信。它是一种可以处理网络通信数据的抽象概念,通常与TCP/IP协议族一起使用。 在C#中,我们可以使用Socket类实现网络通信。下面我们来详细讲解一下C#简单实现发送socket字符串的攻略。 第一步:创建Socket对象 我们可以…

    C# 2023年6月8日
    00
  • C#中通过反射将枚举元素加载到ComboBo的实现方法

    C#中通过反射将枚举元素加载到ComboBox的实现方法可以分为以下步骤: 1. 获取枚举类型 首先需要获取对应枚举类型,可以使用typeof关键字获取: Type enumType = typeof(MyEnumType); 也可以使用GetType方法获取: MyEnumType obj = new MyEnumType(); Type enumType…

    C# 2023年6月3日
    00
  • C#实现图片轮播功能的示例代码

    我来为你详细讲解如何实现C#图片轮播功能的完整攻略。 1. 准备工作 在开始编写代码之前,我们需要做一些准备工作。首先,我们需要一个Windows窗体应用程序项目(WinForms)。可以使用Visual Studio创建一个新项目,选择Visual C# > Windows桌面 > Windows窗体应用程序。 接下来,我们需要将几张图片添加到…

    C# 2023年6月6日
    00
合作推广
合作推广
分享本页
返回顶部