65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 .NET 实现全局可拦截程序和系统挂钩

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (22投票s)

2014 年 8 月 5 日

MIT

14分钟阅读

viewsIcon

47981

downloadIcon

3795

介绍如何在 .NET 中实现全局可拦截挂钩

引言

本文将介绍一种在托管 .NET 中实现所有全局窗口挂钩类型的方法,可以在系统消息被目标应用程序处理之前拦截和修改它们。

背景

对于所有不了解挂钩是什么以及为什么需要它们的人,这里有一些快速信息

引用

挂钩是系统消息处理机制中的一个点,应用程序可以在此点安装一个子例程来监视系统中的消息流量,并在某些类型的消息到达目标窗口过程之前对其进行处理。

如果您不想阅读有关挂钩的全部文档,请在继续之前查看以下文章

  1. 在 .NET 中使用全局系统挂钩
  2. 使用 C# 中的挂钩

我做这件事的动机是,当我听说挂钩和窗口消息时,我在网上搜索,想找到 C# 中全局挂钩的实现。大多数挂钩是鼠标和键盘挂钩,因为它们不需要非托管 DLL。然后我发现了上述文章,它们做了更多的事情,但仍然不是全部。让我们引用 MSDN

引用

.NET Framework 不支持全局挂钩 您无法在 Microsoft .NET Framework 中实现全局挂钩。

这听起来像是一个挑战。

在此,我想描述文章标题的命名法

  • 全局:挂钩过程加载到系统中的每个进程中。
  • 系统:如果发生系统事件,将调用挂钩过程。
  • 程序:挂钩过程加载到一个特定的进程中。

最有用的挂钩

有些挂钩具有特殊属性

  • WH_CBT:代表计算机辅助培训,在激活、创建、销毁、最小化、最大化、移动或调整窗口大小时,以及在移动鼠标或按下鼠标时被调用。它还可以阻止这些操作。
  • WH_GETMESSAGE:在消息到达窗口之前调用,并且可以拦截和修改发送到窗口的消息(在示例应用程序中使用)。WH_CALLWNDPROCRET 可以检查目标应用程序对此消息返回了什么返回值。
  • WH_DEBUG:可能非常有用,在调用任何其他 hooktype 之前调用。如果设置全局调试挂钩,您可以看到操作系统执行的每个窗口消息和每个系统操作(GUI,而非网络或文件)。

目标

让我们快速浏览一下我们的目标

.NET 挂钩类应实现

  • 为每种本机窗口挂钩类型提供一个**事件**,以及与挂钩相关的所有信息。
  • **监控****特定**进程或系统中的所有进程(**全局**)。
  • 一个**翻译器** .ToString() 方法,它将 hookproc 回调中的**所有信息**提取为单个人类可读的字符串。
  • 一种挂钩和取消挂钩的方式。以及**更改和拦截窗口消息**。
  • 使类的使用**直观**,并去除所有不必要的负担。

所有这些如何实现?很简单。

我们需要两个项目

  1. 一个具有一些导出的本机 C++ DLL
  2. 一个 C# 项目,它实现了对 1) 的封装器以及一些 Pinvoke 函数

首先,让我们看看我们的目标

var hook = new Hook(HookType.WH_CBT, true);
hook.HookTriggered += hook_HookTriggered;

void hook_HookTriggered(HookArguments Msg, ref bool Intercept)
{
     var msg = new WH_CBT(Msg);
     Console.WriteLine(msg.ToString());
}

以及一个通用挂钩

var hook = new Hook<WH_CBT>(true);

void hook_HookTriggered(WH_CBT Message, ref bool Intercept)
{
    Console.WriteLine(Message.ToString());
}

1) 深入底层

要挂钩另一个窗口的消息,没有办法绕过将非托管 DLL 加载到目标进程中,并重定向/拦截即将被 messageloop 接收的消息。幸运的是,Windows API 提供了丰富的函数集,可以轻松挂钩。最重要的函数是 SetWindowsHookEx()。此函数甚至具有全局挂钩的可能性。全局挂钩会将此 DLL 加载到每个可用的进程中。此 DLL 不能用托管代码编写,因为大多数进程不运行 .NET Framework。

对于两种类型的挂钩,编写本机 DLL 不是必需的:您仅在自己的应用程序(本地挂钩)中使用的挂钩,以及挂钩到键盘或鼠标的挂钩(WH_KEYBOARD_LL, WH_MOUSE_LL)。 无论如何,此 DLL 必须设计为可以在每个进程中并行工作。这是托管 .NET 功能性包装器的关键部分。

让我们开始吧。

要做的第一件事是定义一个 DLL 导出方法供 .NET 稍后使用

请记住,此 DLL 会被多次加载。这就是为什么我们需要一些共享数据段

// Shared data among all DLL instances.
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
HWND g_hWnd = NULL;   // Window handle
HHOOK g_hHook = NULL; // Hook handle
INT hooktype = 0;
#pragma data_seg()

DWORD WINAPI SetHook(int HookType, BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)
{
    BOOL bOk = FALSE;
    g_hWnd = hWndCaller;
    g_hHook = NULL;

    if (bInstall)
    {
        g_hHook = ::SetWindowsHookEx(HookType, AllHookProc, 
                                     ModuleFromAddress(AllHookProc), dwThreadId);
        hooktype = HookType;

        bOk = (g_hHook != NULL);
    }
    else
    {
        bOk = ::UnhookWindowsHookEx(g_hHook);
        g_hHook = NULL;
    }

    return GetLastError();
}

此函数基本上是对 Windows.h ::SetWindowsHookEx 的封装。SetHook 函数将我们的 C# 应用程序窗口的句柄保存到共享数据段。此 DLL 反射其自身的模块,使用 ModuleFromAdress,并加载到一个或多个进程中。此方法返回 LastWin32Error,以便在 C# 代码中知道发生了什么错误。

下一步是定义 AllHookProc 函数,该函数应处理所有窗口挂钩回调。不变于 HookType 说明符。首先,让我们考虑所有挂钩共有的内容以及我们想要发送的额外信息。让我们看看每个挂钩是如何定义的:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644959%28v=vs.85%29.aspx#procedures

引用
LRESULT CALLBACK HookProc(
  int nCode,
  WPARAM wParam,
  LPARAM lParam
)
{
   // process event
   ...

   return CallNextHookEx(NULL, nCode, wParam, lParam);
}

有趣。每个挂钩都有一个 nCode 和两个 pointers 以及一个 LRESULTintpointer)作为结果。LRESULT 值**应**(我们将打破这个规则来拦截)始终是 CallNextHookEx(NULL, nCode, wParam, lParam);。这两个指针可能是实际信息,或者它们可能是指向包含更多指针和数据的结构的指针等等。由于历史原因,指针被命名为 WideParamLongParam

当挂钩被触发时,会调用 HookProc() 函数。但现在怎么办?我们不是在自己的进程中执行,您还记得吗?我们的 DLL 在远程应用程序上执行,那么我们如何将数据传回我们的应用程序呢?嗯,我找到了一个救命的窗口消息,称为 WM_COPYDATA。这听起来很像进程间通信,而且确实如此。

引用

应用程序发送 **WM_COPYDATA** 消息以将数据传递给另一个应用程序。

关于 IPC,我不知道除了套接字、管道和共享内存之外还有其他东西。此窗口消息非常方便,因为它会自动将任何结构编组到另一个进程。我们找到了发送数据到另一个进程的方法。但是我们要发送什么?

当然是越多越好

HOOKDLL_API typedef struct AllHookMSG
{
    INT HookType;

    INT nCode;
    WPARAM wParam;
    LPARAM lParam;
    DWORD Process;

    time_t Time;
    INT MilliSecond;

} HookMsg, *PHookMsg;

此结构可以由您随意扩展。此 struct 非常有用,因为它可以在可能的情况下收集尽可能多的**信息**,因为您实际上是在**另一个应用程序域**中执行自己的代码。

我选择发送 hookproc 的所有参数以及确切时间以及 ProcessIdProcessId 对于 .NET 项目非常重要,当您想找出实际调用此挂钩的进程时。

现在到实际的 hookproc 函数

// Hook callback called when hook triggers. 
LRESULT CALLBACK AllHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0) //this is a must (see hookproc documentation)
    {
        return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
    }

    SYSTEMTIME st;
    GetSystemTime(&st);

    HookMsg info;

    info.lParam = lParam;
    info.wParam = wParam;
    info.nCode = nCode;

    info.Time = time(0);
    info.MilliSecond = st.wMilliseconds;
    info.Process = GetCurrentProcessId();
    info.HookType = hooktype;

    
    COPYDATASTRUCT InfoBoat;

    InfoBoat.lpData = (PVOID)&info;
    InfoBoat.cbData = sizeof(info);
    InfoBoat.dwData = 0;

    //very important line:
    BOOL Intercept = SendMessage(g_hWnd, WM_COPYDATA, 0, (LPARAM)&InfoBoat); 

    if (Intercept)
    {
        if (info.HookType == WH_CBT) return -1;
        return -1;
    }

    return  ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

前 20 行收集信息并将其格式化以便与 WM_COPYDATA 传输。我称之为 Infoboat。http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx

SendMessage 这一行非常重要。SendMessage 会阻塞直到 Message 被处理(与 PostMessage 不同)。SendMessage 正是我们需要的。这保证了进程不会收到任何消息,GUI 线程被阻塞,最重要的是,**上下文切换到了我们的 C# 应用程序**。

这一行也给了我们**拦截**挂钩链的机会(消息将不会传递给进程)。有些挂钩是可拦截的,有些则不是,我们无法改变这一点。SendMessage 会返回一个值,这个值在我们 C# 项目中定义。

高级读者可能会注意到,如果我们**拦截**了 HookProc,它会返回 -1,如果 HookTypeWH_CBT,它也返回 -1。在两种情况下,我们都不会调用下一个挂钩。这证明您必须始终保持怀疑,即使对于最值得信赖的来源,因为 Microsoft 文档明确说明

引用

对于对应于以下 CBT 挂钩代码的操作,返回值必须为 0 以允许该操作,或 1 以阻止该操作。

经验测试表明,仅返回 -1 才能阻止该操作。(这可以用来阻止窗口显示或销毁。基本上,您无法在目标应用程序中打开或退出任何窗口。)

您可能注意到的另一件事是,wParamlParam 是按值复制的。这些指针在 .NET 进程中肯定无效。这个问题将在 .NET 项目中得到解决。

我们已经可以编写一个使用此 DLL 来使用所有 hooktypes 的 C++ 项目。但这并非目标。

2).NET 项目

我们首先要做的就是实现我们自己的函数和 structs(在一个名为 HookDll静态类中)。

[StructLayout(LayoutKind.Sequential)]
struct AllHookMSG
{
    public int HookType;

    public int nCode;
    public IntPtr wParam;
    public IntPtr lParam;
    public uint Process;

    public long Time;
    public int MilliSecond;
}

[
       DllImport("HookDll.dll", CharSet = CharSet.Auto,
       EntryPoint = "?SetHook@@YGKHHKPAUHWND__@@@Z",
       ExactSpelling = false, CallingConvention = CallingConvention.StdCall)
]
public static extern uint SetHook(int HookType, bool bInstall, 
       [MarshalAs(UnmanagedType.U4)] UInt32 dwThreadId, IntPtr hWndCaller);

第二件事我们需要知道的是,只有窗体(win32 窗口)才能接收窗口消息。正如我们上面所见,我们希望接收我们的 Inforboat(AllHookMsg)。如果我们想从控制台应用程序中使用我们的类怎么办?简单,我们定义自己的 Messageloop 类,它继承自 form。

在那里,我们可以重载 msgproc 方法并过滤 WM_COPYDATASTRUCT

    class MessageLoop : Form
    {
        int filter = 0;

        public IntPtr hWnd
        {
            get { return base.Handle; }
        }

        public MessageLoop(WindowsMessages Filter) : base ()
        {
            filter = (int)Filter;

                base.FormBorderStyle = FormBorderStyle.FixedToolWindow;
                base.ShowInTaskbar = false;
                base.StartPosition = FormStartPosition.Manual;
                base.Location = new System.Drawing.Point(-2000, -2000);
                base.Size = new System.Drawing.Size(1, 1);
                base.Show();
        }

        protected override void WndProc(ref Message m)
        {
            bool Intercept=false;

            if (m.Msg==filter&&MessageCallback!=null)
            {
                MessageCallback(ref m,ref Intercept);
            }

            base.WndProc(ref m);

            if (Intercept)
            {
                m.Result = new IntPtr(1);
            }
        }

        public event dWndProc MessageCallback;
        public delegate void dWndProc(ref Message m,ref bool Intercept);
    }

这是整个 Messageloop 类实现,所以您可以直接复制并使用它。我们不继承 hook 自 form,因此我们没有所有重载。Messageloop 为我们创建了一个窗口,并完全将其隐藏起来。它还有一个用于接收 windowsmessageseventhandler。它还会过滤我们的 WH_COPYDATASTRUCT 消息。非常方便。

我们还看到了在 WndProc(ref Message m) 中返回 1 的可能性。如果您还记得 DLL 声明,其中说明:Intercept=SendMessage()。因此,通过将 m.Result 设置为 1,我们也告诉本机 DLL 拦截消息。

挂钩类

现在我们为 hook 类准备了几乎所有东西。我们需要定义所有 hooktype windows 可以挂钩的类型

    public enum HookType : int
    {
         WH_CALLWNDPROC = 4,
         WH_CBT = 5,
         WH_SYSMSGFILTER = 6,
         WH_MOUSE = 7,
         ....
    }

并非所有东西都已准备就绪,我们可以构建我们最终的 hook

MessageLoop MessageHandler;
public HookType HookType; 

        public Hook(HookType hooktype, Process ToWatch)
        {
            MessageHandler = new MessageLoop(WindowsMessages.WM_COPYDATA);
            MessageHandler.MessageCallback += MessageHandler_WndProc;

            HookType = hooktype;

            uint TID = (uint)ToWatch.Threads[0].Id;

            if (HookType == HookType.WH_SYSMSGFILTER) { TID = 0; }

            uint HookEnabled = HookDll.SetHook((int)HookType, 
                               true, TID, MessageHandler.Handle);
            if (HookEnabled != 0) { throw new Win32Exception((int)HookEnabled); }
        }

        public void Dispose()
        {
            HookDll.SetHook(0, false, 0, IntPtr.Zero);
            MessageHandler.Dispose();
        }

这是 SetHook 的另一个包装器。所有这些抽象都是为了让事情尽可能简单。我们可以清楚地看到,现在我们可以挂钩到一个特定的目标进程。ThreadID 参数是进程的第一个线程(必须包含一个窗口),我们还设置了我们自己定义的 messageloop 的句柄。全局挂钩是此构造函数的另一个重载,并将 TID 设置为 0。这将把本机 DLL 挂钩到所有当前打开的窗口。

只有当 WM_COPYDATASTRUCT 消息进入 messageloop 时,MessageCallback 才会被触发,我们定义自己的 messagecallback 如下

        void MessageHandler_WndProc(ref Message m, ref bool Intercept)
        {
            if (HookTriggered == null) return;

            var InfoBoat = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));
            var HookInfo = (AllHookMSG)Marshal.PtrToStructure(InfoBoat.lpData, typeof(AllHookMSG));

            var time = new System.DateTime(1970, 1, 1).AddSeconds(HookInfo.Time).ToLocalTime().AddMilliseconds(HookInfo.MilliSecond);
            var process = Process.GetProcessById((int)HookInfo.Process);

            //All above variables go into one wrapper class (HookEvent)
            //This is our HookTriggered event which feeds everything to the user
            HookTriggered(HookEvent AllAboveVariables,ref Intercept);
        }

幸运的是,如上所述,windows 会将我们的 struct 编组到我们自己的内存中,因此当我们调用 Marshal.PtrToStructure 时一切正常。首先,我们提取 AllHookMsg 的指针,然后提取它。现在我们拥有了所有需要的东西。一个全局或特定的挂钩,一种拦截方式,以及与挂钩相关的所有信息。时间、进程以及 hookproc 的信息。当然。

那么我们现在做什么?**提取所有信息。**

翻译器类

现在我们有一个很大的问题。我们没有从 HookTriggered 事件中获得任何信息。有 lParamwParam 以及 nCode。**但没有信息**。

幸运的是,有一个变通方法。我们知道目标应用程序现在无法接收任何新消息,因为它仍然阻塞在 SendMessage 中。它无法接收任何东西,因此无法覆盖任何东西。lParamwParam**指向我们目标(远程)进程中的一个有效结构**。这些struct取决于hooktype,并且都记录在 MSDN 中。这里有一个例子

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644976%28v=vs.85%29.aspx

引用
lParam [in]

类型:LPARAM

指向 CWPRETSTRUCT 结构的指针,该结构包含有关消息的详细信息。

该解决方案之所以有效,是因为我们知道目标线程被阻塞了。我们可以直接**读取目标应用程序的进程内存**。请注意:所有翻译的消息在我们的事件方法之外都无效。这样,我们就不需要目标应用程序的合作,不需要 COM 对象,也不需要额外的 IPC。

 [DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess
        (int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll")]
        static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, 
                      byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

        const int PROCESS_VM_READ = 0x0010;
        const int PROCESS_VM_WRITE = 0x0020;
        const int PROCESS_VM_OPERATION = 0x0008;

这两个函数将一个 datablock 从任何进程复制到我们自己的进程。我们现在需要一种方法将其从 byte[] 转换为任何 struct。这可以通过泛型来完成

public static T GetStructFromProcess<T>(Process Process,IntPtr Address) where T:struct
        {
            IntPtr ProcessHandle = OpenProcess(PROCESS_VM_READ, false, Process.Id);

            int bytesrecieved = 0;
            byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
            bool Ok=ReadProcessMemory(ProcessHandle.ToInt32(), 
                    Address.ToInt32(), buffer, buffer.Length, ref bytesrecieved);
            if (!Ok) { throw new Win32Exception(Marshal.GetLastWin32Error()); }
            
            return MarshalHelper.DeserializeMsg<T>(buffer);
        }

        static T DeserializeMsg<T>(Byte[] data) where T : struct
        {
            int objsize = Marshal.SizeOf(typeof(T));
            IntPtr buff = Marshal.AllocHGlobal(objsize);
            Marshal.Copy(data, 0, buff, objsize);
            T retStruct = (T)Marshal.PtrToStructure(buff, typeof(T));
            Marshal.FreeHGlobal(buff);
            return retStruct;
        }

我还编写了一个 WriteStructToProcess 方法,该方法理论上可以更改目标进程中的任何窗口消息和结构,即使是那些标记为不可拦截的。

现在我们将此代码放入一个助手类(MarshalHelper)中,并且只需调用

CWPRETSTRUCT IsWMCOPY = MarshalHelper.GetStructFromProcess<CWPRETSTRUCT>
                        (process, PassData.lParam);

我们就快完成了。我为每种 HookType 创建了一个 messagetranslator 类(称为 WH_HookType),并重写了 .ToString() 方法,使其更易于人类阅读。因此,任何挂钩使用的struct都在 System.Hooks 命名空间中。

所以,这是 HookType.WH_MOUSE 的示例翻译器,称为 WH_MOUSE

    public class WH_MOUSE : IHook
    {
        public int Code { get; private set; }
        public IntPtr wParam { get; private set; }
        public IntPtr lParam { get; private set; }
        public Process Caller { get; private set; }
        public DateTime Time { get; private set; }
       
        new public const string Description = "The system calls this function whenever 
        an application calls the GetMessage or PeekMessage function and there is a 
        mouse message to be processed. ";

        public override bool InterceptEffective
        {
            get
            {
                return true;
            }
        }

        public INPUT_Messages Attachment
        {
            get
            {
                return (INPUT_Messages)Code;
            }
        }

        public MouseMessages MouseMessage
        {
            get { return (MouseMessages)wParam; }
        }

        public bool KeyIsDown
        {
            get { return !Convert.ToBoolean(lParam.ToInt32() & (1 << 30));}
        }

        public Win32Window Above
        {
            get { return new Win32Window(MouseData.hwnd);}
        }

        public MOUSEHOOKSTRUCT MouseData
        {
            get {return MarshalHelper.GetStructFromProcess<MOUSEHOOKSTRUCT>(Caller, lParam);}
        }

        public override string ToString()
        {
            return MouseMessage + " event @ " + MouseData.pt + 
            " above " + (HitTest)MouseData.wHitTestCode+" at "+Caller.ProcessName;
        }
        public WH_MOUSE(HookArguments Msg): base(Msg)
        {
            if (Msg == null) { return; }
            
            this.Code = Msg.nCode;
            this.wParam = Msg.wParam;
            this.lParam = Msg.lParam;
            this.Caller = Msg.Process;
            this.Time = Msg.TimeStamp;
        }
    }

我创建了 12 个这样的类,以使每个挂钩回调都易于人类阅读,并更容易编程或过滤特定事件。

这是 WH_CBT 的示例输出

示例应用程序

如果您只想自己尝试一下,请查看下载链接。

基本上,我们想使用我们的功能,并拦截和更改我们对远程记事本进程的输入。(我们不会使用 WH_KEYBOARD_LL)。

我们创建一个新的窗体,然后挂钩一个 WH_GETMESSAGE 挂钩。为此,我们不必将 Intercept 设置为 true,但我们可以直接设置 hook.message(此 hooktype 的特殊性)。

我们将监视 WM_CHAR 事件,因为这是一个在按下字符到达记事本之前触发的事件。

using System.Hooks

//constructor
var k = new Hook(HookType.WH_GETMESSAGE, NotepadProcess);
k.HookTriggered += k_HookTriggered;

//hookcallback
void k_HookTriggered(HookArguments Msg, ref bool Intercept)
        {
            var hook = new WH_GETMESSAGE(Msg);

            if (hook.Message.Msg == (int)WindowsMessages.WM_CHAR)
            {
                IntPtr character = new IntPtr(HelloWorld());

                hook.Message = Message.Create(hook.Message.HWnd, 
                               hook.Message.Msg, character, hook.Message.LParam);
            }
        }

我们将 WM_CHAR 中的 char 更改为我们自己的字符(字符代码是一个 IntPtr)。

备注

如果在调试时设置全局挂钩,则无法设置断点,因为这会阻止所有窗口刷新其内容。您必须从任务管理器中杀死挂钩应用程序,一切都会恢复正常。

想法

以下是我们可以在挂钩方面做的一些事情

  • 使任何进程崩溃
  • 拦截按键、重绘和其他事件
  • 修改发送到进程的消息(在示例应用程序中使用)
  • 禁止启动某些进程(安全)
  • 更改任何 GUI 元素中的文本
  • 阻止窗口刷新其内容
  • 调试应用程序

未完成的事项

单比特信息提取

此类库是针对本机 Win32 API 创建的。我已将大部分 Pinvoke 声明复制到命名空间,但仍缺少一些包装器。特别是将编码为 lParamwParam 的单个比特的信息的翻译缺失。例如,wh_keyboard 回调的 lParam

最常见 Windows 消息的翻译

大多数挂钩回调返回指向 msg struct 的指针。它包含有关窗口消息的信息,例如 WM_CHARNTCHTEST。应该有一个翻译器类来提取 Msg struct 中的信息。

递归结构到进程读/写

如上所示,有一种方法可以将本机结构复制/读取到/从目标进程。目前,只能复制 wparamlparam 的值。如果这些值是指向其他 struct 的引用,则也应复制这些引用,而不会写入错误的内存位置。

将本机 DLL 编译为嵌入式数据

在 EXE 旁边有一个额外的文件不是最理想的解决方案,并且肯定有一种方法可以摆脱它。也许将其嵌入为资源并在运行时提取。

编译 64 位和 32 位本机 DLL

SetWindowsHookEx 方法将 32 位 DLL 注入到 32 位进程中,并将 64 位 DLL 注入到 64 位进程中。需要有两个 DLL 版本。

如果您找到任何这些问题的解决方案,请随时留下评论。

结束语

我们已经看到了如何创建一个挂钩类,该类不需要用户任何先验知识,就可以在托管 C# 程序中拦截、更改和监视全局或本地 windowsmessages

请注意使用这些类,因为它们为您提供了比纯 .NET Framework 更多的功能。

特别是全局挂钩。

它们为您提供了干扰其他进程的能力。部分代码在其他进程的命名空间中运行,部分函数直接写入进程内存。Windows 8(我测试过的版本)行为良好,不会将 globalhook DLL 注入到 taskmanager 或 explorer 中,但仍然

如果您阻止或拦截回调,任何带有窗口的进程都将崩溃。

请**不要滥用这项工作**。例如,读取密码,因为这完全可以通过 WH_GETMASSAGE 实现,或者在拦截某些鼠标事件时让用户抓狂。

如果某些工作有错误或令人困惑/不一致,请告诉我。如果您发现任何错误或有有趣的内容要展示,请留下评论。

历史

  • 2014 年 8 月 5 日:初始版本
© . All rights reserved.