Hiding Window Handles through Shadow Table Hooking on Windows XP
Endless tracing in Soft-ice, patience in IDA and one function- EnumChildWindows() lead to the discovery that kernel-mode window handle hiding is only another possibility in the forever expanding list of rootkit technology. Although it begs the question; why would you need to hide a window handle in the first place if the concept of a root kit is based on that of stealth and invisibility? Rootkits (especially those implemented in drivers) aren’t going to be popping up too many windows and dialogs. It’s more a case of invincibility rather than invisibility; if an application (such as taskmgr.exe) can’t find a handle to a particular window- it can’t close it. Although hooking ZwTerminateProcess() and returning STATUS_ACCESS_DENIED usually assures a process won’t be terminated; it won’t hide your window handles or prevent any application from sending WM_CLOSE messages to it’s message queue.
In IDA, a quick disassembly of EnumChildWindows(), one of ‘the’ user-mode functions for window enumerations (child and top level alike), showed that user32.dll loads *0x138 into edx register- the call number for the kernel mode function NtUserBuildHwndList() found in the system service table of win32k functions- right before the system switches to kernel-mode and executes it.
After some careful internal analysis with softice, it was apparent that NtUserBuildHwndList() constructs a list/array of certain window handles based on the values of variables passed in through the argument list. It then assigns to a passed in pointer (pHandleList) the address of the beginning of this list, and copies the number of handles the list contains into yet another parameter variable (* HandlesInBufferCount).
Code: Select all
NTSTATUS NTAPI
NtUserBuildHwndList(
IN ULONG ARG_1,
IN ULONG hParentWnd,
IN ULONG HwndListType,
IN ULONG ThreadId,
OUT ULONG ARG_5,
OUT ULONG* pHandleList,
OUT ULONG* HandlesInBufferCount
)
ARG_1
[in] Always seems to be 0x00000000.
hParentWnd
[in] The unique window handle ID of the parent window who’s children are being enumerated; this parameter is only used in the HwndListType parameter [below is set to 0x00000001
HwndListType
[in] 0x00000000 for a list of all the top level windows in the system or 0x00000001 for a list of all child windows belonging to a parent [specified in the hParentWnd parameter above].
ThreadId
[in] If set; will return a window list containing all windows created by or belonging to the ThreadId.
ARG_5
[out] Not fully understood; sometimes contains the same value returned in HandlesInBufferCount, but not always.
pHandleList
[out] pointer to the first window handle in the list/array – next entry is +4 bytes. The list is delimited with a value of 0x00000001 however, it is probably better to use the value returned in HandlesInBufferCount [below] to find the number of items and the end of the list.
HandlesInBufferCount
[out] The number of window handles in the list
Return Value
The return value is one of the NT STATUS codes.
Hooking functions such as NtUserBuildHwndList() in the KeServiceDescriptorTableShadow is a little more complicated than intercepting those regular system services in the exported KeServiceDescriptorTable. Briefly, an easier way to do this involves writing a simple user-mode process that creates a symbolic link to the driver in a thread that has previously called IsGuiThread(TRUE). This same thread then calls DeviceIoControl() on the driver and passes down a control message like IO_HOOK_SYSTEM_SERVICES.
Code: Select all
typedef struct tag_SYSTEM_SERVICE_TABLE {
PULONG ServiceTable; // array of entry points
PULONG CounterTable; // array of usage counters
ULONG ServiceLimit; // number of table entries
PCHAR ArgumentTable; // array of argument counts
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE, **PPSYSTEM_SERVICE_TABLE;
// and the non-exported KeServiceDescriptorTableShadow).
Code: Select all
typedef struct tag_SERVICE_DESCRIPTOR_TABLE {
SYSTEM_SERVICE_TABLE ntoskrnl; // main native API table
SYSTEM_SERVICE_TABLE win32k; // win subsyste
SYSTEM_SERVICE_TABLE sst3;
SYSTEM_SERVICE_TABLE sst4;
} SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE, **PPSERVICE_DESCRIPTOR_TABLE;
Once successfully hooked, inside our function we must:
1) Call the original NtUserBuildHwndList() to obtain the real window list.
2) Determine whether the preceding call was a STATUS_SUCCESS and if so, dynamically modify this list of window handle entries returned by (1) and update any changes made in HandlesInBufferCount.
3) In our hooked function return STATUS_SUCCESS- in effect passing to whatever user-mode application is requesting it a modified list containing all windows but those belonging to a specific process.
Step 2 is a rather complicated- since we’re in kernel-mode, we don’t have functions like GetWindowThreadProcessId() to match a window handle ID to a process. Using IRPs, we could pass this information to user-mode, ask it to check the value and report back to the driver; but this is rather extensive and messy.
Instead, tracing through GetWindowThreadProcessId() showed that if a process is requesting information on a window handle not belonging to one of its own threads; it relies on the system service, NtUserQueryWindow() *(call number 0x1E3) to get the process/thread ID.
Code: Select all
ULONG NtUserQueryWindow(
IN ULONG WindowHandle,
IN ULONG TypeInformation
)
WindowHandle
[in] The unique window handle ID of the window who’s process is being queried.
TypeInformation
[in] 0x00000000 for ProcessID, 0x00000001 for ThreadID
Return Value
The return value is the Process ID or Thread ID owning the WindowHandle.
The source can be found in my (metro_mystery's) vault on rootkit.com here
That’s it- you’re windows are hidden ;)
*[This was tested on Windows XP with no service pack installed (build number 2600) and includes some hard-coded offsets and non-exported win32k function call numbers. With little modification, this code can be adopted to run on other versions of Windows]