Listing 1: entrypoi.cpp Driver entry points and buffer management
#pragma warning( disable : 4201 4514 4060) #include "DbgTrapProcs.h" #define NT_DEVICE_NAME L"\\Device\\DbgTrap" #define DOS_DEVICE_NAME L"\\DosDevices\\DbgTrap" // DRIVER_STATE is used by CleanUp function to determine work // to be done. As the driver completes it's initialization we // increment it's state. If an error occurs during at any time // Cleanup does all work undoing what we've done up to that point. typedef enum DRIVER_STATE { STATE_INITIAL, // We own no resources STATE_HASBUFFER, // We've allocated our event buffer STATE_HASDEVICES, // We've created a device object STATE_HASLINK, // We've created a symbolic link STATE_MONITORING // We're hooked into the debugger service }; DRIVER_STATE state; // See DRIVER_STATE discussion above MDL mdl; // MDL describing the GUI's view of eb extern "C" NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); // Moves our cheezy state machine into the next state inline DRIVER_STATE NextState() { //_asm lock inc state InterlockedIncrement( (PLONG)&state ); return state; } // Moves our cheezy state machine into the previous state inline DRIVER_STATE PreviousState() { //_asm lock inc state InterlockedDecrement( (PLONG)&state ); return state; } // Prepares our driver to be unloaded. Can be called from anywhere // not just unload routine. Any fatal errors will call this before // spiriling downward to doom. void Cleanup(PDRIVER_OBJECT DriverObject) { UNICODE_STRING uniWin32NameString; DTPrint( ("Cleanup called!!" ) ); switch ( state ) { case STATE_MONITORING: InstallDebugServiceHook( FALSE ); case STATE_HASLINK: // Delete the link from our device name to a // name in the Win32 namespace. RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); IoDeleteSymbolicLink( &uniWin32NameString ); case STATE_HASDEVICES: // Finally delete our device object IoDeleteDevice( DriverObject->DeviceObject ); case STATE_HASBUFFER: // Free up our trapper resources BringDownTrapper(); case STATE_INITIAL: state = STATE_INITIAL; break; } } // generically checks basic things about potential buffer NTSTATUS ValidateBuffer(PVOID pBuffer, // potential buffer DWORD length, // length this buffer claims to be DWORD requiredLength // length this buffer *must* be ) { if ( pBuffer == NULL ) return STATUS_INVALID_PARAMETER; if ( length < requiredLength ) return STATUS_INFO_LENGTH_MISMATCH; return STATUS_SUCCESS; } // We keep a pointer to the ring 3 buffer for each open file handle // to our device that has been given access to our event buffer. // This function sets that buffer value into the file object that // originated the current IRP void SetGuiView(IN PIRP pIrp, IN PVOID value) { PFILE_OBJECT pFile = pIrp->Tail.Overlay.OriginalFileObject; pFile->FsContext = (PVOID)value; } // We keep a pointer to the ring 3 buffer for each open file handle // to our device that has been given access to our event buffer. // This function plucks that buffer value out of the file object // indirectly, via the current IRP. PVOID GetGuiView(IN PIRP pIrp) { PFILE_OBJECT pFile = pIrp->Tail.Overlay.OriginalFileObject; return pFile->FsContext; } PMDL pMdl; // We graciously share our event buffer with the GUI. It's up to // the GUI to behave properly with the event buffer. Since our // device is exclusive & I am writing both the GUI & the driver, // there is no reason to think I'd write the driver any better // than I'd write the GUI...so there's no problem ;-) NTSTATUS MapGuiView(IN OUT PVOID outputBuffer, IN DWORD outputBufferLength, IN PIRP pIrp) { NTSTATUS status = STATUS_UNSUCCESSFUL; // Anything to do? if ( GetGuiView(pIrp) != NULL) return STATUS_SUCCESS; // Do we have good parameters? status = ValidateBuffer( outputBuffer, outputBufferLength, sizeof(DWORD) ); if ( status != STATUS_SUCCESS ) return status; _try { DWORD size = MmSizeOfMdl(pHeader,bufferSize); pMdl = (PMDL)ExAllocatePool( NonPagedPool, size ); pMdl = MmCreateMdl( pMdl, pHeader, bufferSize); if ( (pMdl->MdlFlags & (MDL_PAGES_LOCKED | MDL_SOURCE_IS_NONPAGED_POOL | MDL_MAPPED_TO_SYSTEM_VA | MDL_PARTIAL) ) == 0) MmBuildMdlForNonPagedPool(pMdl); PVOID ptr = MmMapLockedPages(pMdl, UserMode); if ( ptr == NULL ) *(PDBGTRAP_HEADER*)outputBuffer = NULL; else *(PDBGTRAP_HEADER*)outputBuffer = (PDBGTRAP_HEADER)(ULONG(ptr)|MmGetMdlByteOffset(pMdl)); } __except (EXCEPTION_EXECUTE_HANDLER) { return STATUS_ACCESS_VIOLATION; } return status; } // Does nothing if there's no buffer view associated with the current // file object. Otherwise, we get rid of the user mode view of our // event buffer. void UnmapGuiView(PIRP pIrp) { // Pull the pointer out from the FileObject PVOID pGuiView = GetGuiView( pIrp ); if ( pGuiView == NULL ) return; // Now unmap em MmUnmapLockedPages((PVOID)((ULONG)pGuiView&~(PAGE_SIZE-1)),pMdl); // do some stuff..mmunmaplockedpages() SetGuiView( pIrp, NULL ); } // Handler for all IRP_MJ_CLOSE NTSTATUS DTClose(IN PDEVICE_OBJECT pDO, IN PIRP pIrp) { // Make sure that we unmap the ring 3 view of our buffer if // necessary UnmapGuiView( pIrp ); // Just complete the IRP normally pIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(pIrp, IO_NO_INCREMENT ); return STATUS_SUCCESS; } // Handler for IRP_MJ_DEVICE_CONTROL & IRP_MJ_CREATE NTSTATUS DTDispatch(IN PDEVICE_OBJECT pDO,IN PIRP pIrp) { PVOID inputBuffer = NULL; PVOID outputBuffer = NULL; DWORD inputBufferLength = 0; DWORD outputBufferLength = 0; NTSTATUS status = STATUS_NOT_IMPLEMENTED; PIO_STACK_LOCATION pIrpStack=IoGetCurrentIrpStackLocation(pIrp); // Get the pointer to the input/output buffer and it's length inputBuffer = pIrpStack->Parameters.DeviceIoControl .Type3InputBuffer; outputBuffer = pIrp->UserBuffer; inputBufferLength = pIrpStack->Parameters.DeviceIoControl .InputBufferLength; outputBufferLength = pIrpStack->Parameters.DeviceIoControl .OutputBufferLength; pIrp->IoStatus.Information = 0; // Dispatch based on major fcn code. switch (pIrpStack->MajorFunction) { case IRP_MJ_CREATE: SetGuiView( pIrp, NULL ); status = STATUS_SUCCESS; break; case IRP_MJ_DEVICE_CONTROL: // Dispatch on IOCTL switch (pIrpStack->Parameters.DeviceIoControl .IoControlCode) { case IOCTL_HOOK: if ( InstallDebugServiceHook( TRUE ) ) { NextState(); status = MapGuiView( outputBuffer, outputBufferLength, pIrp); if ( status == STATUS_SUCCESS ) SetGuiView( pIrp, (PVOID)* (PDWORD)outputBuffer ); } break; case IOCTL_UNHOOK: UnmapGuiView( pIrp ); if ( InstallDebugServiceHook( FALSE ) ) PreviousState(); status = STATUS_SUCCESS; break; case IOCTL_RESET: ResetBuffer(); status = STATUS_SUCCESS; break; case IOCTL_LOGUSER: status = ValidateBuffer( inputBuffer, inputBufferLength, 1); if ( status == STATUS_SUCCESS ) bLogUser = *(PBOOL)inputBuffer; break; case IOCTL_LOGKERNEL: status = ValidateBuffer( inputBuffer, inputBufferLength, 1); if ( status == STATUS_SUCCESS ) bLogKernel = *(PBOOL)inputBuffer; break; default: // ?? Where did this come from ?? status = STATUS_NOT_IMPLEMENTED; break; } break; default: // ?? Where did this come from ?? status = STATUS_NOT_IMPLEMENTED; pIrp->IoStatus.Information = 0; break; } // We're done with I/O request. Record the status of the // I/O action. pIrp->IoStatus.Status = status; // Don't boost priority when returning since this took // little time. IoCompleteRequest(pIrp, IO_NO_INCREMENT ); return status; } VOID DTUnload(IN PDRIVER_OBJECT DriverObject) { DTPrint( ("Unloading!!" ) ); Cleanup( DriverObject ); } #pragma code_seg("INIT") NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { PDEVICE_OBJECT deviceObject = NULL; NTSTATUS status; UNICODE_STRING uniNtNameString; UNICODE_STRING uniWin32NameString; DTPrint( ("DriverEntry called - Debug Build!") ); // Initialize globals state = STATE_INITIAL; // Initialize the buffer status = InitTrapper(); if ( !NT_SUCCESS(status) ) { DTPrint( ("Couldn't initialize debug trap subsystem") ); Cleanup( DriverObject ); return status; } NextState(); // Create the device object RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME ); status = IoCreateDevice(DriverObject, 0, // We don't use a // device extension &uniNtNameString, FILE_DEVICE_UNKNOWN, 0, // No standard device // characteristics TRUE, // This IS an exclusive // device &deviceObject ); if ( !NT_SUCCESS(status) ) { DTPrint( ("Couldn't create the device") ); Cleanup( DriverObject ); return status; } NextState(); // Set up our dispatch fncs DriverObject->MajorFunction[IRP_MJ_CREATE] = DTDispatch; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DTClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DTDispatch; DriverObject->DriverUnload = DTUnload; // Create a symbolic link for the GUI RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME ); status = IoCreateSymbolicLink( &uniWin32NameString, & uniNtNameString ); if (!NT_SUCCESS(status)) { DTPrint( ("Couldn't create the symbolic link") ); Cleanup( DriverObject ); return status; } NextState(); return status; } #pragma code_seg() //End of File