diff options
Diffstat (limited to 'private/sdktools/instaler/debug.c')
-rw-r--r-- | private/sdktools/instaler/debug.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/private/sdktools/instaler/debug.c b/private/sdktools/instaler/debug.c new file mode 100644 index 000000000..c20b9eed4 --- /dev/null +++ b/private/sdktools/instaler/debug.c @@ -0,0 +1,481 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + debug.c + +Abstract: + + Main loop for INSTALER program + +Author: + + Steve Wood (stevewo) 09-Aug-1994 + +Revision History: + +--*/ + +#include "instaler.h" + +DWORD +DebugEventHandler( + LPDEBUG_EVENT DebugEvent + ); + +VOID +InstallBreakpointsForDLL( + PPROCESS_INFO Process, + LPVOID BaseOfDll + ); + +VOID +RemoveBreakpointsForDLL( + PPROCESS_INFO Process, + LPVOID BaseOfDll + ); + +char *DebugEventNames[] = { + "Unknown debug event", + "EXCEPTION_DEBUG_EVENT", + "CREATE_THREAD_DEBUG_EVENT", + "CREATE_PROCESS_DEBUG_EVENT", + "EXIT_THREAD_DEBUG_EVENT", + "EXIT_PROCESS_DEBUG_EVENT", + "LOAD_DLL_DEBUG_EVENT", + "UNLOAD_DLL_DEBUG_EVENT", + "OUTPUT_DEBUG_STRING_EVENT", + "RIP_EVENT", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + "Unknown debug event", + NULL +}; + + +VOID +DebugEventLoop( VOID ) +{ + DEBUG_EVENT DebugEvent; + DWORD ContinueStatus; + DWORD OldPriority; + + // + // We want to process debug events quickly + // + + OldPriority = GetPriorityClass( GetCurrentProcess() ); + SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ); + + do { + if (!WaitForDebugEvent( &DebugEvent, INFINITE )) { + DeclareError( INSTALER_WAITDEBUGEVENT_FAILED, GetLastError() ); + ExitProcess( 1 ); + } + + if (DebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { + if (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT && + DebugEvent.u.Exception.ExceptionRecord.ExceptionCode != STATUS_SINGLE_STEP + ) { + DbgEvent( DBGEVENT, ( "Debug exception event - Code: %x Address: %x Info: [%u] %x %x %x %x\n", + DebugEvent.u.Exception.ExceptionRecord.ExceptionCode, + DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress, + DebugEvent.u.Exception.ExceptionRecord.NumberParameters, + DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 0 ], + DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 1 ], + DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 2 ], + DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 3 ] + ) + ); + } + } + else { + DbgEvent( DBGEVENT, ( "Debug %s (%x) event\n", + DebugEventNames[ DebugEvent.dwDebugEventCode ], + DebugEvent.dwDebugEventCode + ) + ); + } + ContinueStatus = DebugEventHandler( &DebugEvent ); + if (!ContinueDebugEvent( DebugEvent.dwProcessId, + DebugEvent.dwThreadId, + ContinueStatus + ) + ) { + DeclareError( INSTALER_CONTDEBUGEVENT_FAILED, GetLastError() ); + ExitProcess( 1 ); + } + } + while (!IsListEmpty( &ProcessListHead )); + + + // + // Drop back to old priority to interact with user. + // + + SetPriorityClass( GetCurrentProcess(), OldPriority ); +} + + +DWORD +DebugEventHandler( + LPDEBUG_EVENT DebugEvent + ) +{ + DWORD ContinueStatus; + PPROCESS_INFO Process; + PTHREAD_INFO Thread; + PBREAKPOINT_INFO Breakpoint; + + ContinueStatus = (DWORD)DBG_CONTINUE; + if (FindProcessAndThreadForEvent( DebugEvent, &Process, &Thread )) { + switch (DebugEvent->dwDebugEventCode) { + case CREATE_PROCESS_DEBUG_EVENT: + // + // Create process event includes first thread of process + // as well. Remember process and thread in our process tree + // + + if (AddProcess( DebugEvent, &Process )) { + InheritHandles( Process ); + AddThread( DebugEvent, Process, &Thread ); + } + break; + + case EXIT_PROCESS_DEBUG_EVENT: + // + // Exit process event includes last thread of process + // as well. Remove process and thread from our process tree + // + + if (DeleteThread( Process, Thread )) { + DeleteProcess( Process ); + } + break; + + case CREATE_THREAD_DEBUG_EVENT: + // + // Create thread. Remember thread in our process tree. + // + + AddThread( DebugEvent, Process, &Thread ); + break; + + case EXIT_THREAD_DEBUG_EVENT: + // + // Exit thread. Remove thread from our process tree. + // + + DeleteThread( Process, Thread ); + break; + + case LOAD_DLL_DEBUG_EVENT: + // + // DLL just mapped into process address space. See if it is one + // of the ones we care about and if so, install the necessary + // breakpoints. + // + + InstallBreakpointsForDLL( Process, DebugEvent->u.LoadDll.lpBaseOfDll ); + break; + + case UNLOAD_DLL_DEBUG_EVENT: + // + // DLL just unmapped from process address space. See if it is one + // of the ones we care about and if so, remove the breakpoints we + // installed when it was mapped. + // + + RemoveBreakpointsForDLL( Process, DebugEvent->u.UnloadDll.lpBaseOfDll ); + break; + + case OUTPUT_DEBUG_STRING_EVENT: + case RIP_EVENT: + // + // Ignore these + // + break; + + case EXCEPTION_DEBUG_EVENT: + // + // Assume we wont handle this exception + // + + ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; + switch (DebugEvent->u.Exception.ExceptionRecord.ExceptionCode) { + // + // Breakpoint exception. + // + + case STATUS_BREAKPOINT: + EnterCriticalSection( &BreakTable ); + if (Thread->BreakpointToStepOver != NULL) { + // + // If this breakpoint was in place to step over an API entry + // point breakpoint, then deal with it by ending single + // step mode, resuming all threads in the process and + // reinstalling the API breakpoint we just stepped over. + // Finally return handled for this exception so the + // thread can proceed. + // + + EndSingleStepBreakpoint( Process, Thread ); + HandleThreadsForSingleStep( Process, Thread, FALSE ); + InstallBreakpoint( Process, Thread->BreakpointToStepOver ); + Thread->BreakpointToStepOver = NULL; + ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; + } + else { + // + // Otherwise, see if this breakpoint is either an API entry + // point breakpoint for the process or a return address + // breakpoint for a thread in the process. + // + Breakpoint = FindBreakpoint( DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress, + Process, + Thread + ); + if (Breakpoint != NULL) { + // + // This is one of our breakpoints, call the breakpoint + // handler. + // + if (HandleBreakpoint( Process, Thread, Breakpoint )) { + // + // Now single step over this breakpoint, by removing it and + // setting a breakpoint at the next instruction (or using + // single stepmode if the processor supports it). We also + // suspend all the threads in the process except this one so + // we know the next breakpoint/single step exception we see + // for this process will be for this one. + // + + Thread->BreakpointToStepOver = Breakpoint; + RemoveBreakpoint( Process, Breakpoint ); + HandleThreadsForSingleStep( Process, Thread, TRUE ); + BeginSingleStepBreakpoint( Process, Thread ); + } + else { + Thread->BreakpointToStepOver = NULL; + } + + ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; + } + else { + DbgEvent( DBGEVENT, ( "Skipping over hardcoded breakpoint at %x\n", + DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress + ) + ); + + // + // If not one of our breakpoints, assume it is a hardcoded + // breakpoint and skip over it. This will get by the one + // breakpoint in LdrInit triggered by the process being + // invoked with DEBUG_PROCESS. + // + + if (SkipOverHardcodedBreakpoint( Process, + Thread, + DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress + ) + ) { + // + // If we successfully skipped over this hard-coded breakpoint + // then return handled for this exception. + // + + ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; + } + } + } + LeaveCriticalSection( &BreakTable ); + break; + + case STATUS_SINGLE_STEP: + // + // We should only see this exception on processors that + // support a single step mode. + // + + EnterCriticalSection( &BreakTable ); + if (Thread->BreakpointToStepOver != NULL) { + EndSingleStepBreakpoint( Process, Thread ); + HandleThreadsForSingleStep( Process, Thread, FALSE ); + InstallBreakpoint( Process, Thread->BreakpointToStepOver ); + Thread->BreakpointToStepOver = NULL; + ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; + } + LeaveCriticalSection( &BreakTable ); + break; + + case STATUS_VDM_EVENT: + // + // Returning NOT_HANDLED will cause the default behavior to + // occur. + // + break; + + default: + DbgEvent( DBGEVENT, ( "Unknown exception: %08x at %08x\n", + DebugEvent->u.Exception.ExceptionRecord.ExceptionCode, + DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress + ) + ); + break; + } + break; + + default: + DbgEvent( DBGEVENT, ( "Unknown debug event\n" ) ); + break; + } + } + return( ContinueStatus ); +} + + +VOID +InstallBreakpointsForDLL( + PPROCESS_INFO Process, + LPVOID BaseOfDll + ) +{ + UCHAR ModuleIndex, ApiIndex; + PBREAKPOINT_INFO Breakpoint; + + // + // Loop over module table to see if the base address of this DLL matches + // one of the module handles we want to set breakpoints in. If not, ignore + // event. + // + + for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) { + if (ModuleInfo[ ModuleIndex ].ModuleHandle == BaseOfDll) { + // + // Loop over the list of API entry points for this module and set + // a process specific breakpoint at the first instruction of each + // entrypoint. + // + + for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) { + if (ModuleIndex == ApiInfo[ ApiIndex ].ModuleIndex) { + if (CreateBreakpoint( ApiInfo[ ApiIndex ].EntryPointAddress, + Process, + NULL, // process specific + ApiIndex, + NULL, + &Breakpoint + ) + ) { + Breakpoint->ModuleName = ModuleInfo[ ApiInfo[ ApiIndex ].ModuleIndex ].ModuleName; + Breakpoint->ProcedureName = ApiInfo[ ApiIndex ].EntryPointName; + DbgEvent( DBGEVENT, ( "Installed breakpoint for %ws!%s at %08x\n", + Breakpoint->ModuleName, + Breakpoint->ProcedureName, + ApiInfo[ ApiIndex ].EntryPointAddress + ) + ); + } + } + } + break; + } + } +} + +VOID +RemoveBreakpointsForDLL( + PPROCESS_INFO Process, + LPVOID BaseOfDll + ) +{ + UCHAR ModuleIndex, ApiIndex; + + // + // Loop over module index to see if the base address of this DLL matches + // one of the module handles we set breakpoints in above. If not, ignore + // event. + // + + for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) { + if (ModuleInfo[ ModuleIndex ].ModuleHandle == BaseOfDll) { + // + // Loop over the list of API entry points for this module and remove + // each process specific breakpoint set above for each entrypoint. + // + + for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) { + if (ModuleIndex == ApiInfo[ ApiIndex ].ModuleIndex) { + DestroyBreakpoint( ApiInfo[ ApiIndex ].EntryPointAddress, + Process, + NULL // process specific + ); + } + } + break; + } + } +} + + +BOOLEAN +InstallBreakpoint( + PPROCESS_INFO Process, + PBREAKPOINT_INFO Breakpoint + ) +{ + if (!Breakpoint->SavedInstructionValid && + !ReadMemory( Process, + Breakpoint->Address, + &Breakpoint->SavedInstruction, + SizeofBreakpointInstruction, + "save instruction" + ) + ) { + return FALSE; + } + else + if (!WriteMemory( Process, + Breakpoint->Address, + BreakpointInstruction, + SizeofBreakpointInstruction, + "breakpoint instruction" + ) + ) { + return FALSE; + } + else { + Breakpoint->SavedInstructionValid = TRUE; + return TRUE; + } +} + +BOOLEAN +RemoveBreakpoint( + PPROCESS_INFO Process, + PBREAKPOINT_INFO Breakpoint + ) +{ + if (!Breakpoint->SavedInstructionValid || + !WriteMemory( Process, + Breakpoint->Address, + &Breakpoint->SavedInstruction, + SizeofBreakpointInstruction, + "restore saved instruction" + ) + ) { + return FALSE; + } + else { + return TRUE; + } +} |