A hello and some of my research :)

Huge area to cover, we have assembled and written tutorials that have proven helpful over time.
Post Reply
Posts: 2
Joined: Sun Jan 16, 2005 5:30 am

A hello and some of my research :)

Post by Subsky » Sun Jan 16, 2005 7:47 am

Heylo peoples! I'm subsky- and some of you may know me as Metro Mystery from http://www.rootkit.com. I thought that after a while of inactivity over there- I'd find another community whereby I can contribute some of the security related issues I have addressed. These seems like a pretty good place to start.

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


      IN ULONG ARG_1,
      IN ULONG hParentWnd,
      IN ULONG HwndListType,
      IN ULONG ThreadId,
      OUT ULONG ARG_5,
      OUT ULONG* pHandleList,
      OUT ULONG* HandlesInBufferCount

[in] Always seems to be 0x00000000.

[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

[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].

[in] If set; will return a window list containing all windows created by or belonging to the ThreadId.

[out] Not fully understood; sometimes contains the same value returned in HandlesInBufferCount, but not always.

[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.

[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

// structure of a Service Descriptor Table. (KeServiceDescriptorTable
// 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;

The address in the source code was just hard-coded at *0x80544BC0 however, you can get it dynamically using the _KTHREAD->ServiceTable value.

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


[in] The unique window handle ID of the window who’s process is being queried.

[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]

User avatar
Owns you
Posts: 1618
Joined: Thu May 06, 2004 9:18 am

Post by Prism » Sun Jan 16, 2005 9:19 am


Welcome to the forum.

User avatar
The Evil Clown
Posts: 1714
Joined: Fri Mar 26, 2004 8:47 pm

Post by Ramius » Sun Jan 16, 2005 10:56 am

Based on the content of the post, I moved the threaad to the tutorial section.

Thanks for a contributory post, especially as your first.

RIF - Reading is Fundamental
Hacking is a process, not a product

User avatar
Corporate Drunkard
Posts: 1911
Joined: Tue Jul 29, 2003 11:47 pm
Location: Guam

Post by Life » Sun Jan 16, 2005 11:51 am

Nice post. Welcome to the forums Subsky.
It was once suggested that a million monkeys working at a million typewriters would produce the works of Shakespeare. However, a million monkeys working at a million keyboards has only produced 2girls1cup, goatse and MySpace.

Posts: 1
Joined: Sun Jan 16, 2005 2:41 pm

Post by DaveyBoy » Sun Jan 16, 2005 2:49 pm

I am submitting a tuorial soon and I would like to say how well written this one is! Well done!

Im new too!

User avatar
Net Battle Bot
Owns you
Posts: 1816
Joined: Fri Jun 04, 2004 6:44 am
Location: Groom Lake

Post by Net Battle Bot » Sun Jan 16, 2005 8:40 pm

DaveyBoy wrote:P.S
Im new too!
No shit? :wink:

Nice to see you here subsky.
Without practice one cannot prove; without proof one cannot be trusted; without trust one cannot be respected.

Post Reply