14

How to get list of hooks installed globally in Windows (using SetWindowsHookEx API)?

lightstep
  • 765
  • 2
  • 8
  • 19

2 Answers2

8

See


Finding hooks Compared to other operations, enumerating the installed hooks is quite easy.

Thread specific hooks are recorded in a win32k per-thread data structure tagged, rather imaginatively, as THREADINFO1. This is essentially an ETHREAD/TEB like structure but one tailored specifically for user and gdi information. One of its members (aphkStart) is a 16 element array of pointers, individually they either point to NULL, or the head of a linked list of HOOK structures. Enumerating the hooks is simply a measure of walking down those chains.

For convenience, and probably so that iteration isn’t required to see if any hooks are set, the THREADINFO contains another member, fsHooks, which is a bitfield. If a bit is on, the corresponding index in the hook array is valid. Instead of 33 comparisons (16 for NULL and 17 for a for-loop), telling if there are hooks requires just one, nifty!

Global hooks, which are per desktop2, are also stored in a per-object structure, also imaginatively named (DESKTOPINFO), and are also stored in an array with an accompanying bitfield. Bridging the two is pDeskInfo, a member of THREADINFO which points to its owning DESKTOPINFO.

Despite the bellyaching in the intro, working with all these undocumented structures isn’t actually too hard in practice. The Windows 7 symbols for win32k.sys include their layouts, which is nice. The symbols for the Vista/Server 2008 era don’t though, this is where the assembly studying comes and saves the day.

Knowing what these structures look like is one thing, getting at them is another…

Having gotten our grubby mitts on them, we find HOOK structures record most of the relevant information themselves:

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;
};

You can download the software here


An overview for detecting installed global hooks follows:

  1. Call PsGetCurrentThread and get the ETHREAD structure of the current thread. ETHREAD is an opaque data structure according to the MSDN documentation.
  2. Extract the THREADINFO structure by calling PsGetThreadWin32Thread. Both of them are undocumented.
  3. Extract the DESKTOPINFO.
  4. There you can a find all the globally installed hooks. They are organized in a array. Each element is a linked list and corresponds to a specific hook (WH_*).

An overview for detecting installed local hooks follows:

  1. Given a thread ID.
  2. Call PsLookupThreadByThreadId and get the ETHREAD structure of the specified thread.
  3. Extract the THREADINFO structure by calling PsGetThreadWin32Thread.
  4. There you can a find all the locally installed hooks for the specified thread. They are organized in a array. Each element is a linked list and corresponds to a specific hook (WH_*).

You can see the source here


Plugin for Process Hacker 2 (http://processhacker.sourceforge.net), displays system hooks and able to unhook (right click menu).

Grab the Process Hacker source and compile it, then add HookTools.vcxproj to Plugins.sln. VS 2013 was used. Set your library path in VC++ directories.


or related question with answer here

But I still haven't found a reliable way to do it.

Vijay
  • 891
  • 3
  • 19
  • 35
  • 2
    *"This webpage is not available"*. Please see [How do I write a good answer?](http://stackoverflow.com/help/how-to-answer), specifically: *"Provide context for links: Links to external resources are encouraged, but please add context around the link so your fellow users will have some idea what it is and why it’s there. **Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline**."* – IInspectable Oct 27 '15 at 00:22
  • From reading those links, my impression is all approaches to do this rely on a kernel driver–the hook list is stored in kernel memory (managed by the `win32k.sys` driver). The kernel offers system calls to register (`NtUserSetWindowsHookEx`) and deregister (`NtUserUnhookWindowsHookEx`) the hooks, but there is no equivalent get system call. So you need a driver to run code in kernel space, then you can read those undocumented data structures. Since Microsoft can change their layout, any update can break your driver. It's a real pity MS don't provide a `GetWindowsHookEx` function! – Simon Kissane Jan 03 '23 at 20:51
4

One evil way would be to hook all of the hooking functions before anything else gets a chance to.

kichik
  • 33,220
  • 7
  • 94
  • 114
  • I know it is possible to install my own hook that will be called before any other, but it doesn't tell how many registered hooks follow when my hook calls CallNextHookEx. – lightstep Dec 19 '11 at 17:31
  • Not that kind of hook. Import function hook on `SetWindowsHookEx`. This way you can count when a process calls that function. – kichik Dec 19 '11 at 17:48
  • 1
    I see, but how can i be sure to hook SetWindowsHookEx before anyone calls it? – lightstep Dec 19 '11 at 21:11
  • Do it from a driver and make the best effort to make it the first driver loading to the system. – kichik Dec 19 '11 at 21:14
  • 3
    "Before anything else gets a chance to"? What's to stop something doing it before you to be able to find your hooks? – Deanna Mar 28 '13 at 09:06
  • Nothing :) It's always a cat & mouse game. Go as as deep as required by your threat model. – kichik Mar 28 '13 at 17:13
  • to detect the keylogger , could we count the hooked programs in the hook chain ? – Mahdi Amrollahi Aug 14 '15 at 08:49