已安装的Windows钩子列表

14
如何在Windows中获取已全局安装的钩子列表(使用SetWindowsHookEx API)?

你不能只做这个;你想要达到什么目的? - Michael
2
这些信息有什么用处呢?如果你试图检测你的应用程序是否被挂钩,攻击者只需挂钩虚构的“EnumerateHooks”函数! - Raymond Chen
1
我想检测系统中安装的键盘记录器。 - lightstep
1
请勿在我的机器上使用,页面底部。http://www.ntinternals.org/source.php - Hans Passant
@lightstep 同感。最近遇到了一些奇怪的按键丢失问题;确实需要有官方的解决方法。 - iCodeSometime
显示剩余2条评论
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
8

请参阅:


查找钩子:与其他操作相比,枚举已安装的钩子非常容易。

线程特定钩子记录在一个Windows 32位(win32k)每个线程数据结构中,标记为THREADINFO1。这本质上是一个ETHREAD/TEB类似的结构,但专门用于用户和gdi信息。其中之一成员(aphkStart)是指针的16元素数组,它们分别指向NULL,或者HOOK结构的链接列表的头。枚举挂钩只是沿着这些链行走的措施。

为了方便起见,并且可能不需要迭代即可查看是否设置了任何挂钩,THREADINFO包含另一个成员fsHooks,这是一个位域。如果一个位打开,则钩子数组中的相应索引有效。而不是33个比较(16个用于NULL和17个用于for循环),告诉是否有挂钩仅需要一个,不错!

全局挂钩(每个桌面2)也存储在一个名为DESKTOPINFO的每个对象结构中,并且也存储在一个带有相应位域的数组中。将两者连接起来的是THREADINFO的成员pDeskInfo,它指向其拥有的DESKTOPINFO。

尽管在介绍中抱怨,但实际上使用所有这些未记录的结构并不太难。win32k.sys的Windows 7符号包括它们的布局,这很好。 Vista/Server 2008时代的符号则没有,这就是汇编研究的地方,可以解救危机。

知道这些结构看起来是一件事,而得到它们则是另一回事……

在获取了它们后,我们发现HOOK结构本身记录了大部分相关信息:

struct tagHOOK
{
    THRDESKHEAD head; // info about the creator
    struct tagHOOK* phkNext; // next entry in linked list
    int iHook; // WH_ hook type
    UINT_PTR offPfn; // RVA to hook function in ihmod library
    UINT flags; // HF_ flags (GLOBAL, ANSI)
    int ihmod;
    THREADINFO* ptiHooked; // the hooked thread
    PVOID rpDesk; // saved desktop pointer
    ULONG nTimeout :7;
    ULONG fLastHookHung :1;
};
您可以在此处下载软件:点此链接

检测已安装全局钩子的概述如下:

  1. 调用 PsGetCurrentThread 并获取当前线程的 ETHREAD 结构。根据 MSDN 文档,ETHREAD 是不透明的数据结构。
  2. 通过调用 PsGetThreadWin32Thread 提取 THREADINFO 结构(两者都是未公开的)。
  3. 提取 DESKTOPINFO。
  4. 您可以在其中找到所有已安装的全局钩子。它们以数组形式组织。每个元素都是一个链接列表,并对应于特定的钩子(WH_*)。

检测已安装本地钩子的概述如下:

  1. 给定线程 ID。
  2. 调用 PsLookupThreadByThreadId 并获取指定线程的 ETHREAD 结构。
  3. 通过调用 PsGetThreadWin32Thread 提取 THREADINFO 结构。
  4. 您可以在其中找到指定线程的所有本地安装钩子。它们以数组形式组织。每个元素都是一个链接列表,并对应于特定的钩子(WH_*)。

您可以在此处查看源代码:点此链接


Process Hacker 2(http://processhacker.sourceforge.net) 的插件,显示系统钩子并可以取消钩子(右键单击菜单)。

获取 Process Hacker 源代码并编译它,然后将 HookTools.vcxproj 添加到 Plugins.sln。使用 VS 2013。 在 VC++ 目录中设置库路径。


或者在此处查看相关问题和答案

但我尚未找到一种可靠的方法来执行此操作。


2
"此网页不可用。" 请参阅如何撰写优质答案?,特别是:“为链接提供上下文:鼓励使用外部资源的链接,但请在链接周围添加上下文,以便您的同行用户了解它是什么以及为什么存在。始终引用重要链接的最相关部分,以防目标站点无法访问或永久离线。” - IInspectable
从阅读这些链接来看,我的印象是所有实现此功能的方法都依赖于内核驱动程序 - 钩子列表存储在内核内存中(由win32k.sys驱动程序管理)。内核提供系统调用以注册(NtUserSetWindowsHookEx)和注销(NtUserUnhookWindowsHookEx)钩子,但没有等效的获取系统调用。因此,您需要一个驱动程序在内核空间运行代码,然后才能读取这些未记录的数据结构。由于Microsoft可能会更改其布局,任何更新都可能破坏您的驱动程序。真遗憾微软没有提供GetWindowsHookEx函数! - Simon Kissane

4
一种恶劣的方式是在其他任何操作之前挂钩所有的挂钩函数。

我知道可以安装自己的钩子,在任何其他钩子之前调用,但它没有说明在我的钩子调用CallNextHookEx时有多少已注册的钩子跟随。 - lightstep
1
我明白了,但是我怎么能确保在任何人调用它之前挂钩SetWindowsHookEx呢? - lightstep
请从驱动程序中实现,并尽最大努力使其成为系统加载的第一个驱动程序。 - kichik
3
在其他任何事情有机会之前,什么能阻止某些东西在你找到钩子之前就去做它呢? - Deanna
什么都没有 :) 这总是一场猫鼠游戏。根据您的威胁模型深入挖掘。 - kichik
显示剩余2条评论

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