纯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#中间语言及ILDASM工具用法

    C#编译器会将C#源代码编译成一个叫做CIL(Common Intermediate Language,公用中间语言)的中间语言,这个中间语言是基于栈的虚拟机语言。CIL是一种基于栈的虚拟机语言,每个CIL指令都会入栈一个或多个值,并且返回结果也会通过栈来返回。 ILDASM是一个IL(Intermediate Language,中间语言)反汇编器,允许用户…

    C# 2023年6月3日
    00
  • asp.net中XML如何做增删改查操作

    ASP.NET提供了多种操作XML的方式,例如使用Linq to XML、XmlDocument等。下面分别介绍在ASP.NET中如何利用Linq to XML和XmlDocument进行XML的增删改查操作。 使用Linq to XML操作XML 1. 增加节点 可以通过添加元素来添加一个节点。示例代码如下: XDocument xdoc = XDocum…

    C# 2023年6月6日
    00
  • ASP.Net Core3.0中使用JWT认证的实现

    ASP.NET Core 3.0中使用JWT认证的实现攻略如下: 安装必要的NuGet包 在开始之前,需要安装以下NuGet包: Microsoft.AspNetCore.Authentication.JwtBearer System.IdentityModel.Tokens.Jwt 您可以使用以下命令在命令行中安装这些NuGet包: dotnet add …

    C# 2023年5月16日
    00
  • C#图片压缩的实现方法

    下面是详细的讲解: 1. 概述 C#图片压缩一般采用的是将原图转换成JPG或PNG格式,然后使用指定的压缩质量或压缩比例进行压缩。下面我将详细讲解C#实现图片压缩的具体步骤以及代码实现。 2. 实现方法 2.1 引用命名空间 我们需要先引用System.Drawing.Imaging和System.IO这两个命名空间,这两个命名空间包括我们需要使用到的类或方…

    C# 2023年6月7日
    00
  • 浅谈C#数组(二)

    浅谈C#数组(二) 何为数组? 数组是一组数据,这组数据有着相同的数据类型。在C#中,数组可以存储同类型的元素。 声明一个数组 C#中声明一个数组,可以使用以下格式: dataType[] arrayName; 其中,数据类型可以是C#中的任意一个数据类型,如int、string等等;arrayName则代表对象的名称。 数组初始化 C#中的数组可以在声明时…

    C# 2023年5月31日
    00
  • C#中JavaScriptSerializer帮助类用法实例

    C#中的JavaScriptSerializer帮助类用于将.NET对象序列化为JSON格式字符串,或将JSON格式字符串反序列化为.NET对象。以下是使用JavaScriptSerializer帮助类的完整攻略。 步骤1:添加JavaScriptSerializer命名空间 首先,我们需要添加JavaScriptSerializer命名空间。使用方法是在.…

    C# 2023年5月31日
    00
  • C#如何通过RFC连接sap系统

    这里是C#通过RFC连接SAP系统的详细攻略。 一、前置要求 在进行RFC连接SAP系统之前,需要准备以下条件和环境: 已安装SAP GUI或SAP RFC SDK(建议使用SAP RFC SDK) 已获得SAP系统的RFC连接权限 熟悉C#编程语言 二、SAP RFC SDK介绍 SAP RFC SDK是一个允许开发人员使用C/C++或C#等语言连接到SA…

    C# 2023年5月15日
    00
  • .NET5控制台程序使用EF连接MYSQL数据库的方法

    下面是详细讲解“.NET5控制台程序使用EF连接MYSQL数据库的方法”的完整攻略: 准备工作 确认已经安装.NET5、EF(Core)等必要的环境和工具。 安装Pomelo.EntityFrameworkCore.MySql(用于支持EF连接MYSQL数据库的驱动程序)。 创建控制台程序 使用dotnet命令行工具创建一个新的.NET5控制台程序: dot…

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