Windows hooks detector

Update 2020/04/04: Remove dead links.

Update 2012/08/26: Added support for Windows 7.

Recently, I had to fix a problem with Microsoft Office Word. The problem was that the normal key combination Alt-Shift for changing the input language was not working. After some time I discovered that the culprit was a rather old piece of software that had installed some Windows hooks targeted at the Microsoft Office Word, which (probably) stopped the propagation of the Alt-Shift key combination before it could actually change the language.

While I was investigating this issue, I noticed that there was no available utility that could detect and report installed Windows hooks. So here I present a Windows hooks detector. It is a command line application accompanied by a system driver, that scans, detects and reports installed Windows hooks.

Also you can download a demo hook application, that installs local and global hooks for testing purposes.

The source code is on GitHub: prekageo/winhook. The software has been tested on Windows XP and Windows 7.

According to the MSDN documentation for Windows hooks:

a hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.

A Windows hook is installed by calling SetWindowsHookEx (source code) and removed by calling UnhookWindowsHookEx. It can either be installed globally or locally. When it is installed globally it intercepts messages for all threads belonging to the same desktop. When it is installed locally it intercepts messages directed to a specific thread.

The Windows API does not offer any way for an application to list the installed hooks. After a hook is installed, it is stored and managed inside kernel structures which cannot be accessed from the user mode. Thus a kernel driver is necessary to access these structures. Furthermore, the whole mechanism of installing, handling and removing hooks is undocumented, which means that there is need to reverse engineer the relevant code. So after spending some time with the IDA Pro Disassembler, SoftICE and a Windows XP image running on VMware the job was done.

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_*).

Each hook is represented by a HOOK structure, which is undocumented. The HOOK structure stores the virtual address of the handler function. If the handler resides inside a DLL, then the relative virtual address is stored and the DLL path is stored in an atom table. That’s because a DLL’s base address may be different across processes. Also, by storing the DLL path in the atom table, the system knows which DLL to load if the hook is triggered inside a new process. We could say that the hook DLL is lazily loaded into processes.

Unfortunately, this atom table is internal. Also, there is no exported function of the kernel that accesses it. So the only way to read its contents is to call directly non-exported and undocumented kernel function calls. In order to get the necessary addresses, the utility uses the Debug Help Library to load the PDB of win32k.sys. Two symbols are needed:

  • UserGetAtomName: A function that reads the above mentioned internal atom table.
  • aatomSysLoaded: An array containing the atoms of hook DLLs. The necessary offset in this array is stored in the HOOK structure.

The utility has been tested with the following versions of win32k.sys: 5.1.2600.2180, 5.1.2600.2770, 5.1.2600.5512, 6.1.7600.16385. These versions correspond to Windows XP SP2, SP3 and Windows 7. If you don’t have one of those versions and the utility crashes your system, please send me your win32k.sys for analysis. Otherwise, please inform me in order to update the supported versions.

There is one more way to get the DLL path for a hook, although it is less reliable, so I haven’t implemented it. Though, I am going to describe it. After loading a hook DLL the system stores in the PROCESSINFO structure the base address of the loaded DLL. So if the hook DLL is loaded in the inspected process, you can retrieve its base address and from the base address after traversing the module list of the process you can find the DLL path. If the hook DLL has not been loaded yet, then you are out of luck.

If you would like to compile the utility you need the Windows Driver Kit (WDK) and ddkbuild.

15 Responses to “Windows hooks detector”

  1. ekbar Says:

    A nice approach. By the way PsGetThreadWin32Thread is not working on Windows 7. Any suggestions???

    Thanks

    • prekageo Says:

      I have updated the project. Now it supports Windows 7. I have also moved it to GitHub for better collaboration. There were a few issues in order to support Windows 7:

      • The Windows 7 kernel uses ASLR, which means that the location of drivers is randomized, so it is not straightforward to ask the PDB for a symbol’s location. You have to use the EnumDeviceDrivers function in order to get the base address of the driver and feed that address into SymLoadModuleEx.
      • PROCESSINFO, DESKTOPINFO, THREADINFO are internal and undocumented structures of the Windows kernel and they have changed from Windows XP to Windows 7, so I had to reverse engineer the new offsets for the needed fields.
      • The last thing is that on Windows 7 you cannot access the THREADINFO of a thread that runs in a different session from the session of the current thread. This is a form of isolation for increased security. It seems that services run in session 0, while user applications run in session 1. So you have to do a check by using the undocumented functions PsGetThreadSessionId and PsGetCurrentProcessSessionId. This is the technique that the Windows kernel uses in the non-exported function PtiFromThreadId of the win32k.sys driver, in order to verify the access of a thread to the THREADINFO of another thread.
    • prekageo Says:

      You may want also to check this link: Hookers – Underneath the Sheets. It’s from another blog and it seems that it follows the same pattern for listing the hooks. It looks like great work.

  2. XVII Says:

    Can you say anything about the order of the hooks? Is a new hook appended or prepended to the existing number of hooks? I’m concerned about a certain hook not calling CallNextHookEx function, which according to msdn is allowed even though “it is highly recommended”.

    • prekageo Says:

      In the source code mentioned in the article, there is a comment which reads “Link this hook into the front of the hook-list.”. So, I suppose that a new hook is prepended to list of hooks, although I have not verified this behavior.

  3. vijaysaraff Says:

    I get the following error when attempting to run the exe on win7 x64 sp1

    HookDetect – Windows hook viewer
    George Prekas

    StartService failed with error 1275: This driver has been blocked from loading

  4. vijaysaraff Says:

    Thanks for the reply! I did try the DSEO tool http://www.ngohq.com/?page=dseo but after several restarts it still wasn’t working (didn’t show testing mode watermark, so I’m not sure if it enabled it correctly).
    But after disabling UAC, and using the DSEO tool, I was able to run this application http://blog.airesoft.co.uk/2011/07/hookers-underneath-the-sheets that you provided a link to in the comments, and that worked in listing out all the hooks. So in the end this is quite a complex process to be able to do it successfully or easily.

  5. Xearinox Says:

    http://blog.airesoft.co.uk/2011/07/hookers-underneath-the-sheets not work in Windows XP, your soft is better.

  6. Alex Says:

    It does not work on x64 because your driver is 32-bit

  7. artur Says:

    What does mean
    SymFromName failed with error 126: The specified module could not be found.

  8. straightupandup7 Says:

    here’s another hook detector, https://github.com/jay/gethooks
    problem is it doesn’t work on x64, I’m trying but not getting very far, maybe you’d have better luck. Apparently the main problem is offsets, but might be other problems.

  9. thegodfather123 Says:

    Hey, I have win32k.sys version “6.1.7601.23407”. The hook detector isn’t working. Can you please help me, and update the version?

  10. straightupandup7 Says:

    @thegodfather123 have a look at https://github.com/tigros/HookTools since ur using win7, doesn’t work on win 8/10 tho.

  11. straightupandup7 Says:

    PS click “3 releases” to get the dll rather than compiling yourself, unless u wanna compile πŸ™‚

Leave a reply to straightupandup7 Cancel reply