The current article is devoted to an easy approach for setting up global API hooks on a system-wide scale. For DLL injection, we will utilize a registry key called AppInit_DLLs, and to perform API hooking in Windows, we will utilize the Mhook library. This article will also provide you a DLL injection example: we will demonstrate how you can easily make the calc.exe process invisible in the running process list.
Leader of Driver Team
Windows API hooking is a process allowing to intercept API function calls. This gives you the control over the way operating system or a piece of software behaves. Some of the software solutions that utilize hooks include: antimalware software, application security solutions, security monitoring tools, system utilities, tools for programming, and many others.
API hooks can be divided into the following types:
- Local hooks: These influence only specific applications.
- Global hooks: These affect all system processes.
The type of hook technique for Windows that we cover here belongs to the global type. It affects all processes across all sessions (as opposed to the SetWindowsHooks method, which is limited only to a selected desktop).
AppInit_DLLs infrastructure loads a predefined set of DLLs to all user-mode processes connected with the User32.dll library (in fact, there are almost no executables, which wouldn’t be connected with it). When User32.dll is initialized, it loads the corresponding DLLs, thus performing the DLL injection into processes.
To change the way the
AppInit_DLLs infrastructure behaves, you need to configure the
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows registry key values. The following values are available:
|Allows you to switch AppInit_DLLs on or off on a global scale.||0x0 disables AppInit_DLLs |
0x1 enables AppInit_DLLs
|Allows you to specify the list of DLLs for loading. The items must be separated either by commas or spaces. To specify the full path to a DLL, use short file names.||C:\PROGRA~1\Test\Sample.dll|
|Allows you to limit the range of DLLs only to code-signed ones.||0x0 allows loading of any DLLs |
0x1 allows loading of only code-signed DLLs.
Several API hooking libraries exist. Typically, they do the following:
- Replace the initial part of a defined function code with our own code (also known as trampoline). Upon execution, the function jumps to a hook handler.
- Store the original version of the replaced code of the defined function. This is required for the defined function to operate properly.
- Restore the replaced part of the defined function.
As I mentioned before, when building our global hooks, we will use Mhook library. It is a free and easy-to-use open-source library for Windows API hooking supporting x32 and x64 system architectures. Its interface isn’t complicated and is self-explanatory:
More details on how to use the library is available further in the article and on the Mhook home page.
For this example, we will use C++ to write a user-mode DLL to illustrate DLL injection techniques. To do this, the latest version of the Mhook sources is required, which will be added to your project. Please note, any precompiled headers must be disabled for Mhook files.
As we have already said, to provide an API hooking example, we will make the calc.exe process invisible in the list of processes – for any Windows tool representing such list. This example will demonstrate how to create and inject DLL into a process, thus setting up a global API hook - and creating a kind of appinit_dlls rootkit .
To list running processes, you need to call the
NtQuerySystemInformationNTAPI function. This means that our project requires some NTAPI stuff. As we cannot find the full information in the winternl.h header, the data types must be defined manually:
The creation and initialization of a global variable allows us to store the address of an original function:
After a function has been hooked, first it calls the original function. Then we examine
SystemInformationClass. In case it reveals to be SystemProcessInformation, in the list of running processes, we need to find and remove all records related to calc.exe. And that’s it!
Please note that the original and hooked functions must have identical signatures.
It does not require much effort to set up a hook: you simply need to call
DllMain after loading a DLL to a new process:
To reverse hooking, you need to call Mhook_Unhook from DllMain after unloading the DLL from the process:
Now we will demonstrate how our DLL hook works. Follow these steps:
- Build the project and place the AppInitHook.dll, which you will have in the result, to the disk C root.
- In the Windows Registry Editor, locate the
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windowskey and select the
- Edit the value and enter the path to the DLL hook (in our example, it is C:\AppInitHook.dll).
- After you finish editing the registry, the hooks starts functioning.
Now let’s launch several instances of the process that has been hidden. After that, examine the processes in the Windows Task Manager: the calc.exe is absent from the list.
As the provided API hook is global, we can see that the same result is displayed by other programs with functionality similar to Windows Task Manger. For example, Process Explorer from Mark Russinovich.
The final check: open the command line and run tasklist.exe.
The process of standard Windows calculator and all its instances have been successfully hidden. The API hook works as expected.
Now we need to say a few words about the limitations to this method:
- Connection to User32.dll: As we already said in the beginning of the article, only the processes that are connected to User32.dll can be affected.
- Only functions from Ntdll.dll and Kernel32.dll can be called: The reason for this is that DLL hooking takes place in DllMain of User32.dll and no other library is initialized at that moment.
- Windows 7 and Windows 2008 R2 security features: These features require AppInit DLLs with digital signatures. This isn’t really a big issue as the features can be disabled via Windows Registry Editor.
- No spaces in the full path to an AppInit DLL are allowed.