Last time around, we’ve seen how to do something slightly useful in our driver. This time, we’ll simulate a technique used over ten years ago by Windows kernel rootkits to hide a process from tools such as Task Manager.
First, some background: the Windows scheduler doesn’t need process information to run code. The scheduler needs access only to threads—threads ready for execution are stored in a set of ready queues. When a thread enters a wait state, the system tracks its information using _KWAIT_BLOCK structures, which again don’t require access to processes.
Still, the system keeps track of the list of running processes, not the least for tools like Task Manager to display information on what’s going on in the system. Malicious software has been fond of subverting the information presented to Task Manager in order to hide processes; similar techniques exist for hiding files, registry entries, network connections, and other traces of malicious activity. (Needless to say, security software is detecting and preventing tricks of this sort as they emerge, and the arms race goes on.)
One way of hiding processes is called Direct Kernel Object Manipulation [pdf], which involves modifying the internal data structures the system uses to keep track of processes. Namely, there is a linked list of _EPROCESS structures which represent running processes. Unlinking an _EPROCESS structure from this list will cause the process to become invisible to tools like Task Manager.
Because the _EPROCESS structure is undocumented, and isn’t part of the WDK headers, you will need to find the embedded _LIST_ENTRY offset manually for each OS version you intend to support. On Windows XP 32-bit, this offset is 0x88. A _LIST_ENTRY is an entry in a doubly-linked list, with backward and forward pointers. This means that given one _EPROCESS you can find the rest by traversing these pointers—and you can find the first _EPROCESS by using PsGetCurrentProcess(), which is a documented WDK API.
The following is a routine that hides the calling process by unlinking it from the aforementioned list:
eProcess = (ULONG)PsGetCurrentProcess();
plist = (PLIST_ENTRY)(eProcess+FLINKOFFSET);
*((ULONG*)plist->Blink) = (ULONG) plist->Flink;
*((ULONG*)plist->Flink+1) = (ULONG) plist->Blink;
plist->Flink = (PLIST_ENTRY) &(plist->Flink);
plist->Blink = (PLIST_ENTRY) &(plist->Flink);
The last two lines ensure that the forward and backward links on the hidden _EPROCESS remain valid (pointing to itself), in case some API will decide to use them. (This might happen, for instance, when the process exits and the Process Manager removes it from the list of processes.)
Next time: This post already wanders into grounds in which I’m not sure I’m willing to keep treading. We’ll see about next time.