//---------------------------------------------------------------------------- // // VDM debugging support. // // Copyright (C) Microsoft Corporation, 1997-2002. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" BOOL fVDMInitDone; BOOL fVDMActive; VDMPROCESSEXCEPTIONPROC pfnVDMProcessException; VDMGETTHREADSELECTORENTRYPROC pfnVDMGetThreadSelectorEntry; VDMGETPOINTERPROC pfnVDMGetPointer; VDMGETCONTEXTPROC pfnVDMGetContext; VDMSETCONTEXTPROC pfnVDMSetContext; VDMGETSELECTORMODULEPROC pfnVDMGetSelectorModule; #define SEGTYPE_AVAILABLE 0 #define SEGTYPE_V86 1 #define SEGTYPE_PROT 2 #define MAXSEGENTRY 1024 SEGENTRY segtable[MAXSEGENTRY]; VOID VDMRegCmd( LPSTR achInput, X86_NT5_CONTEXT *pvc ) { DWORD dwVal; if ( _stricmp(achInput,"rp") == 0 ) { g_Target->m_Machines[MACHIDX_I386]-> OutputAll(g_Target->m_Machines[MACHIDX_I386]->m_AllMask, DEBUG_OUTPUT_NORMAL); return; } if (achInput[1] != 'e') { dprintf("VDM R: can only operate on 32-bit registers\n"); return; } if (strlen(achInput) < 6) { dprintf("VDM R: Missing value\n"); return; } dwVal = strtoul(&achInput[5], NULL, 16); if ( _strnicmp(&achInput[2],"ax", 2) == 0 ) { pvc->Eax = dwVal; } else if ( _strnicmp(&achInput[2],"bx", 2) == 0 ) { pvc->Ebx = dwVal; } else if ( _strnicmp(&achInput[2],"cx", 2) == 0 ) { pvc->Ecx = dwVal; } else if ( _strnicmp(&achInput[2],"dx", 2) == 0 ) { pvc->Edx = dwVal; } else if ( _strnicmp(&achInput[2],"si", 2) == 0 ) { pvc->Esi = dwVal; } else if ( _strnicmp(&achInput[2],"di", 2) == 0 ) { pvc->Edi = dwVal; } else if ( _strnicmp(&achInput[2],"ip", 2) == 0 ) { pvc->Eip = dwVal; } else if ( _strnicmp(&achInput[2],"sp", 2) == 0 ) { pvc->Esp = dwVal; } else if ( _strnicmp(&achInput[2],"bp", 2) == 0 ) { pvc->Ebp = dwVal; } else if ( _strnicmp(&achInput[2],"fl", 2) == 0 ) { pvc->EFlags = dwVal; } else { dprintf("Invalid register\n"); return; } dprintf("%.8X will be flushed to '%3.3s'. Use 'rp' to display pending values\n", dwVal, &achInput[1]); } void DebugEvent64To(LPDEBUG_EVENT64 Event64, LPDEBUG_EVENT Event) { Event->dwDebugEventCode = Event64->dwDebugEventCode; Event->dwProcessId = Event64->dwProcessId; Event->dwThreadId = Event64->dwThreadId; switch(Event64->dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: ExceptionRecord64To(&Event64->u.Exception.ExceptionRecord, &Event->u.Exception.ExceptionRecord); Event->u.Exception.dwFirstChance = Event64->u.Exception.dwFirstChance; break; case CREATE_THREAD_DEBUG_EVENT: Event->u.CreateThread.hThread = (PVOID)(ULONG_PTR)(Event64->u.CreateThread.hThread); Event->u.CreateThread.lpThreadLocalBase = (PVOID)(ULONG_PTR)(Event64->u.CreateThread.lpThreadLocalBase); Event->u.CreateThread.lpStartAddress = (LPTHREAD_START_ROUTINE)(ULONG_PTR)(Event64->u.CreateThread.lpStartAddress); break; case CREATE_PROCESS_DEBUG_EVENT: Event->u.CreateProcessInfo.hFile = (PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.hFile); Event->u.CreateProcessInfo.hProcess = (PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.hProcess); Event->u.CreateProcessInfo.hThread = (PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.hThread); Event->u.CreateProcessInfo.lpBaseOfImage = (PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpBaseOfImage); Event->u.CreateProcessInfo.dwDebugInfoFileOffset = Event64->u.CreateProcessInfo.dwDebugInfoFileOffset; Event->u.CreateProcessInfo.nDebugInfoSize = Event64->u.CreateProcessInfo.nDebugInfoSize; Event->u.CreateProcessInfo.lpThreadLocalBase = (PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpThreadLocalBase); Event->u.CreateProcessInfo.lpStartAddress = (LPTHREAD_START_ROUTINE)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpStartAddress); Event->u.CreateProcessInfo.lpImageName = (PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpImageName); Event->u.CreateProcessInfo.fUnicode = Event64->u.CreateProcessInfo.fUnicode; break; case EXIT_THREAD_DEBUG_EVENT: Event->u.ExitThread.dwExitCode = Event64->u.ExitThread.dwExitCode; break; case EXIT_PROCESS_DEBUG_EVENT: Event->u.ExitProcess.dwExitCode = Event64->u.ExitProcess.dwExitCode; break; case LOAD_DLL_DEBUG_EVENT: Event->u.LoadDll.hFile = (PVOID)(ULONG_PTR)(Event64->u.LoadDll.hFile); Event->u.LoadDll.lpBaseOfDll = (PVOID)(ULONG_PTR)(Event64->u.LoadDll.lpBaseOfDll); Event->u.LoadDll.dwDebugInfoFileOffset = Event64->u.LoadDll.dwDebugInfoFileOffset; Event->u.LoadDll.nDebugInfoSize = Event64->u.LoadDll.nDebugInfoSize; Event->u.LoadDll.lpImageName = (PVOID)(ULONG_PTR)(Event64->u.LoadDll.lpImageName); Event->u.LoadDll.fUnicode = Event64->u.LoadDll.fUnicode; break; case UNLOAD_DLL_DEBUG_EVENT: Event->u.UnloadDll.lpBaseOfDll = (PVOID)(ULONG_PTR)(Event64->u.UnloadDll.lpBaseOfDll); break; case OUTPUT_DEBUG_STRING_EVENT: Event->u.DebugString.lpDebugStringData = (LPSTR)(ULONG_PTR)(Event64->u.DebugString.lpDebugStringData); Event->u.DebugString.fUnicode = Event64->u.DebugString.fUnicode; Event->u.DebugString.nDebugStringLength = Event64->u.DebugString.nDebugStringLength; break; case RIP_EVENT: Event->u.RipInfo.dwError = Event64->u.RipInfo.dwError; Event->u.RipInfo.dwType = Event64->u.RipInfo.dwType; break; } } ULONG VDMEvent(DEBUG_EVENT64* pDebugEvent) /* returns - 0, if exception not handled STATUS_VDM_EVENT, if exception handled otherwise, the return value is the translated event status, for example STATUS_BREAKPOINT */ { LPSTR Str; LPSTR pFileName; BOOL b; ULONG lpNumberOfBytesRead; UINT_PTR address; PULONG_PTR lpdw; int segslot; int mode; BOOL fData; WORD selector; WORD segment; WORD newselect; BOOL fStop; DWORD ImgLen; ULONG ulRet; BOOL fNeedSegTableEdit; BOOL fNeedInteractive; BOOL fVerbose; CHAR achInput[_MAX_PATH]; BOOL fProcess; SEGMENT_NOTE se; IMAGE_NOTE im; BOOL bProtectMode; WORD EventFlags; ulRet = VDMEVENT_HANDLED; if (!fVDMInitDone) { fVDMInitDone = TRUE; HINSTANCE hmodVDM = NULL; const char* c_szVDMFailed = NULL; fVDMActive = ( (hmodVDM = LoadLibrary("VDMDBG.DLL")) && (pfnVDMProcessException = (VDMPROCESSEXCEPTIONPROC) GetProcAddress(hmodVDM, c_szVDMFailed = "VDMProcessException") ) && (pfnVDMGetPointer = (VDMGETPOINTERPROC) GetProcAddress(hmodVDM, c_szVDMFailed = "VDMGetPointer") ) && (pfnVDMGetThreadSelectorEntry = (VDMGETTHREADSELECTORENTRYPROC) GetProcAddress(hmodVDM, c_szVDMFailed = "VDMGetThreadSelectorEntry") ) && (pfnVDMGetContext = (VDMGETCONTEXTPROC) GetProcAddress(hmodVDM, c_szVDMFailed = "VDMGetContext") ) && (pfnVDMSetContext = (VDMSETCONTEXTPROC) GetProcAddress( hmodVDM, c_szVDMFailed = "VDMSetContext") ) && (pfnVDMGetSelectorModule = (VDMGETSELECTORMODULEPROC) GetProcAddress( hmodVDM, c_szVDMFailed = "VDMGetSelectorModule") ) ); // fVDMActive if (!fVDMActive) { // display error on first time... if (!hmodVDM) { dprintf("LoadLibrary(VDMDBG.DLL) failed\n"); } else if (c_szVDMFailed && *c_szVDMFailed) { // is valid printable string dprintf("%s can not be found in VDMDBG.DLL\n", c_szVDMFailed); } else { dprintf("Unknown failure while initializing VDMDBG.DLL\n"); } // iff } // if } // if if (!fVDMActive) return VDMEVENT_NOT_HANDLED; DEBUG_EVENT Event; DebugEvent64To(pDebugEvent, &Event); lpdw = &(Event.u.Exception.ExceptionRecord.ExceptionInformation[0]); fProcess = (*pfnVDMProcessException)(&Event); fNeedSegTableEdit = FALSE; fNeedInteractive = FALSE; fVerbose = FALSE; mode = LOWORD(lpdw[0]); EventFlags = HIWORD(lpdw[0]); bProtectMode = (BOOL) (EventFlags & VDMEVENT_PE); // Has the caller explicitly asked for interaction? if (EventFlags & VDMEVENT_NEEDS_INTERACTIVE) { fNeedInteractive = TRUE; } if (EventFlags & VDMEVENT_VERBOSE) { fVerbose = TRUE; } switch( mode ) { case DBG_SEGLOAD: case DBG_SEGMOVE: case DBG_SEGFREE: case DBG_MODLOAD: case DBG_MODFREE: address = lpdw[2]; b = g_Target->ReadVirtual(g_Process, EXTEND64(address), &se, sizeof(se), &lpNumberOfBytesRead ) == S_OK; if ( !b || lpNumberOfBytesRead != sizeof(se) ) { return( VDMEVENT_NOT_HANDLED ); } break; case DBG_DLLSTART: case DBG_DLLSTOP: case DBG_TASKSTART: case DBG_TASKSTOP: address = lpdw[2]; b = g_Target->ReadVirtual(g_Process, EXTEND64(address), &im, sizeof(im), &lpNumberOfBytesRead ) == S_OK; if ( !b || lpNumberOfBytesRead != sizeof(im) ) { return( VDMEVENT_NOT_HANDLED ); } break; } switch( mode ) { default: ulRet = VDMEVENT_NOT_HANDLED; 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 ); } } if (fVerbose) { dprintf("VDM SegLoad: %s(%d) %s => %x\n", se.FileName, segment, fData ? "Data" : "Code", selector); } break; case DBG_SEGMOVE: fNeedSegTableEdit = TRUE; segment = se.Segment; selector = se.Selector1; newselect = se.Selector2; if ( newselect == 0 ) { mode = DBG_SEGFREE; } else if (segment > 1) { // // real mode module is getting split up into different // segments, so create a new segtable entry // segslot = 0; while ( segslot < MAXSEGENTRY ) { if (( segtable[segslot].type != SEGTYPE_AVAILABLE ) && ( segtable[segslot].selector == selector )) { mode = DBG_MODLOAD; segment--; //make it zero-based selector = newselect; pFileName = segtable[segslot].path_name; // Don't have the image length here so // just choose one. ImgLen = segtable[segslot].ImgLen; break; } segslot++; } } if (fVerbose) { dprintf("VDM SegMove: (%d) %x => %x\n", segment, selector, newselect); } break; case DBG_SEGFREE: fNeedSegTableEdit = TRUE; selector = se.Selector1; if (fVerbose) { dprintf("VDM SegFree: %x\n",selector); } break; case DBG_MODFREE: fNeedSegTableEdit = TRUE; if ( strlen(se.FileName) != 0 ) { dprintf("Freeing [%s]\n", se.FileName ); } else if (fVerbose) { dprintf("VDM ModFree: unknown module\n"); } break; case DBG_MODLOAD: fNeedSegTableEdit = TRUE; selector = se.Selector1; ImgLen = se.Length; segment = 0; pFileName = se.FileName; 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 ); } } if (fVerbose) { dprintf("VDM ModLoad: %s => %x, len=%x\n", se.FileName, selector, ImgLen); } break; case DBG_SINGLESTEP: if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_I386) { fNeedInteractive = FALSE; ulRet = STATUS_SINGLE_STEP; } else { fNeedInteractive = TRUE; } break; case DBG_BREAK: if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_I386) { fNeedInteractive = FALSE; ulRet = STATUS_BREAKPOINT; } else { fNeedInteractive = TRUE; } break; case DBG_GPFAULT: dprintf(" GP Fault in VDM\n"); if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_I386) { fNeedInteractive = FALSE; ulRet = STATUS_ACCESS_VIOLATION; } else { fNeedInteractive = TRUE; } break; case DBG_GPFAULT2: dprintf("GP Fault in VDM\n"); dprintf("!!! second chance !!!\n"); fNeedInteractive = TRUE; break; case DBG_INSTRFAULT: dprintf("invalid opcode fault in VDM\n"); fNeedInteractive = TRUE; break; case DBG_DIVOVERFLOW: dprintf("divide overflow in VDM\n"); fNeedInteractive = TRUE; break; case DBG_STACKFAULT: dprintf("stack fault in VDM\n"); if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_I386) { fNeedInteractive = FALSE; ulRet = STATUS_ACCESS_VIOLATION; } else { fNeedInteractive = TRUE; } break; case DBG_TASKSTART: if ( fNeedInteractive || fVerbose ) { dprintf("VDM Start Task <%s:%s>\n", im.Module, im.FileName ); } break; case DBG_DLLSTART: if ( fNeedInteractive || fVerbose ) { dprintf("VDM Start Dll <%s:%s>\n", im.Module, im.FileName ); } 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[MAX_DISASM_LEN]; char path[128]; UINT cSeg; ADDR addr; X86_NT5_CONTEXT vc; g_Target->m_Machines[MACHIDX_I386]-> m_Context.X86Nt5Context.ContextFlags = VDMCONTEXT_FULL; (*pfnVDMGetContext)(OS_HANDLE(g_EventProcess->m_SysHandle), OS_HANDLE(g_EventThread->m_Handle), (LPVDMCONTEXT)&g_Target-> m_Machines[MACHIDX_I386]->m_Context); g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context.EFlags &= ~V86FLAGS_TRACE; vc = g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context; // Dump a simulated context g_Target->m_Machines[MACHIDX_I386]->OutputAll(g_Target->m_Machines[MACHIDX_I386]->m_AllMask, DEBUG_OUTPUT_PROMPT_REGISTERS); b = (*pfnVDMGetSelectorModule)(OS_HANDLE(g_EventProcess->m_SysHandle), OS_HANDLE(g_EventThread->m_Handle), (WORD)g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context.SegCs, &cSeg, text, 128, path, 128 ); if ( b ) { dprintf("%s:%d!%04x:\n", text, cSeg, (WORD)g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context.Eip ); } addr.seg = (WORD)g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context.SegCs; addr.off = g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context.Eip; if ( !bProtectMode ) { addr.type = ADDR_V86 | FLAT_COMPUTED; addr.flat = (*pfnVDMGetPointer)( OS_HANDLE(g_EventProcess->m_SysHandle), OS_HANDLE(g_EventThread->m_Handle), addr.seg, (ULONG)addr.off, FALSE ); } else { addr.type = ADDR_16 | FLAT_COMPUTED; addr.flat = (*pfnVDMGetPointer)( OS_HANDLE(g_EventProcess->m_SysHandle), OS_HANDLE(g_EventThread->m_Handle), addr.seg, (ULONG)addr.off, TRUE ); } if ( Flat(addr) == 0 ) { dprintf("Unable to disassemble failing code\n"); } else { g_Target->m_Machines[MACHIDX_I386]-> Disassemble( g_Process, &addr, text, TRUE ); dprintf("%s", text ); } AddExtensionDll("vdmexts", TRUE, g_Target, NULL); while ( TRUE ) { GetInput("VDM>", achInput, sizeof(achInput), GETIN_LOG_INPUT_LINE); if ( _stricmp(achInput,"gh") == 0 || _stricmp(achInput,"g") == 0 ) { ulRet = VDMEVENT_HANDLED; break; } if ( _stricmp(achInput,"gn") == 0 ) { ulRet = VDMEVENT_NOT_HANDLED; break; } if ( _stricmp(achInput, "t") == 0 ) { ulRet = VDMEVENT_HANDLED; vc.EFlags |= V86FLAGS_TRACE; break; } if ((achInput[0] == 'r') && (_stricmp(achInput, "r") != 0)) { VDMRegCmd(achInput, &vc); g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context = vc; continue; } if ( _stricmp(achInput,"?") == 0 ) { dprintf("\n---------- NTVDM Monitor Help ------------\n\n"); dprintf("g - Go\n"); dprintf("gh - Go : Exception handled\n"); dprintf("gn - Go : Exception not handled\n"); dprintf("help - Display extension help\n"); dprintf("r - reg=[eax|ebx|ecx|edx|eip|esp|ebp|efl]\n"); dprintf("rp - display pending register set\n"); dprintf("t - Trace 1 instruction\n"); dprintf("! - Execute extension command\n"); dprintf(". - Execute native NTSD command\n"); dprintf("\nAnything else is interpreted as a VDMEXTS extension command\n\n"); continue; } g_CurCmd = &achInput[1]; g_CommandStart = g_CurCmd; ProcessCommandsAndCatch(NULL); } g_Target->m_Machines[MACHIDX_I386]->m_Context.X86Nt5Context = vc; (*pfnVDMSetContext)(OS_HANDLE(g_EventProcess->m_SysHandle), OS_HANDLE(g_EventThread->m_Handle), (LPVDMCONTEXT)&g_Target->m_Machines[MACHIDX_I386]->m_Context); } /* ** 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 = (PSTR)calloc(1,strlen(se.FileName)+1); if ( !Str ) { return( VDMEVENT_NOT_HANDLED ); } 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 = segment; 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 = (PSTR)calloc(1,strlen(pFileName)+1); if ( !Str ) { return( VDMEVENT_NOT_HANDLED ); } strcpy( Str, pFileName ); segtable[segslot].path_name = Str; segtable[segslot].ImgLen = ImgLen; fStop = TRUE; } break; } if ( fStop ) { break; } segslot++; } if ( segslot == MAXSEGENTRY ) { if ( mode == DBG_SEGLOAD ) { dprintf("Warning - adding selector %04X for segment %d, segtable full\n", selector, segment ); } } } pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode = ulRet; return( ulRet ); }