关于SetWindowsHookEx()和钩子的问题

4
这里有些背景信息。我正在替换一个dll,该dll通过“AppInit_DLLs”注册表项在dll注入技术中使用。它的目的是存在于每个进程中,并设置钩子到GDI32.dll以收集有关打印的信息。这是一种有点奇特的方式来获取我们想要的结果。这个.dll本身已经超过10年了(使用Visual Studio 97编写),我们希望用比注入dll更少侵入性的东西来替换它。
看起来“SetWindowsHookEx()”可能是我们正在寻找的东西。我遇到了一些问题,但我也与同事讨论过我们是否应该继续追求这个方向。以下是我们无法确定的一些问题:
1.当我们钩取一个dll中的例程(例如GDI32.dll中的StartDoc())时,我们是否真的会在任何其他进程使用该dll中的例程时得到通知?这是我们使用注入的.dll获得的功能,我们需要相同的功能。
2.当触发钩子时,钩子处理过程是在实际调用的进程空间中运行还是在设置钩子的进程空间中运行?我的意见是它必须在调用例程的进程空间中运行。例如,如果程序从GDI32.dll调用StartDoc(),它将把钩子处理过程代码“注入”到其空间中并执行。否则,必须在调用进程和设置钩子的进程之间自动建立一些进程间通信,并且我认为这不可能是真的。此外,由于需要知道其中一个内容是调用进程的名称,因此必须使此钩子处理例程在调用进程的进程空间中运行。如果不是实际运行在该进程中,我不确定如何获取该信息。
3.如果使用.NET托管环境编写钩子处理例程,那么当钩入未使用.NET托管环境的进程时,它会出现问题吗?我们真的想在这里摆脱C++并使用C#,但是如果我们的钩子从未托管的进程中调用,会发生什么情况?如前所述,我认为我们的钩子处理例程将在最初调用已钩住例程的进程中运行。但是,如果这是真的,那么我认为如果该进程没有使用.NET运行时环境,而传入的钩子处理代码正在使用,则我们可能会遇到麻烦。
1个回答

3
  1. 是的。

  2. 通常情况下,钩子函数是在所钩住事件的进程上下文中执行的。

    成功调用SetWindowsHookEx后,操作系统会自动将包含回调函数的钩子DLL注入到满足指定钩子类型要求的所有目标进程的地址空间中。(当然,钩子代码不一定会立即被注入。)

    这个一般规则的例外是低级键盘和鼠标钩子(WH_LL_KEYBOARDWH_LL_MOUSE)。由于这些钩子类型并未注入到客户端进程中,因此回调会在最初调用SetWindowsHookEx的同一线程中调用。

  3. 为了回答第三个问题,需要牢记上述最后一点非常重要。由于低级键盘和鼠标钩子是仅有的两种不需要DLL注入的全局钩子,它们也是唯二可以用托管.NET代码编写的钩子类型。

    对于其他类型的钩子,你在问题中提出的关切是准确的。你需要使用C或C++编写这些钩子DLL。当然,应用程序的其他部分仍然可以用托管语言编写。唯一重要的是钩子DLL。

你可以考虑使用 Microsoft DetoursEasyHook


谢谢你的回答。我相信你在所有方面都是正确的。不幸的是,这也适用于问题2和3。看看我发现的这个信息小宝石。我的小心脏在这个页面的最后一刻被打破了http://support.microsoft.com/kb/318804。除了键盘和鼠标事件之外,C#中不允许使用全局钩子,就像你提到的那样。看起来我将不得不重新实现一个.dll进行注入。虽然这是一种侵入性技术,但看起来这是唯一的方法。再次感谢。 - Ultratrunks
@ultra:是的,没错。你不能将托管DLL注入到其他进程中,因为它们还没有加载CLR。由于低级键盘和鼠标钩子不是被注入的,所以它们没有这个限制,因此可以用托管代码编写。是的,这是一种侵入性的操作,但这就是使用钩子的现实。出于这个原因和其他原因,我通常不建议使用钩子。但如果你绝对需要在任何系统函数被调用时接收通知,那么DLL注入将是唯一的方法。用C++编写DLL,并从.NET应用程序中使用它。 - Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接