Contents
1. Introduction
1.1. What is API hooking?
1.2. Local and global hooks
2. AppInit_DLLs infrastructure
3. Mhook library
4. Writing the code
4.1. Original function
4.2. Hooked function
4.3. Setting the hook
4.4. Unhooking
5. Running a sample
6. Limitations
7. Useful references
1. Introduction
This article describes an easy way to set up system-wide global API hooks. It uses AppInit_DLLs registry key for DLL injection and Mhook library for API hooking. To illustrate this technique we will show how to easily hide calc.exe from the list of running processes.
1.1 What is API hooking?
API hooking means intercepting some API function calls. By means of it you can alter the behavior of any software. Hooks are widely used by antiviruses, security applications, system utilities, programming tools etc.
1.2 Local and global hooks
There are two types of hooks: local and global ones. Local hooks are applied only to the specific application. Global hooks are applied to all processes in the system. The hook technique, which is shown in this article, is global and impacts on all processes in all sessions (in contrast to the SetWindowsHooks
way that is bounded to the specific desktop).
2. AppInit_DLLs infrastructure
AppInit_DLLs
infrastructure is a mechanism for loading an arbitrary list of DLLs in all user-mode processes which are linked with User32.dll (Actually, there are very few executables that are not linked with it). The DLLs are loaded by User32.dll on its initialization.
The behavior of the AppInit_DLLs
infrastructure is configured by a set of values that are stored under the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows key in the registry. These registry values are described in the table:
Value | Description | Sample values |
LoadAppInit_DLLs
(REG_DWORD) | Value that globally enables or disables AppInit_DLLs. | 0x0 – AppInit_DLLs are disabled.
0x1 – AppInit_DLLs are enabled. |
AppInit_DLLs
(REG_SZ)
| Space - or comma -separated list of DLLs to load. The complete path to the DLL should be specified using short file names. | C:\PROGRA~1\Test\Test.dll |
RequireSignedAppInit_DLLs
(REG_DWORD) | Require code-signed DLLs. | 0x0 – Load any DLLs.
0x1 – Load only code-signed DLLs. |
Table 1 - AppInit_DLLs Infrastructure registry values.
3. Mhook library
There are several libraries for api hooking. The typical things that they do are:
- Overwriting the beginning of the target function with custom code (so-called trampoline). When the function executes it will jump to the hook handler.
- Storing overwritten original code of the target function somewhere. It is needed for the correct target function functioning.
- Restoring overwritten portion of the target function.
Mhook is a free open source library for api hooking. It supports both x86 and x64 platforms and it is very easy in use. Mhook interface is simple and quite self describing:
BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction);
BOOL Mhook_Unhook(PVOID *ppHookedFunction);
For more info on library usage see the code sample shown in the next paragraph or visit Mhook home page.
4. Writing the code
We aregoing to write a user-mode DLL. First you should download the latest Mhook sources and add it to the project. If you are using precompiled headers turn it off for Mhook files.
As I’ve mentioned above our example will hide the calc.exe from the list of running processes.
4.1 Original function
The list of running processes is queried by calling NTAPI function NtQuerySystemInformation
. So, we need to add some NTAPI stuff to our project. Unfortunately winternl.h header doesn’t contain full information and we have to define required data types ourselves:
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
typedef struct _MY_SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
} MY_SYSTEM_PROCESS_INFORMATION, *PMY_SYSTEM_PROCESS_INFORMATION;
typedef NTSTATUS (WINAPI *PNT_QUERY_SYSTEM_INFORMATION)(
__in SYSTEM_INFORMATION_CLASS SystemInformationClass,
__inout PVOID SystemInformation,
__in ULONG SystemInformationLength,
__out_opt PULONG ReturnLength
);
To store original function address create a global variable and initialize it:
PNT_QUERY_SYSTEM_INFORMATION OriginalNtQuerySystemInformation =
(PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress(::GetModuleHandle(L"ntdll"), "NtQuerySystemInformation");
Hooked function
In the hooked function we call the original function first. Then check SystemInformationClass
. If it is SystemProcessInformation
we loop through the list of the running processes and find all entries for calc.exe to cut them out from the list. That’s all!
Note: This function must have the same signature as the original one.
NTSTATUS WINAPI HookedNtQuerySystemInformation(
__in SYSTEM_INFORMATION_CLASS SystemInformationClass,
__inout PVOID SystemInformation,
__in ULONG SystemInformationLength,
__out_opt PULONG ReturnLength
)
{
NTSTATUS status = OriginalNtQuerySystemInformation(SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength);
if (SystemProcessInformation == SystemInformationClass && STATUS_SUCCESS == status)
{
PMY_SYSTEM_PROCESS_INFORMATION pCurrent = NULL;
PMY_SYSTEM_PROCESS_INFORMATION pNext = (PMY_SYSTEM_PROCESS_INFORMATION)SystemInformation;
do
{
pCurrent = pNext;
pNext = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pCurrent + pCurrent->NextEntryOffset);
if (!wcsncmp(pNext->ImageName.Buffer, L"calc.exe", pNext->ImageName.Length))
{
if (0 == pNext->NextEntryOffset)
{
pCurrent->NextEntryOffset = 0;
}
else
{
pCurrent->NextEntryOffset += pNext->NextEntryOffset;
}
pNext = pCurrent;
}
}
while(pCurrent->NextEntryOffset != 0);
}
return status;
}
4.3 Setting the hook
Setting the hook is pretty easy: call Mhook_SetHook
from DllMain
when the DLL is loaded to a new process:
BOOL WINAPI DllMain(
__in HINSTANCE hInstance,
__in DWORD Reason,
__in LPVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
Mhook_SetHook((PVOID*)&OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation);
break;
4.4 Unhooking
Unhooking is performed by calling Mhook_Unhook
from DllMain
when the DLL is unloaded from the process:
BOOL WINAPI DllMain(
__in HINSTANCE hInstance,
__in DWORD Reason,
__in LPVOID Reserved
)
{
switch (Reason)
{
...
case DLL_PROCESS_DETACH:
Mhook_Unhook((PVOID*)&OriginalNtQuerySystemInformation);
break;
}
5. Running a sample
Now it’s time to show the described hook in action. Build the project and put the resulting AppInitHook.dll to the root of the disk C.
Figure 1 - The hook DLL is put to the root of the disk C.
Open the registry editor and locate AppInit_DLLs
registry key (The key is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows). Then specify the path to the hook DLL (C:\AppInitHook.dll in our case).
Figure 2 – Modifying the registry.
After the registry has been modified the hook starts working. Let’s run a few instances of calc.exe. Then open Windows Task Manager and look at the processes tab. There is no calc.exe at all!
Figure 3 - Windows Task Manager processes tab.
Let’s see what shows another popular tool written by Mark Russinovich - Process Explorer.
Figure 4 - Process Explorer shows no calc.exe.
All calc.exe instances are hidden successfully. And finally run command line tool tasklist.exe:
Figure 5 - Tasklist.exe listing of the running processes.
The hook is working!
6. Limitations
There are a few limitations of this hook technique you should know about:
- As it was mentioned before this hook is applied only to those processes that are linked to User32.dll.
- As hooking is performed in
DllMain
of User32.dll you can call functions only from Kernel32.dll and Ntdll.dll (other libraries are not initialized yet). - Windows7/Windows 2008 R2 introduces the new security feature – AppInit DLLs have to be digitally signed (however there is a registry key that can turn this feature off).
- The file path to AppInit DLL must not contain spaces.
Useful references
- This article on the Apriorit website
- Working with the AppInit_DLLs registry value
- AppInit DLLs in Windows 7 and Windows Server 2008 R2
- API hooking revealed
- Mhook, an API hooking library, v2.2
- Microsoft Research's Detours
- DllMain Callback Function