Listing 2: debugser.cpp Interrupt-handling code
#include "DbgTrapProcs.h" #include "idt.h" typedef struct DBG_PRINT_PARAMS { DWORD s; char* pString; // string to DbgPrint DWORD u; PVOID caller; // Return address of who invoked DbgPrint() } DBG_PRINT_PARAMS, *PDBG_PRINT_PARAMS; DWORD debugServiceVector = 0x2D; // Interrupt vector used by NT BOOL bWeHooked = FALSE; // Are we hooked yet..with some soul NT_IDT oldIDTE; // Original IDT entry for debug service we replace PVOID oldISR; // Original ISR for Debugger service BOOL bLogUser = TRUE; BOOL bLogKernel = TRUE; PDBGTRAP_HEADER pHeader; // Header for giving info to the GUI PDBGTRAP_EVENT peb; // Our event buffer DWORD maxBufferSize = 256*1024;// Max Size of our event buffer (eb) DWORD bufferSize; // Actual # of bytes allocated for eb DWORD numEntries; // Max entries in the event buffer KSPIN_LOCK bl; // Buffer Lock PDWORD pindex; // Pointer to current index in buffer DWORD imgNameOfs; // Offset in KPEB for image name // NOTE: these must be used in the same scope #define LOCK_BUFFER KIRQL __oldirql__ = \ KeAcquireSpinLockRaiseToSynch( &bl ); #define UNLOCK_BUFFER KeReleaseSpinLock( & bl, __oldirql__ ); void ResetBuffer() { LOCK_BUFFER memset( peb, 0, bufferSize ); *pindex = 0; UNLOCK_BUFFER } NTSTATUS InitTrapper() { // Allocate a buffer that holds exactly N events, plus the header numEntries = (maxBufferSize-sizeof(DBGTRAP_HEADER)) /sizeof(DBGTRAP_EVENT); bufferSize = numEntries*sizeof(DBGTRAP_EVENT) +sizeof(DBGTRAP_HEADER); pHeader = (PDBGTRAP_HEADER) ExAllocatePool(NonPagedPool, bufferSize); if ( pHeader == FALSE) return STATUS_INSUFFICIENT_RESOURCES; // Initialize the header pHeader->size = sizeof(DBGTRAP_HEADER); pHeader->version = DT_VERSION; pHeader->sig = 'JOSE'; // Shameless, I know... pHeader->numEntries = numEntries; pHeader->index = 0; // Initialize the buffer & counter peb = &pHeader->eb[0]; pindex = &pHeader->index; // Initialize the buffer lock KeInitializeSpinLock( &bl ); // Set vars correctly DWORD mv; PsGetVersion( &mv, NULL, NULL, NULL ); if ( mv == 5) imgNameOfs = 0x1fc; else imgNameOfs = 0x1DC; ResetBuffer(); return STATUS_SUCCESS; } // Free up our trapper resources void BringDownTrapper() { // Since there is no safe way to syncronize freeing the buffer // with buffer logging, or debug service interrupt hook *must* // have been removed prior to calling here. ExFreePool( pHeader ); } // Logs parameters specific to a DbgPrint() call void LogDbgPrint(PDBGTRAP_EVENT pEvent, PDBG_PRINT_PARAMS param) { pEvent->callingAddr = param->caller; DWORD l = strlen( param->pString ); l = (l>MAX_STRING_LENGTH) ? MAX_STRING_LENGTH-1 : l; memcpy( pEvent->string, param->pString, l ); pEvent->string[ MAX_STRING_LENGTH-1 ] = 0; } // Logs parameters specific to Dbg(Un)LoadImageSymbols() void LogLoadImageSymbols(PDBGTRAP_EVENT pEvent, PANSI_STRING name, PVOID* baseAddr ) { char n[256]; char buff[sizeof(n)+10]; pEvent->callingAddr = baseAddr[5]; memcpy( n, name->Buffer, name->Length ); n[name->Length] = '\0'; DWORD l = sprintf( buff, "Driver %s @ 0x%x", n, *baseAddr ); l = (l>MAX_STRING_LENGTH) ? MAX_STRING_LENGTH-1 : l; memcpy( pEvent->string, buff, l ); pEvent->string[ MAX_STRING_LENGTH-1 ] = 0; } // Logs general parameters common to all debug services void LogEvent(PDBGTRAP_EVENT pEvent, NtDebuggerService service) { PEPROCESS pep = 0; pEvent->count = *pindex; KeQuerySystemTime( &pEvent->time ); pEvent->pid = PsGetCurrentProcessId(); pep = PsGetCurrentProcess(); strcpy( pEvent->procName, &((CHAR*)pep)[imgNameOfs] ); pEvent->action = service; } void LoggerDispatch(NtDebuggerService service, PVOID paramsCX, PVOID paramsDX) { LOCK_BUFFER // see if we're going to overrun the buffer if ( *pindex >= numEntries ) *pindex = 0; PDBGTRAP_EVENT pCurrentEvent = &peb[*pindex]; switch (service) { case DS_PRINT: LogDbgPrint( pCurrentEvent, (PDBG_PRINT_PARAMS)paramsCX ); break; case DS_PROMPT: break; case DS_LOADSYMBOLS: case DS_UNLOADSYMBOLS: LogLoadImageSymbols( pCurrentEvent, (PANSI_STRING)paramsCX, (PVOID*)paramsDX); break; } LogEvent( pCurrentEvent, service ); ++*pindex; UNLOCK_BUFFER } void _declspec(naked) DTDebuggerTrap() { static NtDebuggerService service; // Which debugger service // is requested static DWORD paramsCX; // Parameters passed to // the service static DWORD paramsDX; _asm { // Check & see if there's anything to do test [esp+8], X86_VM // VDM doesn't have Dbg services jnz DT_CHAIN test [esp+4], X86_USER jz DT_CAMEFROMKERNEL cmp [esp+4], NT_UCS // Don't log if user selector != 1b jnz DT_CHAIN cmp BYTE PTR ss:[bLogUser], 0 // Don't log of GUI says not to jz DT_CHAIN jmp DT_LOG // Passed all tests, log user event DT_CAMEFROMKERNEL: cmp ss:[bLogKernel], 0 jz DT_CHAIN // First rule of tinkering: // "Save all the pieces" // So let's Set up a semi-standard environment DT_LOG: pushad push ds push es push gs push fs mov bx, NT_DS mov ds, bx mov es, bx mov gs, bx mov bx, NT_FS mov fs, bx push edx push ecx push eax call LoggerDispatch // Restore everything pop fs pop gs pop es pop ds popad // Chain to original ISR DT_CHAIN: jmp cs:[oldISR] } } // This guy actually installs the hook on the debug service vector. // Returns TRUE if we actually did anything, FALSE if not. // NOTE: This routine (like others in this driver) is NOT thread // safe. Our whole subsystem currently expects a single // controler-single reader (but can have mutliple writers(cpus)). // This routine should therefore only be called inside a dispatch // routine from the GUI, or in the context of the system's worker // thred via DriverEntry, or Unload. BOOL InstallDebugServiceHook(BOOL bHook) { PNT_IDT pidtBase = NULL; // Anything to do? if ( bWeHooked == bHook ) return FALSE; // Loop over all processors & hook the debug service vector for ( char cpu=0; cpu<*KeNumberProcessors; ++cpu ) { PKTHREAD pThread = KeGetCurrentThread(); KeSetAffinityThread( pThread, 1<<cpu ); // Get this processor's IDT base address pidtBase = GetIDTBase(); if ( bHook ) pidtBase[ debugServiceVector ].Hook( DTDebuggerTrap, &oldISR ); else pidtBase[ debugServiceVector ].Hook( oldISR ); } // Set the thread's affinity back to run on all processors PKTHREAD pThread = KeGetCurrentThread(); KeSetAffinityThread( pThread, (1<<*KeNumberProcessors)-1 ); // No turning back now...all or nothing bWeHooked = !bWeHooked; return TRUE; } //End of File