纯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日

相关文章

  • 开源.NetCore通用工具库Xmtool使用连载 – 散列算法篇

    【Github源码】 《上一篇》详细介绍了Xmtool工具库中的加解密类库,今天我们继续为大家介绍其中的散列算法类库。 散列算法在某些特殊场景也可以当做加密方法使用;其特点是不可逆,同一内容每次散列值绝对一致,所以也可用作对数据内容是否被篡改的校验方法;或者其他需要唯一性编码的场景;本类库提供了MD5、SHA1、SHA256、SHA384、SHA512等常用…

    C# 2023年5月9日
    00
  • 聊聊Unity 自定义日志保存的问题

    针对“聊聊Unity自定义日志保存的问题”,我可以提供以下完整攻略: 1. 了解Unity自带的日志系统 Unity自己的日志系统提供了五个级别的日志输出,分别是:Log、Warning、Error、Assert和Exception。日志输出的级别可以通过Debug.unityLogger.filterLogType属性来控制。我们可以通过在代码中使用Deb…

    C# 2023年5月15日
    00
  • C#使用HttpPost请求调用WebService的方法

    下面我会详细讲解在C#中使用HttpPost请求调用WebService的方法,包含以下几个步骤: 创建C#客户端代理类 设置WebService的URL和相应的方法名 准备请求参数 发送HttpPost请求 解析并处理响应数据 具体步骤如下: 1. 创建C#客户端代理类 首先,在Visual Studio中以项目方式打开C#工程,然后右键单击工程名称,选择…

    C# 2023年5月15日
    00
  • C# 无需COM组件创建快捷方式的实现代码

    下面我将详细讲解如何使用C#来实现无需COM组件创建快捷方式的实现代码。 什么是COM组件 COM(Component Object Model)组件是一种通用的二进制接口标准,允许不同语言和平台之间的软件互操作。创建快捷方式的COM组件一般为Windows Script Host。 使用C#实现快捷方式 在C#中,我们可以使用Shell对象来访问Windo…

    C# 2023年6月7日
    00
  • 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序报错的解决办法

    当在本地计算机上使用Microsoft Office相关库时,可能会出现“未在本地计算机上注册microsoft.ACE.oledb.12.0”提供程序的报错。这是由于缺少相关的驱动程序或者未安装相应的软件所导致的。下面是解决该问题的完整攻略。 1. 确认公司计算机已安装“Microsoft Access Database Engine” “Microsof…

    C# 2023年5月15日
    00
  • ASP.NET JSON字符串与实体类的互转换示例代码

    我根据这个主题给出一份攻略。 引言 ASP.NET 是一种用于构建 Web 应用程序的框架,而 JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,常用于前后端之间的数据传递。在 ASP.NET 中,我们有时候需要将 JSON 字符串转换为实体类,或者将实体类转换为 JSON 字符串。在这里,我们将会通过两个示例来…

    C# 2023年5月31日
    00
  • IIS下调用证书出现异常的解决方法 (C#)

    针对“IIS下调用证书出现异常的解决方法(C#)”这个问题,我来给出一份完整的攻略,步骤如下: 步骤一:检查证书是否安装正确 在IIS服务器上,需要将证书正确地安装并且匹配相应的网站。所以,第一步是确保证书在服务器上正确地安装了,以及是否与网站匹配。如果证书未正确安装或匹配,将会出现调用证书异常的问题。你可以通过以下步骤来检查证书是否安装正确: 打开IIS …

    C# 2023年5月15日
    00
  • C# 通过 inline-asm 解决嵌入x86汇编

    首先,C# 通过 inline-asm 解决嵌入x86汇编,需要使用 __asm 关键字,在 C# 程序中编写 inline 汇编代码。 例如,下面是一个使用 inline-asm 在 C# 程序中调用 x86 汇编代码的示例: unsafe public static void InlineAsmTest() { int result = 0; // 内联…

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