/*** ntsd.c - main program loop for NT debugger * * Copyright 1990, Microsoft Corporation * * Purpose: * To initialize and process the NT-OS/2 debugging system * events. * * Revision History: * * [-] 20-Mar-1990 Richk Created. * *************************************************************************/ #include #include #include #include "ntsdp.h" #include #include #include typedef DWORD (*PWX86_EXTENSION_ROUTINE)( HANDLE hCurrentProcess, HANDLE hCurrentThread, PNTSD_EXTENSION_APIS lpExtensionApis, DEBUG_EVENT *pDebugEvent, DWORD *pContinueStatus ); DWORD Wx86ExceptionEvent( DEBUG_EVENT *pDebugEvent, DWORD *pContinueStatus ); DWORD DoWx86ExtApi( PWX86_EXTENSION_ROUTINE pWx86Ext, DEBUG_EVENT *pDebugEvent, DWORD *pContinueStatus ); DWORD Wx86CreateProcessEvent( DEBUG_EVENT *pDebugEvent ); #define WX86_HANDLED 0 #define WX86_PROMPT 1 #define WX86_CONTINUE 2 typedef PFPO_DATA (*PFINDFPODATA)(DWORD dwPCAddr); typedef ULONG (*PWX86DISASM)( ULONG Offset, PUCHAR pBuffer, BOOLEAN fShowEA, LPVDMCONTEXT ContextX86 ); typedef struct _NtsdRoutines { PFINDFPODATA FindFpoDataForModule; PREAD_PROCESS_MEMORY_ROUTINE SwReadMemory; PGET_MODULE_BASE_ROUTINE SwGetModuleBase; PWX86DISASM Wx86Disasm; } NTSDROUTINES, *PNTSDROUTINES; PWX86_EXTENSION_ROUTINE pfnWx86ExtCreateProcess; PWX86_EXTENSION_ROUTINE pfnWx86ExtException; HANDLE hWx86ExtensionsDll=NULL; SYSTEM_INFO SystemInfo; OSVERSIONINFO OsVersionInfo; // from ntcmd.c , used by Wx86 enhancements ULONG GetExpressionRoutine(char *CommandString); void GetSymbolRoutine(LPVOID, PUCHAR, PULONG); DWORD disasmExportRoutine(LPDWORD, LPSTR, BOOL); DWORD CheckControlC (VOID); void SuspendAllThreads(void); void ResumeAllThreads(void); // from stkwalk.c BOOL SwReadMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead ); DWORD SwGetModuleBase( HANDLE hProcess, DWORD Address ); CHAR symBuffer[SYM_BUFFER_SIZE]; CHAR symStartBuffer[SYM_BUFFER_SIZE]; PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) symBuffer; PIMAGEHLP_SYMBOL symStart = (PIMAGEHLP_SYMBOL) symStartBuffer; #define EXCEPTIONCODE lpDebugEvent->u.Exception.ExceptionRecord.ExceptionCode #define FIRSTCHANCE lpDebugEvent->u.Exception.dwFirstChance PPROCESS_INFO pProcessHead; PPROCESS_INFO pProcessEvent; PPROCESS_INFO pProcessCurrent; int fControlC; int fFlushInput; #define EXCEPTION_LIST_MAX 20 BOOLEAN fDefaultExceptionBreak; DWORD ExceptionList[EXCEPTION_LIST_MAX]; ULONG cExceptionList; DWORD dwPidToDebug; HANDLE hProcessToDebug; HANDLE BaseHandle; BOOLEAN fWaitingForDebugEvent; DWORD dwRipPrintLevel = SLE_ERROR; DWORD dwRipBreakLevel = SLE_ERROR; DWORD hEventToSignal; HANDLE hThreadToResume; BOOLEAN fCreateProcessAlso; BOOLEAN fStopFirst = TRUE; BOOLEAN fWOWStopFirst; BOOLEAN fLazyLoad = TRUE; BOOLEAN fStopOnProcessExit = TRUE; PUCHAR LogFileName = "ntsd.log"; PUCHAR DefaultExtDllName = NULL; BOOLEAN fLogAppend; BOOLEAN fExecuteLoop = TRUE; BOOLEAN MYOB; char szUnknownImage[] = "UNKNOWN"; ULONG GetPageSize(void); void NtsdExecution(void); BOOLEAN GetDefaultBreak(DWORD); void SetDefaultBreak(DWORD, BOOL); PUCHAR SetDefaultExtDllName(PUCHAR); void fnSetException(void); void ListDefaultBreak(void); DWORD GetContinueStatus(DWORD, BOOLEAN); void InitEventVars(DEBUG_EVENT *); void SetTermStatus(DEBUG_EVENT *); PPROCESS_INFO pProcessFromIndex(ULONG); PTHREAD_INFO pThreadFromIndex(ULONG); PIMAGE_INFO pImageFromIndex(UCHAR); static int GetToken(PUCHAR*, PUCHAR); void ReadIniFile(PULONG); void AddProcess(DEBUG_EVENT *); void DelProcess(DEBUG_EVENT *); void AddThread(DEBUG_EVENT *); void DelThread(DEBUG_EVENT *); void AddImage(DEBUG_EVENT *); void DelImage(DEBUG_EVENT *); void PrintDebugString(DEBUG_EVENT *); void PrintRip(DEBUG_EVENT *); BOOL VDMEvent(DEBUG_EVENT *); void fnBangCmd(PUCHAR, PUCHAR*); PPROCESS_INFO pProcessFromEvent(DEBUG_EVENT *); PTHREAD_INFO pThreadFromEvent(DEBUG_EVENT *); void OutputProcessInfo(char *); void BrkptInit(void); VOID ValidateBreakpointTable( PCHAR file, int line ); NTSTATUS DbgKdWriteBreakPoint(PVOID, PULONG); NTSTATUS DbgKdRestoreBreakPoint(ULONG); BOOLEAN ReadVirtualMemory(PUCHAR, PUCHAR, ULONG, PULONG); NTSTATUS DbgKdWriteVirtualMemory(PVOID, PVOID, ULONG, PULONG); void RemoveProcessBps(PPROCESS_INFO); void RemoveThreadBps(PTHREAD_INFO); void DeferSymbolLoad(PIMAGE_INFO); PVOID GetClientId(void); long vm86DefaultSeg = -1L; unsigned short fVm86; unsigned short f16pm; PUCHAR pszScriptFile; BOOLEAN fCreateThreadBreak; BOOLEAN fExitThreadBreak; BOOLEAN fLoadDllBreak; BOOLEAN fPortDisconnectBreak; BOOLEAN fIgnoreLdrThunkAV; #define STATUS_CPP_EH_EXCEPTION 0xe06d7363 BOOLEAN fEhExceptionBreak; BOOLEAN fAccessViolationBreak = TRUE; BOOLEAN fInpageIoErrorBreak = TRUE; BOOLEAN fControlCHandled = TRUE; BOOLEAN fDivideByZeroBreak = TRUE; BOOLEAN fInvalidHandleBreak = TRUE; UCHAR oldcmdState; BOOLEAN fSecondConsole; // if FALSE,run child in same console BOOLEAN fDebugOutput; // if FALSE, output to user screen // if TRUE, output to debug screen BOOLEAN fVerboseOutput; // if TRUE, output verbose info HWND hOurWnd; extern ULONG pageSize; extern ULONG WatchCount; extern ULONG SystemReportedTime; extern BOOLEAN Timing; BOOL fVDMInitDone; BOOL fVDMActive; BOOL (WINAPI *pfnVDMProcessException)(LPDEBUG_EVENT); BOOL (WINAPI *pfnVDMGetThreadSelectorEntry)(HANDLE,HANDLE,DWORD,LPVDMLDT_ENTRY); ULONG (WINAPI *pfnVDMGetPointer)(HANDLE,HANDLE,WORD,DWORD,BOOL); BOOL (WINAPI *pfnVDMGetThreadContext)(LPDEBUG_EVENT,LPVDMCONTEXT); BOOL (WINAPI *pfnVDMSetThreadContext)(LPDEBUG_EVENT,LPVDMCONTEXT); BOOL (WINAPI *pfnVDMKillWOW)(VOID); BOOL (WINAPI *pfnVDMDetectWOW)(VOID); BOOL (WINAPI *pfnVDMBreakThread)(HANDLE); BOOL (WINAPI *pfnVDMGetSelectorModule)(HANDLE,HANDLE,WORD,PUINT,LPSTR, UINT,LPSTR, UINT); BOOL (WINAPI *pfnVDMGetModuleSelector)(HANDLE,HANDLE,UINT,LPSTR,LPWORD); BOOL (WINAPI *pfnVDMModuleFirst)(HANDLE,HANDLE,LPMODULEENTRY,DEBUGEVENTPROC,LPVOID); BOOL (WINAPI *pfnVDMModuleNext)(HANDLE,HANDLE,LPMODULEENTRY,DEBUGEVENTPROC,LPVOID); BOOL (WINAPI *pfnVDMGlobalFirst)(HANDLE,HANDLE,LPGLOBALENTRY,WORD,DEBUGEVENTPROC,LPVOID); BOOL (WINAPI *pfnVDMGlobalNext)(HANDLE,HANDLE,LPGLOBALENTRY,WORD,DEBUGEVENTPROC,LPVOID); typedef struct _segentry { int type; LPSTR path_name; WORD selector; WORD segment; DWORD ImgLen; // MODLOAD only } SEGENTRY; #define SEGTYPE_AVAILABLE 0 #define SEGTYPE_V86 1 #define SEGTYPE_PROT 2 #define MAXSEGENTRY 1024 SEGENTRY segtable[MAXSEGENTRY]; #ifdef BP_CORRUPTION BOOL IsAddrInBrkptList( DWORD Address ); #endif BP_CORRUPTION BOOL NtsdDebugActiveProcess ( DWORD PidToDebug ); BOOL ControlCHandler( IN ULONG CtrlType ) { HANDLE Thread; DWORD ThreadId; UNREFERENCED_PARAMETER(CtrlType); fControlC = 1; fFlushInput = TRUE; if (fWaitingForDebugEvent && dwPidToDebug != 0) { if (hProcessToDebug == NULL) { hProcessToDebug = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPidToDebug); if (hProcessToDebug == NULL) dprintf( "%s: Unable to open ProcessId: %x\n", DebuggerName, dwPidToDebug ); else if ((BaseHandle = GetModuleHandle( "kernel32" )) == NULL) dprintf( "%s: Unable to get KERNEL32 module handle.\n", DebuggerName, dwPidToDebug ); } Thread = CreateRemoteThread( hProcessToDebug, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(BaseHandle,"DebugBreak"), NULL, 0, &ThreadId ); if ( Thread ) { CloseHandle( Thread ); } } return TRUE; } NtsdMainExceptionFilter( LPEXCEPTION_POINTERS ep ) { DWORD ExceptionCode = ep->ExceptionRecord->ExceptionCode; PVOID ExceptionAddress = ep->ExceptionRecord->ExceptionAddress; PDWORD ExceptionInformation = ep->ExceptionRecord->ExceptionInformation; #ifdef BP_CORRUPTION if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { if (ExceptionInformation[0] == 1 && IsAddrInBrkptList(ExceptionInformation[1])) { dprintf("%s: Unexpected write to breakpoint list (%08x) at code address %08x\n", DebuggerName, ExceptionInformation[1], (DWORD)ExceptionAddress ); DbgPrint("%s: Unexpected write to breakpoint list (%08x) at code address %08x\n", DebuggerName, ExceptionInformation[1], (DWORD)ExceptionAddress ); DebugBreak(); } } #endif BP_CORRUPTION if (dwPidToDebug == 0xffffffff) { dprintf("%s: Fatal Unhandled Exception %x while debugging CSR\n", DebuggerName, ExceptionCode); DbgPrint("%s: Fatal Unhandled Exception %x while debugging CSR\n", DebuggerName, ExceptionCode); DebugBreak(); } return EXCEPTION_CONTINUE_SEARCH; } /*** main - main program of NT-WIN32 debugger * * Purpose: * To perform the debugging system initialization, allocate system * resources for debugger use, and start debuggee execution. * * Input: * argc - argument count * argv - argument vector array * envp - pointer to environment variable list * * Output: * None. * * Exceptions: * error exit: * no program name given * program file not found * CreateProcess failure * * Notes: * Never returns - NtsdExecution runs until termination. * *************************************************************************/ int _CRTAPI1 main( int argc, char *argv[], char *envp[] ) { LPSTR lpstrCmd; STARTUPINFO StartupInfo; STARTUPINFO MyStartupInfo; PROCESS_INFORMATION ProcessInformation; BOOL b; UCHAR ch; ULONG n; ULONG debugType = DEBUG_ONLY_THIS_PROCESS; ULONG separateWowVDM = 0; DWORD dwTemp; extern PUCHAR Version_String; DWORD ExitCode; ULONG SymOptions = SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_NO_CPP; UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); UNREFERENCED_PARAMETER(envp); DebuggerName = "NTSD"; ExitCode = 0; GetSystemInfo(&SystemInfo); OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo); GetVersionEx(&OsVersionInfo); __try { ReadIniFile(&debugType); pageSize = GetPageSize(); lpstrCmd = GetCommandLine(); // skip over program name do ch = *lpstrCmd++; while (ch != ' ' && ch != '\t' && ch != '\0'); // skip over any following white space while (ch == ' ' || ch == '\t') ch = *lpstrCmd++; // process each switch character '-' as encountered while (ch == '-') { ch = *lpstrCmd++; // process multiple switch characters as needed do { switch (ch) { case '-': // '--' is the equivalent of -G -g -o -p -1 debugType = DEBUG_PROCESS; fLazyLoad = TRUE; fStopOnProcessExit = FALSE; fDebugOutput = TRUE; fStopFirst = FALSE; dwPidToDebug = 0xffffffff; ch = *lpstrCmd++; break; case 'S': case 's': fLazyLoad = FALSE; ch = *lpstrCmd++; break; case 'I': case 'i': fIgnoreLdrThunkAV = TRUE; ch = *lpstrCmd++; break; case 'A': case 'a': lpstrCmd++; lpstrCmd = SetDefaultExtDllName(lpstrCmd); ch = *lpstrCmd; break; case 'O': case 'o': debugType = DEBUG_PROCESS; ch = *lpstrCmd++; break; case '2': fSecondConsole = TRUE; ch = *lpstrCmd++; break; case 'D': case 'd': fDebugOutput = TRUE; ch = *lpstrCmd++; break; case 'g': fStopFirst = FALSE; ch = *lpstrCmd++; break; case 'G': fStopOnProcessExit = FALSE; ch = *lpstrCmd++; break; case 'V': case 'v': fVerboseOutput = TRUE; ch = *lpstrCmd++; break; case 'W': case 'w': separateWowVDM = CREATE_SEPARATE_WOW_VDM; ch = *lpstrCmd++; break; case 'P': case 'p': // pid debug takes decimal argument do ch = *lpstrCmd++; while (ch == ' ' || ch == '\t'); if (dwPidToDebug) { dprintf("%s: pid number redefined\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } if ( ch == '-' ) { ch = *lpstrCmd++; if ( ch == '1' ) { fDebugOutput = TRUE; dwPidToDebug = 0xffffffff; ch = *lpstrCmd++; } } else { while (ch >= '0' && ch <= '9') { dwTemp = dwPidToDebug * 10 + ch - '0'; if (dwTemp < dwPidToDebug) { dprintf("%s: pid number overflow\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } dwPidToDebug = dwTemp; ch = *lpstrCmd++; } } if (!dwPidToDebug) { dprintf("%s: bad pid '%ld'\n", DebuggerName, dwPidToDebug); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } break; case 'R': case 'r': // Rip flags takes single-char decimal argument do { ch = *lpstrCmd++; } while (ch == ' ' || ch == '\t'); dwRipBreakLevel = ch - '0'; if (dwRipBreakLevel > 3) { dprintf("%s: bad Rip level '%ld'\n", DebuggerName, dwRipBreakLevel); dwRipBreakLevel = 0; } else { dwRipPrintLevel = dwRipBreakLevel; } ch = *lpstrCmd++; break; case 'T': case 't': // Rip flags takes single-char decimal argument do ch = *lpstrCmd++; while (ch == ' ' || ch == '\t'); dwRipPrintLevel = ch - '0'; if (dwRipPrintLevel > 3) { dprintf("%s: bad Rip level '%ld'\n", DebuggerName, dwRipPrintLevel); dwRipPrintLevel = 0; } ch = *lpstrCmd++; break; case 'e': case 'E': // event to signal takes decimal argument do ch = *lpstrCmd++; while (ch == ' ' || ch == '\t'); if (hEventToSignal) { dprintf("%s: Event to signal redefined\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } while (ch >= '0' && ch <= '9') { dwTemp = hEventToSignal * 10 + ch - '0'; if (dwTemp < hEventToSignal) { dprintf("%s: Event To Signal\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } hEventToSignal = dwTemp; ch = *lpstrCmd++; } if (!hEventToSignal) { dprintf("%s: bad hEventToSignal '%ld'\n", DebuggerName, hEventToSignal); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } break; case 'M': case 'm': ch = *lpstrCmd++; if (_strcmpi(lpstrCmd, "yob") == 0) { MYOB = TRUE; break; } else { goto badswitch; } case 'x': case 'X': ch = *lpstrCmd++; if (ch == 0 || ch == ' ' || ch == '\t') { fAccessViolationBreak = FALSE; break; } else if (ch == 'e') { b = TRUE; } else if (ch == 'd') { b = FALSE; } else { goto badswitch; } while (isspace(*lpstrCmd)) { lpstrCmd++; } dwTemp = strtol(lpstrCmd, &lpstrCmd, 16); if (dwTemp == 0) { goto badswitch; } SetDefaultBreak(dwTemp, b); ch = *lpstrCmd++; break; case 'Z': case 'z': ch = *lpstrCmd++; if (GetClientId()) { dprintf("%s: OS2 SubSystem not started", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } break; default: badswitch: dprintf("%s: bad switch '%c'\n", DebuggerName, ch); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } } while (ch != ' ' && ch != '\t' && ch != '\0'); // skip over any following white space while (ch == ' ' || ch == '\t') ch = *lpstrCmd++; } // if no image name and not attaching to active process, error if (ch == '\0' && !dwPidToDebug) { dprintf("Usage: ntsd [-o] [-d] (-p pid-num | " "name-of-image [parameters...])\n"); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } lpstrCmd--; if (fLazyLoad) { SymOptions |= SYMOPT_DEFERRED_LOADS; } SymSetOptions( SymOptions ); if (!DefaultExtDllName) { SetDefaultExtDllName("userexts"); } #if defined (TARGET_MIPS) if (OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && (OsVersionInfo.dwBuildNumber & 0xffff) > 1144) { MipsContextSize = Ctx64Bit; } else { MipsContextSize = Ctx32Bit; } #endif // TARGET_MIPS if (!fDebugOutput) { #ifndef NTSD_INHERIT_CONSOLE BOOL Success; Success = AllocConsole(); assert(Success); if (!SetConsoleCtrlHandler(ControlCHandler, TRUE)) { dprintf("Warning: unable to set control-c handler.\n"); } ConsoleInputHandle = CreateFile( "CONIN$", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (ConsoleInputHandle == INVALID_HANDLE_VALUE) { DbgPrint( "%s: Unable to open input console handle - error %u\n", DebuggerName, GetLastError() ); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } ConsoleOutputHandle = CreateFile( "CONOUT$", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (ConsoleOutputHandle == INVALID_HANDLE_VALUE) { DbgPrint( "%s: Unable to open output console handle - error %u\n", DebuggerName, GetLastError() ); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } #else // ndef NTSD_INHERIT_CONSOLE if (!SetConsoleCtrlHandler(ControlCHandler, TRUE)) { dprintf("%s: unable to set control-c handler.\n", DebuggerName); } ConsoleInputHandle = GetStdHandle(STD_INPUT_HANDLE); ConsoleOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE); #endif SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); } else { SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS); } if ( ch != '\0' && dwPidToDebug ) { fCreateProcessAlso = TRUE; debugType |= CREATE_SUSPENDED; } if (dwPidToDebug == 0 || fCreateProcessAlso) { GetStartupInfo(&MyStartupInfo); memset(&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.lpDesktop = MyStartupInfo.lpDesktop; debugType |= separateWowVDM; b = CreateProcess( NULL, lpstrCmd, NULL, NULL, TRUE, debugType | (fSecondConsole?CREATE_NEW_CONSOLE:0), // DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &StartupInfo, &ProcessInformation ); if (!b) { dprintf("%s: cannot execute '%s' : error = %lx\n", DebuggerName, lpstrCmd, GetLastError()); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } else { if ( fCreateProcessAlso ) { hThreadToResume = ProcessInformation.hThread; } } } if ( dwPidToDebug ) { // // Enable the privilege that allows the user to debug // another process. // // If this call fails, we can't debug any other processes. // b = NtsdDebugActiveProcess(dwPidToDebug); if (!b) { dprintf("%s: cannot debug pid %ld (error = %ld)\n", DebuggerName, dwPidToDebug, GetLastError()); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } } InitNtCmd(); dprintf("%s",Version_String); dprintf("CommandLine: %s\n",lpstrCmd); SetDebugErrorLevel(max(dwRipPrintLevel, dwRipBreakLevel)); if (dwPidToDebug == 0xffffffff) { CloseProfileUserMapping(); } NtsdExecution(); } __except (NtsdMainExceptionFilter(GetExceptionInformation())) { ; } if (dwPidToDebug == 0 || fCreateProcessAlso) { if (!GetExitCodeProcess(ProcessInformation.hProcess, &ExitCode)) { ExitCode = (DWORD)STATUS_UNSUCCESSFUL; } } ExitProcess(ExitCode); return (int)ExitCode; } ULONG GetPageSize (void) { return (ULONG)SystemInfo.dwPageSize; } /*** DebugEventHandler - main dispatch table * * Purpose: * As debug events come in, they each have to be handled in a unique * manner. This routine does all of the processing required for each * event. * * Also this routine serves the callback mechanism for VDM debug events * using the VDMDBG.DLL apis (they require the ability of calling back * into the debugger). * * Significant events include creation and termination of processes * and threads, and events such as breakpoints. * * Data structures for processes and threads are created and * maintained for use by the program. * * For each event, ProcessStateChange is called which determines if * the event is significant to the debugger to cause it to display * output and/or enter command mode. * * Input: * None. * * Output: * ContinueStatus - A continue status for ContinueDebugEvent * * Exceptions: * error exit: * DbgUiContinue failure * * Notes: * *************************************************************************/ DWORD DebugEventHandler( LPDEBUG_EVENT lpDebugEvent, LPVOID lpData ) { DWORD ContinueStatus; BOOL b; ContinueStatus = (DWORD)DBG_CONTINUE; switch (lpDebugEvent->dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: if (fVerboseOutput) dprintf("*** create process\n"); AddProcess(lpDebugEvent); OutputProcessInfo("*** create process ***"); // never break for process creation. wait for // DBG_DLLS_LOADED exception for first break. if (fVerboseOutput) dprintf("%s: process created: %ld.%ld\n", DebuggerName, lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId); Wx86CreateProcessEvent(lpDebugEvent); break; case EXIT_PROCESS_DEBUG_EVENT: if (fVerboseOutput) dprintf("*** exit process\n"); InitEventVars(lpDebugEvent); if (pProcessCurrent->fWx86Process) { DoWx86ExtApi(pfnWx86ExtCreateProcess, lpDebugEvent, NULL); } SetTermStatus(lpDebugEvent); if (fVerboseOutput) dprintf("%s: process exited: %ld.%ld\n", DebuggerName, lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId); if (fStopOnProcessExit) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); } DelProcess(lpDebugEvent); OutputProcessInfo("*** exit process ***"); fExecuteLoop = (BOOLEAN)(pProcessHead != NULL); break; case CREATE_THREAD_DEBUG_EVENT: if (fVerboseOutput) dprintf("*** create thread\n"); AddThread(lpDebugEvent); if (fVerboseOutput) dprintf("%s: thread created: %ld.%ld\n", DebuggerName, lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId); OutputProcessInfo("*** create thread ***"); // break on thread creation on flag set InitEventVars(lpDebugEvent); oldcmdState = cmdState; cmdState = 'i'; ProcessStateChange(FALSE,(BOOLEAN)(fCreateThreadBreak ? FALSE : TRUE)); if (pProcessCurrent->fWx86Process) { DoWx86ExtApi(pfnWx86ExtCreateProcess, lpDebugEvent, NULL); } break; case EXIT_THREAD_DEBUG_EVENT: if (fVerboseOutput) dprintf("*** exit thread\n"); InitEventVars(lpDebugEvent); if (pProcessCurrent->fWx86Process) { DoWx86ExtApi(pfnWx86ExtCreateProcess, lpDebugEvent, NULL); } SetTermStatus(lpDebugEvent); if (fVerboseOutput) dprintf("%s: thread exited: %ld.%ld\n", DebuggerName, lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId); // break on thread exit on flag set if (fExitThreadBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); } DelThread(lpDebugEvent); OutputProcessInfo("*** exit thread ***"); break; case LOAD_DLL_DEBUG_EVENT: AddImage(lpDebugEvent); OutputProcessInfo("*** load dll ***"); // break on DLL load on flag set if (fLoadDllBreak) { cmdState = 'i'; InitEventVars(lpDebugEvent); ProcessStateChange(FALSE, FALSE); } break; case UNLOAD_DLL_DEBUG_EVENT: OutputProcessInfo("*** unload dll ***"); DelImage(lpDebugEvent); // never break for DLL unload OutputProcessInfo("*** unload dll ***"); break; case OUTPUT_DEBUG_STRING_EVENT: PrintDebugString(lpDebugEvent); // never break for debug string event break; case RIP_EVENT: if (lpDebugEvent->u.RipInfo.dwType <= dwRipPrintLevel) { PrintRip(lpDebugEvent); } if (lpDebugEvent->u.RipInfo.dwType <= dwRipBreakLevel) { InitEventVars(lpDebugEvent); ProcessStateChange(FALSE, FALSE); ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; } break; case EXCEPTION_DEBUG_EVENT: InitEventVars(lpDebugEvent); // // If we are debugging a crashed process, force the // desktop we are on to the front so the user will know // what happened. // if ( hEventToSignal ) { HDESK hDesk; if (!fDebugOutput) { hDesk = GetThreadDesktop(GetCurrentThreadId()); SwitchDesktop(hDesk); } } if (pProcessCurrent->fWx86Process) { switch (Wx86ExceptionEvent(lpDebugEvent, &ContinueStatus)) { case WX86_PROMPT: if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ResumeAllThreads(); // fall thru case WX86_HANDLED: if ( hEventToSignal ) { SetEvent((HANDLE)hEventToSignal); hEventToSignal = 0L; } return ContinueStatus; case WX86_CONTINUE: default: // undefined nonsense fall thru to normal actions break; } } switch (EXCEPTIONCODE) { case STATUS_BREAKPOINT: SystemReportedTime = lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS-1]; WatchCount++; if (fVerboseOutput) dprintf("*** breakpoint exception\n"); ProcessStateChange(TRUE, (BOOLEAN)(pProcessEvent->fStopOnBreakPoint == FALSE)); pProcessEvent->fStopOnBreakPoint = TRUE; ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; if ( hThreadToResume ) { ResumeThread(hThreadToResume); hThreadToResume = NULL; } break; case STATUS_SINGLE_STEP: SystemReportedTime = lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS-1]; WatchCount++; if (fVerboseOutput) dprintf("*** single step exception\n"); ProcessStateChange(FALSE, FALSE); ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED; break; case DBG_CONTROL_C: case DBG_CONTROL_BREAK: if ( EXCEPTIONCODE == DBG_CONTROL_C ) { dprintf("%s: control-c exception\n", DebuggerName); } else { dprintf("%s: control-break exception\n", DebuggerName); } if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, fControlCHandled); break; case STATUS_ACCESS_VIOLATION: if (FIRSTCHANCE && fIgnoreLdrThunkAV) { UCHAR chSymBuffer[SYMBOLSIZE]; LPSTR s; ULONG displacement; // // This option allows new 3.51 binaries to run under // this debugger on old 3.1, 3.5 systems and avoid stopping // at access violations inside LDR that will be handled // by the LDR anyway. // GetSymbolStdCall( (ULONG)(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress), chSymBuffer, &displacement, NULL ); s = (LPSTR)chSymBuffer; if (!_strnicmp( s, "ntdll!", 6 )) { s += 6; if (*s == '_') s += 1; if (!_stricmp( s, "LdrpSnapThunk" ) || !_stricmp( s, "LdrpWalkImportDescriptor" ) ) { goto ignoreAV; } } } dprintf("%s: access violation\n", DebuggerName); if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); if (!FIRSTCHANCE || fAccessViolationBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } else { ignoreAV: ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; } break; case STATUS_IN_PAGE_ERROR: dprintf("%s: in page io error %x\n", DebuggerName, lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[ 2 ] ); if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); if (!FIRSTCHANCE || fInpageIoErrorBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } else ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; break; case STATUS_INVALID_HANDLE: dprintf("%s: Invalid handle\n", DebuggerName ); if (!FIRSTCHANCE) { dprintf("%s: !!! second chance !!!\n", DebuggerName); } if (!FIRSTCHANCE || fInvalidHandleBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } else { ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; } break; case STATUS_PORT_DISCONNECTED: if (!FIRSTCHANCE || fPortDisconnectBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } else ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; break; case STATUS_DATATYPE_MISALIGNMENT: dprintf("%s: datatype misalignment\n", DebuggerName); if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); break; case STATUS_INTEGER_DIVIDE_BY_ZERO: dprintf("%s: integer divide by zero\n", DebuggerName); if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); if (!FIRSTCHANCE || fDivideByZeroBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } else ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; break; case STATUS_CPP_EH_EXCEPTION: dprintf("%s: C++ EH Exception\n", DebuggerName); if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); if (!FIRSTCHANCE || fEhExceptionBreak) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } else ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED; break; case STATUS_POSSIBLE_DEADLOCK: { CHAR Symbol[SYMBOLSIZE]; DWORD Displacement; GetSymbolStdCall(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0], Symbol, &Displacement, NULL ); dprintf("%s: Possible Deadlock Lock %s+%lx at %lx\n", DebuggerName, Symbol, Displacement, lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); if (!FIRSTCHANCE) { dprintf("%s: !!! second chance !!!\n", DebuggerName); } cmdState = 'i'; ProcessStateChange(FALSE, FALSE); ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); } break; case STATUS_VDM_EVENT: b = VDMEvent(lpDebugEvent); ContinueStatus = b ? (DWORD)DBG_CONTINUE : (DWORD)DBG_EXCEPTION_NOT_HANDLED; break; default: dprintf("%s: exception number %08lx\n", DebuggerName, EXCEPTIONCODE); if (!FIRSTCHANCE) dprintf("%s: !!! second chance !!!\n", DebuggerName); if (!FIRSTCHANCE || GetDefaultBreak(EXCEPTIONCODE)) { cmdState = 'i'; ProcessStateChange(FALSE, FALSE); } ContinueStatus = GetContinueStatus(FIRSTCHANCE, FALSE); break; } // // Do this for all exceptions, just in case some other // thread caused an exception before we get around to // handling the breakpoint event. // if ( hEventToSignal ) { SetEvent((HANDLE)hEventToSignal); hEventToSignal = 0L; } break; default: dprintf("%s: event number %08lx\n", DebuggerName, lpDebugEvent->dwDebugEventCode); cmdState = 'i'; ProcessStateChange(FALSE, FALSE); break; } return( ContinueStatus ); } /*** NtsdExecution - main execution loop * * Purpose: * Main execution loop for debugger. Within the loop, it waits * for a debugging system state change and processes it accordingly. * Significant events include creation and termination of processes * and threads, and events such as breakpoints. * Data structures for processes and threads are created and * maintained for use by the program. * For each event, ProcessStateChange is called which determines if * the event is significant to the debugger to cause it to display * output and/or enter command mode. * * Input: * None. * * Output: * pExecuteThread - pointer to structure of current thread * * Exceptions: * error exit: * DbgUiContinue failure * program exit: * termination of last process * * Notes: * *************************************************************************/ void NtsdExecution (void) { BOOL b; DEBUG_EVENT DebugEvent; DWORD ContinueStatus; HWND currentFocus = NULL; while (fExecuteLoop) { if (fVerboseOutput) dprintf("*** wait for debug event\n"); fWaitingForDebugEvent = TRUE; b = WaitForDebugEvent(&DebugEvent,0xffffffff); if (!b) { dprintf("%s: WaitForDebugEvent failed %d\n", DebuggerName, GetLastError()); dprintf("%s: CommandLine was %s\n", DebuggerName, GetCommandLine()); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } fWaitingForDebugEvent = FALSE; ContinueStatus = DebugEventHandler( &DebugEvent, NULL ); b = ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, ContinueStatus); if (!b) { dprintf("%s: ContinueDebugEvent failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } } } BOOLEAN GetDefaultBreak (DWORD ExceptionCode) { ULONG index; BOOLEAN fBreak; fBreak = fDefaultExceptionBreak; for (index = 0; index < cExceptionList; index++) if (ExceptionCode == ExceptionList[index]) { fBreak = (BOOLEAN)!fBreak; break; } return fBreak; } void SetDefaultBreak (DWORD ExceptionCode, BOOL fSet) { ULONG index; if (ExceptionCode == 0) { // exception code 0 is for global set and clear fDefaultExceptionBreak = fSet; cExceptionList = 0; } else if (fDefaultExceptionBreak == fSet) { // exception state same as global flag clears entry // in list if there for (index = 0; index < cExceptionList; index++) { if (ExceptionCode == ExceptionList[index]) { cExceptionList--; while (index < cExceptionList) { ExceptionList[index] = ExceptionList[index + 1]; index++; } break; } } } else { // exception state different from global flag is added // to list if not already there for (index = 0; index < cExceptionList; index++) if (ExceptionCode == ExceptionList[index]) break; if (index == cExceptionList) { if (cExceptionList == EXCEPTION_LIST_MAX) error(LISTSIZE); ExceptionList[cExceptionList++] = ExceptionCode; } } } void fnSetException (void) { UCHAR ch; UCHAR ch2; BOOLEAN fSetException; ULONG value; UCHAR prevCmdState; ch = PeekChar(); ch = (UCHAR)tolower(ch); if (ch == '\0') ListDefaultBreak(); else { pchCommand++; if (ch == 'e') fSetException = TRUE; else if (ch == 'd') fSetException = FALSE; else error(SYNTAX); ch = PeekChar(); ch = (UCHAR)tolower(ch); pchCommand++; if (ch == '*') SetDefaultBreak(0, fSetException); else { ch2 = (UCHAR)tolower(*pchCommand); pchCommand++; if (ch == 'c' && ch2 == 't') fCreateThreadBreak = fSetException; else if (ch == 'e' && ch2 == 't') fExitThreadBreak = fSetException; else if (ch == 'l' && ch2 == 'd') fLoadDllBreak = fSetException; else if (ch == 'a' && ch2 == 'v') fAccessViolationBreak = fSetException; else if (ch == 'i' && ch2 == 'p') fInpageIoErrorBreak = fSetException; else if (ch == '3' && ch2 == 'c') fPortDisconnectBreak = fSetException; else if (ch == 'c' && ch2 == 'c') fControlCHandled = fSetException; else if (ch == 'd' && ch2 == 'z') fDivideByZeroBreak = fSetException; else if (ch == 'e' && ch2 == 'h') fEhExceptionBreak = fSetException; else if (ch == 'c' && ch2 == 'h') fInvalidHandleBreak = fSetException; else if (ch == 'p' && ch2 == 'f') { // call routine to start/stop profiling of DLL fProfilingDLL = fSetException; prevCmdState = cmdState; cmdState = 'd'; if (fSetException) { if (fProfilingDLL) { fnStartProfilingDLL(&ps_ProfileDLL); } else { fProfilingDLL = fnStartProfilingDLL(&ps_ProfileDLL); } } else { fProfilingDLL = TRUE; fnStopProfilingDLL(&ps_ProfileDLL); fProfilingDLL = FALSE; } cmdState = prevCmdState; } else { pchCommand -= 2; value = GetExpression(); if (value == 0) error(SYNTAX); SetDefaultBreak(value, fSetException); } } } } void ListDefaultBreak (void) { ULONG index; dprintf("ct - break on create thread - "); dprintf(fCreateThreadBreak ? "enabled\n" : "disabled\n"); dprintf("et - break on exit thread - "); dprintf(fExitThreadBreak ? "enabled\n" : "disabled\n"); dprintf("ld - break on load DLL - "); dprintf(fLoadDllBreak ? "enabled\n" : "disabled\n"); dprintf("av - break on access violation - "); dprintf(fAccessViolationBreak ? "enabled\n" : "disabled\n"); dprintf("dz - break on divide by zero - "); dprintf(fDivideByZeroBreak ? "enabled\n" : "disabled\n"); dprintf("ip - break on in page io error - "); dprintf(fInpageIoErrorBreak ? "enabled\n" : "disabled\n"); dprintf("3c - break on gui app exit - "); dprintf(fPortDisconnectBreak ? "enabled\n" : "disabled\n"); dprintf("cc - handle control-c - "); dprintf(fControlCHandled ? "enabled\n" : "disabled\n"); dprintf("eh - break on C++ EH exception - "); dprintf(fEhExceptionBreak ? "enabled\n" : "disabled\n"); dprintf("ch = break on closing bad handle - "); dprintf(fInvalidHandleBreak ? "enabled\n" : "disabled\n"); dprintf("pf - profiling DLL - "); dprintf(fProfilingDLL ? "enabled\n" : "disabled\n"); dprintf("\n* - break on default exception - "); dprintf(fDefaultExceptionBreak ? "enabled\n" : "disabled\n"); if (cExceptionList) { dprintf(" opposite break on:\n"); for (index = 0; index < cExceptionList; index++) dprintf(" %08lx\n", ExceptionList[index]); } } PUCHAR SetDefaultExtDllName(PUCHAR lpstrCmd) { LPSTR lpTemp; DWORD dwTemp; if (DefaultExtDllName) { free(DefaultExtDllName); } while (*lpstrCmd == ' ' || *lpstrCmd == '\t') lpstrCmd++; lpTemp = lpstrCmd; while (*lpTemp && *lpTemp != ' ' && *lpTemp != '\t') lpTemp++; dwTemp = lpTemp - lpstrCmd; DefaultExtDllName = malloc(dwTemp+1); if (!DefaultExtDllName) { dprintf("%s: Couldn't allocate memory\n", DebuggerName); } else { strncpy(DefaultExtDllName, lpstrCmd, dwTemp); DefaultExtDllName[dwTemp] = '\0'; } lpstrCmd += dwTemp; return(lpstrCmd); } void InitEventVars (DEBUG_EVENT *pDebugEvent) { pProcessEvent = pProcessCurrent = pProcessFromEvent(pDebugEvent); pProcessCurrent->pThreadEvent = pProcessCurrent->pThreadCurrent = pThreadFromEvent(pDebugEvent); } void SetTermStatus (DEBUG_EVENT *pDebugEvent) { PTHREAD_INFO pThread; pThread = pThreadFromEvent(pDebugEvent); pThread->fTerminating = TRUE; } PPROCESS_INFO pProcessFromIndex (ULONG index) { PPROCESS_INFO pProcess; pProcess = pProcessHead; while (pProcess && pProcess->index != index) pProcess = pProcess->pProcessNext; return pProcess; } PTHREAD_INFO pThreadFromIndex (ULONG index) { PTHREAD_INFO pThread; pThread = pProcessCurrent->pThreadHead; while (pThread && pThread->index != index) pThread = pThread->pThreadNext; return pThread; } typedef struct BrkptStruc { BOOLEAN fBrkptUsed; union { #ifdef ADDR_BKPTS ADDR Address; #else PVOID Address; #endif struct BrkptStruc *pNextBrkpt; } x; ULONG Instr; } BRKPT, *PBRKPT; #define BRKPTNUM MAX_NUMBER_OF_BREAKPOINTS BRKPT BrkptArray[BRKPTNUM]; PBRKPT pBrkpt = &BrkptArray[0]; void BrkptInit () { int index; for (index = 0; index < BRKPTNUM - 1; index++) { BrkptArray[index].fBrkptUsed = FALSE; BrkptArray[index].x.pNextBrkpt = &BrkptArray[index + 1]; } BrkptArray[index].fBrkptUsed = FALSE; BrkptArray[index].x.pNextBrkpt = NULL; } #ifdef i386 NTSTATUS DbgKdLookupSelector (IN USHORT Processor, IN OUT PDESCRIPTOR_TABLE_ENTRY pDesc) { UNREFERENCED_PARAMETER(Processor); if (GetThreadSelectorEntry(pProcessCurrent->pThreadCurrent->hThread, pDesc->Selector, &pDesc->Descriptor)) return STATUS_SUCCESS; else return STATUS_UNSUCCESSFUL; } #endif #ifdef ADDR_BKPTS NTSTATUS AddrWriteBreakPoint( IN ADDR BreakPointAddress, OUT PULONG BreakPointHandle ) { PBRKPT pNewBrkpt; ADDR tempAddr = BreakPointAddress; PADDR paddr = &tempAddr; if ( pBrkpt == NULL ) return STATUS_UNSUCCESSFUL; pNewBrkpt = pBrkpt; NotFlat(tempAddr); // Force recomputing of flat address ComputeFlatAddress(paddr,NULL); if (GetMemString(paddr, (PUCHAR)&(pNewBrkpt->Instr), cbBrkptLength) && SetMemString(paddr, (PUCHAR)&trapInstr, cbBrkptLength)) { pBrkpt = pBrkpt->x.pNextBrkpt; pNewBrkpt->fBrkptUsed = TRUE; pNewBrkpt->x.Address = BreakPointAddress; *BreakPointHandle = (ULONG)pNewBrkpt; return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } NTSTATUS AddrRestoreBreakPoint( ULONG BreakPointHandle ) { PBRKPT pOldBrkpt = (PBRKPT)BreakPointHandle; ADDR tempAddr = pOldBrkpt->x.Address; PADDR paddr = &tempAddr; if (pOldBrkpt < &BrkptArray[0] || pOldBrkpt > &BrkptArray[BRKPTNUM - 1] || !pOldBrkpt->fBrkptUsed) return STATUS_UNSUCCESSFUL; NotFlat(tempAddr); // Force recomputing of flat address ComputeFlatAddress(paddr,NULL); if (SetMemString(paddr, (PUCHAR)&(pOldBrkpt->Instr), cbBrkptLength)) { pOldBrkpt->fBrkptUsed = FALSE; pOldBrkpt->x.pNextBrkpt = pBrkpt; pBrkpt = pOldBrkpt; return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } #else NTSTATUS DbgKdWriteBreakPoint (IN PVOID BreakPointAddress, OUT PULONG BreakPointHandle) { PBRKPT pNewBrkpt; #ifdef MULTIMODE ADDR tempAddr = { ADDR_32 | FLAT_COMPUTED, 0, (ULONG)BreakPointAddress, (ULONG)BreakPointAddress }; #else ADDR tempAddr = (ADDR)BreakPointAddress; #endif PADDR paddr = &tempAddr; if (pBrkpt == NULL) return STATUS_UNSUCCESSFUL; pNewBrkpt = pBrkpt; if (GetMemString(paddr, (PUCHAR)&(pNewBrkpt->Instr), cbBrkptLength) && SetMemString(paddr, (PUCHAR)&trapInstr, cbBrkptLength)) { pBrkpt = pBrkpt->x.pNextBrkpt; pNewBrkpt->fBrkptUsed = TRUE; pNewBrkpt->x.Address = BreakPointAddress; *BreakPointHandle = (ULONG)pNewBrkpt; return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } NTSTATUS DbgKdRestoreBreakPoint (ULONG BreakPointHandle) { PBRKPT pOldBrkpt = (PBRKPT)BreakPointHandle; #ifdef MULTIMODE ADDR tempAddr = { ADDR_32 | FLAT_COMPUTED, 0, (ULONG)pOldBrkpt->x.Address, (ULONG)pOldBrkpt->x.Address }; #else ADDR tempAddr = (ADDR)pOldBrkpt->x.Address; #endif PADDR paddr; if (pOldBrkpt < &BrkptArray[0] || pOldBrkpt > &BrkptArray[BRKPTNUM - 1] || !pOldBrkpt->fBrkptUsed) return STATUS_UNSUCCESSFUL; paddr = &tempAddr; if (SetMemString(paddr, (PUCHAR)&(pOldBrkpt->Instr), cbBrkptLength)) { pOldBrkpt->fBrkptUsed = FALSE; pOldBrkpt->x.pNextBrkpt = pBrkpt; pBrkpt = pOldBrkpt; return STATUS_SUCCESS; } return STATUS_UNSUCCESSFUL; } #endif BOOLEAN ReadVirtualMemory(PUCHAR pBufSrc, PUCHAR pBufDest, ULONG count, PULONG pcTotalBytesRead) { *pcTotalBytesRead = 0; return (BOOLEAN)ReadProcessMemory(pProcessCurrent->hProcess, (PULONG)pBufSrc, (PVOID)pBufDest, count, pcTotalBytesRead); } NTSTATUS DbgKdWriteVirtualMemory (PVOID addr, PVOID buffer, ULONG count, PULONG pcBytesWritten) { BOOL fSuccess; ULONG index; if (fVerboseOutput) { dprintf("mem write addr: %08lx ", addr); for (index = 0; index < count; index++) dprintf("%02x ", *((PUCHAR)buffer + index)); } fSuccess = WriteProcessMemory(pProcessCurrent->hProcess, (PULONG)addr, (PVOID)buffer, count, pcBytesWritten); if (fSuccess) { if (fVerboseOutput) dprintf("success\n"); return STATUS_SUCCESS; } else { if (fVerboseOutput) dprintf("FAILED\n"); return STATUS_UNSUCCESSFUL; } } //////////////////////////////////////////// void AddProcess ( DEBUG_EVENT *pDebugEvent ) { PPROCESS_INFO pProcessNew; PPROCESS_INFO pProcess; PPROCESS_INFO pProcessAfter; PTHREAD_INFO pThreadNew; ULONG index = 0; DEBUG_EVENT FakeDllLoadForApplication; pProcessNew = calloc(1,sizeof(PROCESS_INFO)); if (!pProcessNew) { dprintf("%s: memory allocation failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } if (pProcessHead == NULL || pProcessHead->index > index) { pProcessNew->pProcessNext = pProcessHead; pProcessHead = pProcessNew; } else { index++; pProcess = pProcessHead; while ((pProcessAfter = pProcess->pProcessNext) && pProcessAfter->index == index) { index++; pProcess = pProcessAfter; } pProcessNew->pProcessNext = pProcessAfter; pProcess->pProcessNext = pProcessNew; } pProcessNew->index = index; pProcessNew->dwProcessId = pDebugEvent->dwProcessId; pProcessNew->hProcess = pDebugEvent->u.CreateProcessInfo.hProcess; pProcessNew->fStopOnBreakPoint = fStopFirst; pProcessCurrent = pProcessNew; pThreadNew = calloc(1,sizeof(THREAD_INFO)); if (!pThreadNew) { dprintf("%s: memory allocation failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } pThreadNew->index = 0; pThreadNew->pThreadNext = NULL; pThreadNew->dwThreadId = pDebugEvent->dwThreadId; pThreadNew->hThread = pDebugEvent->u.CreateProcessInfo.hThread; pThreadNew->lpStartAddress = pDebugEvent->u.CreateProcessInfo.lpStartAddress; pThreadNew->fFrozen = pThreadNew->fSuspend = pThreadNew->fTerminating = FALSE; pProcessNew->pThreadHead = pThreadNew; sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); sym->MaxNameLength = MAX_SYMNAME_SIZE; symStart->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); symStart->MaxNameLength = MAX_SYMNAME_SIZE; SymInitialize( pProcessCurrent->hProcess, NULL, FALSE ); SymRegisterCallback( pProcessCurrent->hProcess, SymbolCallbackFunction, NULL ); SetSymbolSearchPath(FALSE); FakeDllLoadForApplication.dwProcessId = pDebugEvent->dwProcessId; FakeDllLoadForApplication.u.LoadDll.hFile = pDebugEvent->u.CreateProcessInfo.hFile; FakeDllLoadForApplication.u.LoadDll.lpBaseOfDll = pDebugEvent->u.CreateProcessInfo.lpBaseOfImage; FakeDllLoadForApplication.u.LoadDll.dwDebugInfoFileOffset = pDebugEvent->u.CreateProcessInfo.dwDebugInfoFileOffset; FakeDllLoadForApplication.u.LoadDll.nDebugInfoSize = pDebugEvent->u.CreateProcessInfo.nDebugInfoSize; FakeDllLoadForApplication.u.LoadDll.lpImageName = pDebugEvent->u.CreateProcessInfo.lpImageName; FakeDllLoadForApplication.u.LoadDll.fUnicode = pDebugEvent->u.CreateProcessInfo.fUnicode; AddImage( &FakeDllLoadForApplication ); } void DelProcess (DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; PPROCESS_INFO pProcessLast; PIMAGE_INFO pImage; DWORD dwThreadId; pProcessLast = NULL; pProcess = pProcessHead; while (pProcess && pProcess->dwProcessId != pDebugEvent->dwProcessId) { pProcessLast = pProcess; pProcess = pProcess->pProcessNext; } assert(pProcess); dwThreadId = pDebugEvent->dwThreadId; while (pProcess->pThreadHead) { pDebugEvent->dwThreadId = pProcess->pThreadHead->dwThreadId; DelThread(pDebugEvent); } pDebugEvent->dwThreadId = dwThreadId; pProcessCurrent = pProcess; SymCleanup( pProcessCurrent->hProcess ); while (pImage = pProcess->pImageHead) { pProcess->pImageHead = pImage->pImageNext; free(pImage); } if (pProcessLast) { pProcessLast->pProcessNext = pProcess->pProcessNext; } else { pProcessHead = pProcess->pProcessNext; } RemoveProcessBps(pProcess); free(pProcess); } void PrintDebugString(DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; LPSTR Str; BOOL b; DWORD dwNumberOfBytesRead; DWORD dwInputSig; pProcess = pProcessFromEvent(pDebugEvent); Str = calloc(1,pDebugEvent->u.DebugString.nDebugStringLength); if (!Str) { dprintf("%s: memory allocation failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } if (pDebugEvent->u.DebugString.nDebugStringLength == 0) { free(Str); return; } b = ReadProcessMemory( pProcess->hProcess, pDebugEvent->u.DebugString.lpDebugStringData, Str, pDebugEvent->u.DebugString.nDebugStringLength, &dwNumberOfBytesRead ); if ( !b ) { free(Str); return; } if ( dwNumberOfBytesRead != (DWORD)pDebugEvent->u.DebugString.nDebugStringLength ) { free(Str); return; } dprintf("%s",Str); { // // Special processing for hacky debug input string // #define INPUT_API_SIG 0xdefaced struct _hdi { DWORD dwSignature; BYTE cLength; BYTE cStatus; }; typedef struct _hdi HDI; HDI hdi; b = ReadProcessMemory( pProcess->hProcess, pDebugEvent->u.DebugString.lpDebugStringData+pDebugEvent->u.DebugString.nDebugStringLength, &hdi, sizeof(hdi), &dwNumberOfBytesRead ); if ( b ) { if ( hdi.dwSignature == INPUT_API_SIG ) { free(Str); Str = calloc(1,hdi.cLength+1); if ( !Str ) { dprintf("%s: memory allocation failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } NtsdPrompt("", Str, hdi.cLength); b = WriteProcessMemory( pProcess->hProcess, pDebugEvent->u.DebugString.lpDebugStringData+6, Str, (DWORD)hdi.cLength, &dwNumberOfBytesRead ); } } } free(Str); return; } void PrintRip(DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; UCHAR pszErrorString[_MAX_PATH]; va_list arglist; pProcess = pProcessFromEvent(pDebugEvent); dprintf("%s - %s: ", pDebugEvent->u.RipInfo.dwType == SLE_WARNING ? "WARNING" : "ERROR", pProcess->pImageHead->szImagePath); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM| FORMAT_MESSAGE_IGNORE_INSERTS, NULL, pDebugEvent->u.RipInfo.dwError, 0, pszErrorString, sizeof(pszErrorString), &arglist); dprintf(pszErrorString); } ULONG Wx86Disasm( ULONG Offset, PUCHAR pBuffer, BOOLEAN fShowEA, LPVDMCONTEXT ContextX86 ) { ADDR Addr; ADDR32(&Addr, Offset); if (ContextX86) { VDMRegisterContext = *ContextX86; } if (X86disasm(&Addr, pBuffer, fShowEA)) { return Addr.flat; } else { return 0; } } LONG Wx86ExtApiExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo, PWX86_EXTENSION_ROUTINE pWx86Ext ) { dprintf("%s: %08x Exception in Wx86ExtApi %x\n", DebuggerName, ExceptionInfo->ExceptionRecord->ExceptionCode, pWx86Ext ); dprintf(" PC: %08x VA: %08x R/W: %x Parameter: %x\n", ExceptionInfo->ExceptionRecord->ExceptionAddress, ExceptionInfo->ExceptionRecord->ExceptionInformation[1], ExceptionInfo->ExceptionRecord->ExceptionInformation[0], ExceptionInfo->ExceptionRecord->ExceptionInformation[2] ); return EXCEPTION_EXECUTE_HANDLER; } DWORD DoWx86ExtApi( PWX86_EXTENSION_ROUTINE pWx86Ext, DEBUG_EVENT *pDebugEvent, DWORD *pContinueStatus ) { DWORD dwRet; NTSD_EXTENSION_APIS NtsdExtApis; EXCEPTION_RECORD ExRecord; NtsdExtApis.nSize = sizeof(NtsdExtApis); NtsdExtApis.lpOutputRoutine = dprintf; NtsdExtApis.lpGetExpressionRoutine = GetExpressionRoutine; NtsdExtApis.lpGetSymbolRoutine = GetSymbolRoutine; NtsdExtApis.lpDisasmRoutine = disasmExportRoutine; NtsdExtApis.lpCheckControlCRoutine = CheckControlC; __try { dwRet = (pWx86Ext)( pProcessCurrent->hProcess, pProcessCurrent->pThreadCurrent->hThread, &NtsdExtApis, pDebugEvent, pContinueStatus ); } __except (Wx86ExtApiExceptionFilter(GetExceptionInformation(),pWx86Ext)) { dwRet = WX86_CONTINUE; } return dwRet; } DWORD Wx86CreateProcessEvent( DEBUG_EVENT *pDebugEvent ) { HANDLE hWx86ExtDll; PVOID pvCreateProcess; PVOID pvException; HANDLE Wx86Info; NTSTATUS Status; NTSDROUTINES NtsdRoutines; DWORD dwRet = WX86_CONTINUE; // // if New process is a Wx86 process, load in the wx86 extensions // dll. This will stay loaded until ntsd exits. // #ifdef CHICAGO Status = STATUS_UNSUCCESSFUL; #else Status = NtQueryInformationProcess(pDebugEvent->u.CreateProcessInfo.hProcess, ProcessWx86Information, &Wx86Info, sizeof(Wx86Info), NULL ); #endif if (!NT_SUCCESS(Status)) { Wx86Info = NULL; } if ((ULONG)Wx86Info == sizeof(WX86TIB)) { if (!hWx86ExtensionsDll) { hWx86ExtDll = LoadLibrary("wx86e.dll"); if (hWx86ExtDll) { pvCreateProcess = GetProcAddress(hWx86ExtDll, "Wx86ExtCreateProcess" ); pvException = GetProcAddress(hWx86ExtDll, "Wx86ExtException" ); if (!pvException || !pvCreateProcess) { FreeLibrary(hWx86ExtDll); hWx86ExtDll = NULL; } } } else { hWx86ExtDll = hWx86ExtensionsDll; pvCreateProcess = pfnWx86ExtCreateProcess; pvException = pfnWx86ExtException; } if (hWx86ExtDll) { InitEventVars(pDebugEvent); NtsdRoutines.FindFpoDataForModule = FindFpoDataForModule; NtsdRoutines.SwReadMemory = SwReadMemory; NtsdRoutines.SwGetModuleBase = SwGetModuleBase; NtsdRoutines.Wx86Disasm = Wx86Disasm; dwRet = DoWx86ExtApi(pvCreateProcess, pDebugEvent, (PULONG)&NtsdRoutines ); if (dwRet != WX86_CONTINUE) { pProcessCurrent->fWx86Process = TRUE; if (!hWx86ExtensionsDll) { hWx86ExtensionsDll = hWx86ExtDll; pfnWx86ExtCreateProcess = pvCreateProcess; pfnWx86ExtException = pvException; } return dwRet; } } if (hWx86ExtDll && !hWx86ExtensionsDll) { FreeLibrary(hWx86ExtDll); } } return dwRet; } DWORD Wx86ExceptionEvent( DEBUG_EVENT *pDebugEvent, DWORD *pContinueStatus ) { DWORD dwRet = WX86_CONTINUE; if (pDebugEvent->u.Exception.dwFirstChance) { DWORD ExCode = pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode; if (ExCode != DBG_CONTROL_C && ExCode != DBG_CONTROL_BREAK && ExCode != STATUS_PORT_DISCONNECTED) { SuspendAllThreads(); dwRet = DoWx86ExtApi(pfnWx86ExtException, pDebugEvent, pContinueStatus ); if (dwRet != WX86_PROMPT) { ResumeAllThreads(); } } } return dwRet; } BOOL VDMEvent(DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; LPSTR Str; BOOL b; DWORD lpNumberOfBytesRead; DWORD address; LPDWORD lpdw; int segslot; int mode; BOOL fData; BOOL fBPRelease; WORD selector; WORD segment; WORD newselect; BOOL fStop; DWORD ImgLen; BOOL fResult; BOOL fNeedSegTableEdit; BOOL fNeedInteractive; CHAR achInput[_MAX_PATH]; PUCHAR pTemp; PTHREAD_INFO pThread; BOOL fProcess; SEGMENT_NOTE se; IMAGE_NOTE im; pProcess = pProcessFromEvent(pDebugEvent); pThread = pThreadFromEvent(pDebugEvent); lpdw = &(pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); if ( !fVDMInitDone ) { HANDLE hmodVDM; hmodVDM = LoadLibrary("VDMDBG.DLL"); if ( hmodVDM != (HANDLE)NULL ) { fVDMActive = TRUE; pfnVDMProcessException = (BOOL (WINAPI *)(LPDEBUG_EVENT)) GetProcAddress( hmodVDM, "VDMProcessException" ); pfnVDMGetPointer = (ULONG (WINAPI *)(HANDLE,HANDLE,WORD,DWORD,BOOL)) GetProcAddress( hmodVDM, "VDMGetPointer" ); pfnVDMGetThreadSelectorEntry = (BOOL (WINAPI *)(HANDLE,HANDLE,DWORD,LPVDMLDT_ENTRY)) GetProcAddress( hmodVDM, "VDMGetThreadSelectorEntry" ); pfnVDMGetThreadContext = (BOOL (WINAPI *)(LPDEBUG_EVENT,LPVDMCONTEXT)) GetProcAddress( hmodVDM, "VDMGetThreadContext" ); pfnVDMSetThreadContext = (BOOL (WINAPI *)(LPDEBUG_EVENT,LPVDMCONTEXT)) GetProcAddress( hmodVDM, "VDMSetThreadContext" ); pfnVDMKillWOW = (BOOL (WINAPI *)(VOID)) GetProcAddress( hmodVDM, "VDMKillWOW" ); pfnVDMDetectWOW = (BOOL (WINAPI *)(VOID)) GetProcAddress( hmodVDM, "VDMDetectWOW" ); pfnVDMBreakThread = (BOOL (WINAPI *)(HANDLE)) GetProcAddress( hmodVDM, "VDMBreakThread" ); pfnVDMGetSelectorModule = (BOOL (WINAPI *)(HANDLE,HANDLE,WORD,PUINT,LPSTR,UINT,LPSTR,UINT)) GetProcAddress( hmodVDM, "VDMGetSelectorModule" ); pfnVDMGetModuleSelector = (BOOL (WINAPI *)(HANDLE,HANDLE,UINT,LPSTR,LPWORD)) GetProcAddress( hmodVDM, "VDMGetModuleSelector" ); pfnVDMModuleFirst = (BOOL (WINAPI *)(HANDLE,HANDLE,LPMODULEENTRY,DEBUGEVENTPROC,LPVOID)) GetProcAddress( hmodVDM, "VDMModuleFirst" ); pfnVDMModuleNext = (BOOL (WINAPI *)(HANDLE,HANDLE,LPMODULEENTRY,DEBUGEVENTPROC,LPVOID)) GetProcAddress( hmodVDM, "VDMModuleNext" ); pfnVDMGlobalFirst = (BOOL (WINAPI *)(HANDLE,HANDLE,LPGLOBALENTRY,WORD,DEBUGEVENTPROC,LPVOID)) GetProcAddress( hmodVDM, "VDMGlobalFirst" ); pfnVDMGlobalNext = (BOOL (WINAPI *)(HANDLE,HANDLE,LPGLOBALENTRY,WORD,DEBUGEVENTPROC,LPVOID)) GetProcAddress( hmodVDM, "VDMGlobalNext" ); } else { dprintf("LoadLibrary(VDMDBG.DLL) failed\n"); } fVDMInitDone = TRUE; } if ( !fVDMActive ) { return( TRUE ); } else { fProcess = (*pfnVDMProcessException)(pDebugEvent); } fResult = TRUE; fNeedSegTableEdit = FALSE; fNeedInteractive = FALSE; mode = LOWORD(lpdw[0]); switch( mode ) { case DBG_SEGLOAD: case DBG_SEGMOVE: case DBG_SEGFREE: case DBG_MODLOAD: case DBG_MODFREE: address = lpdw[2]; b = ReadProcessMemory( pProcess->hProcess, (LPVOID)address, &se, sizeof(se), &lpNumberOfBytesRead ); if ( !b || lpNumberOfBytesRead != sizeof(se) ) { return( fResult ); } break; case DBG_DLLSTART: case DBG_DLLSTOP: case DBG_TASKSTART: case DBG_TASKSTOP: address = lpdw[2]; b = ReadProcessMemory( pProcess->hProcess, (LPVOID)address, &im, sizeof(im), &lpNumberOfBytesRead ); if ( !b || lpNumberOfBytesRead != sizeof(im) ) { return( fResult ); } break; } switch( mode ) { default: fResult = FALSE; break; case DBG_SEGLOAD: fNeedSegTableEdit = TRUE; selector = se.Selector1; segment = se.Segment; fData = (BOOL)se.Type; segslot = 0; while ( segslot < MAXSEGENTRY ) { if ( segtable[segslot].type != SEGTYPE_AVAILABLE ) { if ( _stricmp(segtable[segslot].path_name, se.FileName) == 0 ) { break; } } segslot++; } if ( segslot == MAXSEGENTRY ) { if ( strlen(se.FileName) != 0 ) { dprintf("Loading [%s]\n", se.FileName ); } } break; case DBG_SEGMOVE: fNeedSegTableEdit = TRUE; selector = se.Selector1; newselect = se.Selector2; if ( newselect == 0 ) { mode = DBG_SEGFREE; fBPRelease = TRUE; } break; case DBG_SEGFREE: fNeedSegTableEdit = TRUE; fBPRelease = (BOOL)se.Type; selector = se.Selector1; break; case DBG_MODFREE: fNeedSegTableEdit = TRUE; if ( strlen(se.FileName) != 0 ) { dprintf("Freeing [%s]\n", se.FileName ); } break; case DBG_MODLOAD: fNeedSegTableEdit = TRUE; selector = se.Selector1; ImgLen = se.Length; segslot = 0; while ( segslot < MAXSEGENTRY ) { if ( segtable[segslot].type != SEGTYPE_AVAILABLE ) { if ( _stricmp(segtable[segslot].path_name, se.FileName) == 0 ) { break; } } segslot++; } if ( segslot == MAXSEGENTRY ) { if ( strlen(se.FileName) != 0 ) { dprintf("Loading [%s]\n", se.FileName ); } } break; case DBG_SINGLESTEP: fNeedInteractive = TRUE; break; case DBG_BREAK: fNeedInteractive = TRUE; break; case DBG_GPFAULT: dprintf("%s: access violation in VDM\n", DebuggerName); fNeedInteractive = TRUE; break; case DBG_INSTRFAULT: dprintf("%s: invalid opcode fault in VDM\n", DebuggerName); fNeedInteractive = TRUE; break; case DBG_DIVOVERFLOW: dprintf("%s: divide overflow in VDM\n", DebuggerName); fNeedInteractive = TRUE; break; case DBG_TASKSTART: if ( fWOWStopFirst ) { dprintf("%: VDM Start Task <%s:%s>\n", DebuggerName, im.Module, im.FileName ); fNeedInteractive = TRUE; } break; case DBG_DLLSTART: if ( fLoadDllBreak ) { dprintf("%s: VDM Start Dll <%s:%s>\n", DebuggerName, im.Module, im.FileName ); fNeedInteractive = TRUE; } break; case DBG_TASKSTOP: fNeedInteractive = FALSE; break; case DBG_DLLSTOP: fNeedInteractive = FALSE; break; } /* ** Temporary code to emulate a 16-bit debugger. Eventually I will make ** NTSD understand these events and call ProcessStateChange to allow ** real 16-bit debugging and other activities on the other 32-bit threads. ** -BobDay */ if ( fNeedInteractive ) { char text[128]; char path[128]; DWORD cSeg; ADDR addr; VDMCONTEXT vc; VDMRegisterContext.ContextFlags = VDMCONTEXT_FULL; (*pfnVDMGetThreadContext)(pDebugEvent,&VDMRegisterContext); VDMRegisterContext.EFlags &= ~V86FLAGS_TRACE; vc = VDMRegisterContext; // Dump a simulated context X86OutputAllRegs(); b = (*pfnVDMGetSelectorModule)(pProcess->hProcess,pThread->hThread, (WORD)VDMRegisterContext.SegCs, &cSeg, text, 128, path, 128 ); if ( b ) { dprintf("%s:%d!%04x:\n", text, cSeg, (WORD)VDMRegisterContext.Eip ); } addr.seg = (WORD)VDMRegisterContext.SegCs; addr.off = VDMRegisterContext.Eip; if ( VDMRegisterContext.EFlags & V86FLAGS_V86 ) { addr.type = ADDR_V86 | FLAT_COMPUTED; addr.flat = (*pfnVDMGetPointer)(pDebugEvent,pThread->hThread, addr.seg, addr.off, FALSE ); } else { addr.type = ADDR_16 | FLAT_COMPUTED; addr.flat = (*pfnVDMGetPointer)(pProcess->hProcess,pThread->hThread, addr.seg, addr.off, TRUE ); } if ( Flat(addr) == 0 ) { dprintf("Unable to disassemble failing code\n"); } else { X86disasm( &addr, text, TRUE ); dprintf("%s", text ); } while ( TRUE ) { NtsdPrompt("VDM>", achInput, sizeof(achInput)); if ( _stricmp(achInput,"gh") == 0 || _stricmp(achInput,"g") == 0 ) { fResult = TRUE; break; } if ( _stricmp(achInput,"gn") == 0 ) { fResult = FALSE; break; } if ( _stricmp(achInput, "t") == 0 ) { fResult = TRUE; vc.EFlags |= V86FLAGS_TRACE; break; } if (achInput[0] == '!') { fnBangCmd(&achInput[1], &pTemp); continue; } if ( _stricmp(achInput,"?") == 0 ) { dprintf("g = Go\n"); dprintf("gh = Go - Exception handled\n"); dprintf("gn = Go - Exception not handled\n"); dprintf("t = Trace 1 instruction\n"); dprintf("! = execute debugger extension\n"); continue; } dprintf("%s:Illegal command\n", DebuggerName); } VDMRegisterContext = vc; (*pfnVDMSetThreadContext)(pDebugEvent,&VDMRegisterContext); } /* ** End of temporary code */ if ( fNeedSegTableEdit ) { segslot = 0; fStop = FALSE; while ( segslot < MAXSEGENTRY ) { switch( mode ) { case DBG_SEGLOAD: if ( segtable[segslot].type == SEGTYPE_AVAILABLE ) { segtable[segslot].segment = segment; segtable[segslot].selector = selector; // This notification message is used only by wow in prot // It could be determined from the current mode to be // correct segtable[segslot].type = SEGTYPE_PROT; Str = calloc(1,strlen(se.FileName)+1); if ( !Str ) { return( fResult ); } strcpy( Str, se.FileName ); segtable[segslot].path_name = Str; segtable[segslot].ImgLen = 0; fStop = TRUE; } break; case DBG_SEGMOVE: if (( segtable[segslot].type != SEGTYPE_AVAILABLE ) && ( segtable[segslot].selector == selector )) { segtable[segslot].selector = newselect; fStop = TRUE; } break; case DBG_SEGFREE: if ( segtable[segslot].selector == selector ) { fStop = TRUE; segtable[segslot].type = SEGTYPE_AVAILABLE; free(segtable[segslot].path_name); segtable[segslot].path_name = NULL; } break; case DBG_MODFREE: if ( segtable[segslot].type != SEGTYPE_AVAILABLE ) { if ( _stricmp(segtable[segslot].path_name,se.FileName) == 0 ) { segtable[segslot].type = SEGTYPE_AVAILABLE; free(segtable[segslot].path_name); segtable[segslot].path_name = NULL; } } break; case DBG_MODLOAD: if ( segtable[segslot].type == SEGTYPE_AVAILABLE ) { segtable[segslot].segment = 0; segtable[segslot].selector = selector; // This notification message is used only by v86 dos // It could be determined from the current mode to be // correct segtable[segslot].type = SEGTYPE_V86; Str = calloc(1,strlen(se.FileName)+1); if ( !Str ) { return( fResult ); } strcpy( Str, se.FileName ); segtable[segslot].path_name = Str; segtable[segslot].ImgLen = ImgLen; fStop = TRUE; } break; } if ( fStop ) { break; } segslot++; } if ( segslot == MAXSEGENTRY ) { if ( mode == DBG_SEGLOAD ) { dprintf("%s: Warning - adding selector %04X for segment %d, segtable full\n", DebuggerName, selector, segment ); } } } return( fResult ); } void AddThread (DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; PTHREAD_INFO pThreadCurrent; PTHREAD_INFO pThreadAfter; PTHREAD_INFO pThreadNew; ULONG index = 0; pProcess = pProcessFromEvent(pDebugEvent); pThreadCurrent = pProcess->pThreadHead; assert(pThreadCurrent); pThreadNew = calloc(1,sizeof(THREAD_INFO)); if (!pThreadNew) { dprintf("%s: memory allocation failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } if (pThreadCurrent->index > index) { pThreadNew->pThreadNext = pThreadCurrent; pProcess->pThreadHead = pThreadNew; } else { index++; while ((pThreadAfter = pThreadCurrent->pThreadNext) && pThreadAfter->index == index) { index++; pThreadCurrent = pThreadAfter; } pThreadNew->pThreadNext = pThreadAfter; pThreadCurrent->pThreadNext = pThreadNew; } pThreadNew->index = index; pThreadNew->dwThreadId = pDebugEvent->dwThreadId; pThreadNew->hThread = pDebugEvent->u.CreateThread.hThread; pThreadNew->lpStartAddress = pDebugEvent->u.CreateThread.lpStartAddress; pThreadNew->fFrozen = pThreadNew->fSuspend = pThreadNew->fTerminating = FALSE; } void DelThread (DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; PTHREAD_INFO pThread; PTHREAD_INFO pThreadLast; pProcess = pProcessFromEvent(pDebugEvent); assert(pProcess); if (!pProcess) { return; } pThreadLast = NULL; pThread = pProcess->pThreadHead; while (pThread && pThread->dwThreadId != pDebugEvent->dwThreadId) { pThreadLast = pThread; pThread = pThread->pThreadNext; } assert(pThread); if (!pThread) { return; } if (pProcess->pThreadCurrent == pThread) { pProcess->pThreadCurrent = NULL; } if (pThreadLast) { pThreadLast->pThreadNext = pThread->pThreadNext; } else { pProcess->pThreadHead = pThread->pThreadNext; } RemoveThreadBps(pThread); free(pThread); } void AddImage (DEBUG_EVENT *pDebugEvent) { PIMAGE_INFO pImageNew; PIMAGE_INFO *pp; UCHAR index; WCHAR NameBuffer[ MAX_PATH ]; CHAR ImageName[ MAX_PATH ]; DWORD cbRead; BOOL fOk; IMAGEHLP_MODULE mi; PIMAGEHLP_SYMBOL sym; pProcessCurrent = pProcessFromEvent(pDebugEvent); if (pDebugEvent->u.LoadDll.lpImageName) { if (!ReadProcessMemory(pProcessCurrent->hProcess, pDebugEvent->u.LoadDll.lpImageName, &pDebugEvent->u.LoadDll.lpImageName, sizeof(pDebugEvent->u.LoadDll.lpImageName), &cbRead ) ) { pDebugEvent->u.LoadDll.lpImageName = NULL; } } if (pDebugEvent->u.LoadDll.lpImageName) { fOk = ReadProcessMemory(pProcessCurrent->hProcess, pDebugEvent->u.LoadDll.lpImageName, NameBuffer, sizeof(NameBuffer), &cbRead ); if ( !fOk ) { pDebugEvent->u.LoadDll.lpImageName = NULL; } } if (pDebugEvent->u.LoadDll.lpImageName) { // // we have a name // if (pDebugEvent->u.LoadDll.fUnicode) { if (!WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK, NameBuffer, -1, ImageName, sizeof(ImageName), NULL, NULL )) { // // unicocde -> ansi conversion failed // ImageName[0] = 0; } } else { strcpy(ImageName,(PCHAR)NameBuffer); } } else { // // we don't have a name // GetModnameFromImage( (ULONG)pDebugEvent->u.LoadDll.lpBaseOfDll, pDebugEvent->u.LoadDll.hFile, ImageName ); } if (!ImageName[0]) { // // invent an image name as a last resort // sprintf( ImageName, "Image@%08x", pDebugEvent->u.LoadDll.lpBaseOfDll ); } // search for existing image at same base address // if found, remove symbols, but leave image structure intact pp = &pProcessCurrent->pImageHead; while (pImageNew = *pp) { if (pImageNew->lpBaseOfImage == pDebugEvent->u.LoadDll.lpBaseOfDll) { if (SymGetModuleInfo( pProcessCurrent->hProcess, (ULONG)pImageNew->lpBaseOfImage, &mi )) { if (mi.SymType != SymDeferred && mi.SymType != SymNone) { SymUnloadModule( pProcessCurrent->hProcess, (ULONG)pImageNew->lpBaseOfImage ); if (fVerboseOutput) { dprintf("%s: force unload of %s\n", DebuggerName, pImageNew->szImagePath); } } } break; } else if (pImageNew->lpBaseOfImage > pDebugEvent->u.LoadDll.lpBaseOfDll) { pImageNew = NULL; break; } pp = &pImageNew->pImageNext; } // if not found, allocate and fill new image structure if (!pImageNew) { for (index=0; indexMaxIndex; index++) { if (pProcessCurrent->pImageByIndex[ index ] == NULL) { pImageNew = calloc(sizeof(IMAGE_INFO),1); break; } } if (!pImageNew) { DWORD NewMaxIndex; PIMAGE_INFO *NewImageByIndex; NewMaxIndex = pProcessCurrent->MaxIndex + 32; if (NewMaxIndex < 0x100) { NewImageByIndex = calloc( NewMaxIndex, sizeof( *NewImageByIndex ) ); } else { NewImageByIndex = NULL; } if (NewImageByIndex == NULL) { dprintf("%s: No room for %ws image record.\n", DebuggerName, ImageName ); return; } if (pProcessCurrent->pImageByIndex) { memcpy( NewImageByIndex, pProcessCurrent->pImageByIndex, pProcessCurrent->MaxIndex * sizeof( *NewImageByIndex ) ); free( pProcessCurrent->pImageByIndex ); } pProcessCurrent->pImageByIndex = NewImageByIndex; index = (UCHAR)pProcessCurrent->MaxIndex; pProcessCurrent->MaxIndex = NewMaxIndex; pImageNew = calloc(sizeof(IMAGE_INFO),1); if (pImageNew == NULL) { dprintf("%s: Unable to allocate memory for %ws image record.\n", DebuggerName, ImageName ); return; } } pImageNew->pImageNext = *pp; *pp = pImageNew; pImageNew->index = index; pProcessCurrent->pImageByIndex[ index ] = pImageNew; } // pImageNew has either the unloaded structure or the newly created one pImageNew->hFile = pDebugEvent->u.LoadDll.hFile; pImageNew->lpBaseOfImage = pDebugEvent->u.LoadDll.lpBaseOfDll; pImageNew->GoodCheckSum = TRUE; GetHeaderInfo( (ULONG)pDebugEvent->u.LoadDll.lpBaseOfDll, &pImageNew->dwCheckSum, &pImageNew->DateTimeStamp, &pImageNew->dwSizeOfImage ); if (pDebugEvent->u.LoadDll.hFile) { CreateModuleNameFromPath( ImageName, pImageNew->szModuleName ); SymLoadModule( pProcessCurrent->hProcess, pDebugEvent->u.LoadDll.hFile, ImageName, pImageNew->szModuleName, (ULONG)pImageNew->lpBaseOfImage, pImageNew->dwSizeOfImage ); } else { SymLoadModule( pProcessCurrent->hProcess, NULL, ImageName, NULL, (ULONG)pImageNew->lpBaseOfImage, pImageNew->dwSizeOfImage ); } if (SymGetModuleInfo( pProcessCurrent->hProcess, (ULONG)pImageNew->lpBaseOfImage, &mi )) { pImageNew->dwSizeOfImage = mi.ImageSize; strcpy( pImageNew->szImagePath, mi.ImageName ); if (!pImageNew->szImagePath[0]) { strcpy( pImageNew->szImagePath, ImageName ); } strcpy( pImageNew->szDebugPath, mi.LoadedImageName ); } CreateModuleNameFromPath( ImageName, pImageNew->szModuleName ); dprintf( "%s ModLoad: %08lx %08lx %-8s\n", DebuggerName, pImageNew->lpBaseOfImage, (ULONG)(pImageNew->lpBaseOfImage) + pImageNew->dwSizeOfImage, pImageNew->szImagePath ); } PIMAGE_INFO pImageFromIndex (UCHAR index) { if (index < pProcessCurrent->MaxIndex) { return pProcessCurrent->pImageByIndex[ index ]; } else { return NULL; } } void DelImage (DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; PIMAGE_INFO pImage, *pp; pProcess = pProcessFromEvent(pDebugEvent); assert(pProcess); pp = &pProcess->pImageHead; while (pImage = *pp) { if (pImage->lpBaseOfImage == pDebugEvent->u.UnloadDll.lpBaseOfDll) { *pp = pImage->pImageNext; pProcessCurrent = pProcess; SymUnloadModule( pProcessCurrent->hProcess, (ULONG)pImage->lpBaseOfImage ); pProcessCurrent->pImageByIndex[ pImage->index ] = NULL; if (pImage->hFile) { CloseHandle( pImage->hFile ); } free(pImage); } else { pp = &pImage->pImageNext; } } return; } PPROCESS_INFO pProcessFromEvent (DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; pProcess = pProcessHead; while (pProcess && pProcess->dwProcessId != pDebugEvent->dwProcessId) pProcess = pProcess->pProcessNext; return pProcess; } PTHREAD_INFO pThreadFromEvent (DEBUG_EVENT *pDebugEvent) { PPROCESS_INFO pProcess; PTHREAD_INFO pThread; pProcess = pProcessFromEvent(pDebugEvent); assert(pProcess); pThread = pProcess->pThreadHead; while (pThread && pThread->dwThreadId != pDebugEvent->dwThreadId) pThread = pThread->pThreadNext; return pThread; } void OutputProcessInfo (char *s) { PPROCESS_INFO pProcess; PTHREAD_INFO pThread; PIMAGE_INFO pImage; if (!fVerboseOutput) { return; } dprintf("OUTPUT_PROCESS: %s\n",s); pProcess = pProcessHead; while (pProcess) { dprintf("id: %x hProcess: %lx index: %d\n", pProcess->dwProcessId, pProcess->hProcess, pProcess->index); pThread = pProcess->pThreadHead; while (pThread) { dprintf(" id: %x hThread: %lx index: %d addr: %08lx\n", pThread->dwThreadId, pThread->hThread, pThread->index, pThread->lpStartAddress); pThread = pThread->pThreadNext; } pImage = pProcess->pImageHead; while (pImage) { dprintf(" hFile: %08lx index: %d base: %08lx\n", (ULONG)pImage->hFile, pImage->index, (ULONG)pImage->lpBaseOfImage); pImage = pImage->pImageNext; } pProcess = pProcess->pProcessNext; } } /* if ((hFile = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) == (HANDLE)-1) return; if (!(hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0L, 0L,NULL))){ CloseHandle(hFile); return; } if (!(lpData = (PCOFF_FILE_HEADER)MapViewOfFile(hMapping, FILE_MAP_READ, 0L, 0L, 0L))){ CloseHandle(hMapping); CloseHandle(hFile); return; } strSearch(lpData, "[ntsd]"); */ static char *(tokens[]) = { "debugchildren", "debugoutput", "stopfirst", "verboseoutput", "lazyload", "true", "false", "$u0", "$u1", "$u2", "$u3", "$u4", "$u5", "$u6", "$u7", "$u8", "$u9", "stoponprocessexit", "sxd", "sxe", "inifile", "setdll" }; void ReadIniFile(PULONG debugType) { FILE* file; char pszName[64]; char rchBuf[_MAX_PATH]; PUCHAR pszMark = INI_MARK; PUCHAR pchCur; DWORD length; int index = 0; int token, value; char chT, ch = *pszMark; if (!(length = GetEnvironmentVariable(INI_DIR, pszName, 64-(sizeof(INI_FILE))))) return; strcpy(pszName+length, INI_FILE); if (!(file = fopen(pszName, "r"))) return; // Look for our mark in the ini file. while (ch && !feof(file)) { chT = fgetc(file); if (ch == (char)tolower(chT)) ch = pszMark[++index]; else ch = pszMark[index = 0]; } if (ch) { fclose(file); return; } // Now just read the lines in do { PUCHAR psz = rchBuf; if (!fgets(rchBuf, sizeof(rchBuf), file)) break; for(index = 0; rchBuf[index] && rchBuf[index] > 26; index++); rchBuf[index] = 0; token = GetToken(&psz, rchBuf + sizeof(rchBuf)); if (token >= NTINI_USERREG0 && token <= NTINI_USERREG9) { while ((*psz == ' ' || *psz == '\t' || *psz == ':') && *psz) psz++; if (*psz) { ULONG index = GetRegString(tokens[token - 1]); SetRegFlagValue(index, (LONG)psz); } continue; } switch(token) { case NTINI_SXD: case NTINI_SXE: pchCur = pchCommand; *(psz-1) = ' '; pchCommand = psz-2; fnSetException(); pchCommand = pchCur; continue; case NTINI_INIFILE: pszScriptFile = calloc(1,strlen(psz)+1); if (!pszScriptFile) { dprintf("%s: memory allocation failed\n", DebuggerName); ExitProcess((UINT)STATUS_UNSUCCESSFUL); } strcpy(pszScriptFile, psz); continue; case NTINI_DEFAULTEXT: SetDefaultExtDllName(psz); continue; } value = GetToken(&psz, rchBuf + sizeof(rchBuf)) != NTINI_FALSE; switch(token) { case NTINI_STOPONPROCESSEXIT: fStopOnProcessExit = (BOOLEAN)value; break; case NTINI_DEBUGCHILDREN: if (value) *debugType=DEBUG_PROCESS; break; case NTINI_DEBUGOUTPUT: fDebugOutput = (BOOLEAN)value; break; case NTINI_STOPFIRST: fStopFirst = (BOOLEAN)value; break; case NTINI_VERBOSEOUTPUT: fVerboseOutput = (BOOLEAN)value; break; case NTINI_LAZYLOAD: fLazyLoad = (BOOLEAN)value; break; } } while(token!=NTINI_END); fclose(file); } static int GetToken(PUCHAR* ppsz, PUCHAR limit) { PUCHAR psz = *ppsz; int token; while((*psz==' ' || *psz=='\t' || *psz==':') && *psz && psz < limit) psz++; if (psz>=limit) return 0; *ppsz = psz; while(*psz!=' ' && *psz!='\t' && *psz!=':' && *psz!='\n' && *psz!='\r'&& *psz && psz < limit){ *psz = (UCHAR)tolower(*psz); psz++; } *psz = 0; if (**ppsz=='[') return NTINI_END; for(token=1;tokenPrivilegeCount = 1; NewPrivileges->Privileges[0].Luid = LuidPrivilege; NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // // Enable the privilege // pbOldPriv = OldPriv; fRc = AdjustTokenPrivileges( Token, FALSE, NewPrivileges, 1024, (PTOKEN_PRIVILEGES)pbOldPriv, &cbNeeded ); if (!fRc) { // // If the stack was too small to hold the privileges // then allocate off the heap // if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { pbOldPriv = calloc(1,cbNeeded); if (pbOldPriv == NULL) { CloseHandle(Token); return(FALSE); } fRc = AdjustTokenPrivileges( Token, FALSE, NewPrivileges, cbNeeded, (PTOKEN_PRIVILEGES)pbOldPriv, &cbNeeded ); } } b = DebugActiveProcess(dwPidToDebug); CloseHandle( Token ); return( b ); #endif }