/*++ Copyright (c) 1990 Microsoft Corporation Module Name: vdmdbg.c Abstract: This module contains the debugging support needed to debug 16-bit VDM applications Author: Bob Day (bobday) 16-Sep-1992 Wrote it Revision History: Neil Sandlin (neilsa) 1-Mar-1997 Enhanced it --*/ #include #pragma hdrstop WORD LastEventFlags; //---------------------------------------------------------------------------- // VDMGetThreadSelectorEntry() // // Public interface to the InternalGetThreadSelectorEntry, needed because // that routine requires the process handle. // //---------------------------------------------------------------------------- BOOL WINAPI VDMGetThreadSelectorEntry( HANDLE hProcess, HANDLE hUnused, WORD wSelector, LPVDMLDT_ENTRY lpSelectorEntry ) { BOOL fResult; UNREFERENCED_PARAMETER(hUnused); fResult = InternalGetThreadSelectorEntry( hProcess, wSelector, lpSelectorEntry ); return( fResult ); } //---------------------------------------------------------------------------- // VDMGetPointer() // // Public interface to the InternalGetPointer, needed because that // routine requires the process handle. // //---------------------------------------------------------------------------- ULONG WINAPI VDMGetPointer( HANDLE hProcess, HANDLE hUnused, WORD wSelector, DWORD dwOffset, BOOL fProtMode ) { ULONG ulResult; UNREFERENCED_PARAMETER(hUnused); ulResult = InternalGetPointer( hProcess, wSelector, dwOffset, fProtMode ); return( ulResult ); } // // Obselete functions // BOOL WINAPI VDMGetThreadContext( LPDEBUG_EVENT lpDebugEvent, LPVDMCONTEXT lpVDMContext) { HANDLE hProcess; BOOL bReturn; hProcess = OpenProcess( PROCESS_VM_READ, FALSE, lpDebugEvent->dwProcessId ); bReturn = VDMGetContext(hProcess, NULL, lpVDMContext); CloseHandle( hProcess ); return bReturn; } BOOL WINAPI VDMSetThreadContext( LPDEBUG_EVENT lpDebugEvent, LPVDMCONTEXT lpVDMContext) { HANDLE hProcess; BOOL bReturn; hProcess = OpenProcess( PROCESS_VM_READ, FALSE, lpDebugEvent->dwProcessId ); bReturn = VDMSetContext(hProcess, NULL, lpVDMContext); CloseHandle( hProcess ); return bReturn; } //---------------------------------------------------------------------------- // VDMGetContext() // // Interface to get the simulated context. The same functionality as // GetThreadContext except that it happens on the simulated 16-bit context, // rather than the 32-bit context. // //---------------------------------------------------------------------------- BOOL WINAPI VDMGetContext( HANDLE hProcess, HANDLE hThread, LPVDMCONTEXT lpVDMContext ) { VDMCONTEXT vcContext; BOOL b; DWORD lpNumberOfBytesRead; int i; BOOL bUseVDMContext = TRUE; #ifdef _X86_ if (hThread) { vcContext.ContextFlags = lpVDMContext->ContextFlags; if (!GetThreadContext(hThread, (CONTEXT*)&vcContext)) { return FALSE; } if ((vcContext.EFlags & V86FLAGS_V86) || (vcContext.SegCs != 0x1b)) { bUseVDMContext = FALSE; } } #endif if (bUseVDMContext) { b = ReadProcessMemory(hProcess, lpVdmContext, &vcContext, sizeof(vcContext), &lpNumberOfBytesRead ); if ( !b || lpNumberOfBytesRead != sizeof(vcContext) ) { return( FALSE ); } } #ifdef _X86_ if ((lpVDMContext->ContextFlags & VDMCONTEXT_CONTROL) == VDMCONTEXT_CONTROL) { // // Set registers ebp, eip, cs, eflag, esp and ss. // lpVDMContext->Ebp = vcContext.Ebp; lpVDMContext->Eip = vcContext.Eip; lpVDMContext->SegCs = vcContext.SegCs; lpVDMContext->EFlags = vcContext.EFlags; lpVDMContext->SegSs = vcContext.SegSs; lpVDMContext->Esp = vcContext.Esp; } // // Set segment register contents if specified. // if ((lpVDMContext->ContextFlags & VDMCONTEXT_SEGMENTS) == VDMCONTEXT_SEGMENTS) { // // Set segment registers gs, fs, es, ds. // // These values are junk most of the time, but useful // for debugging under certain conditions. Therefore, // we report whatever was in the frame. // lpVDMContext->SegGs = vcContext.SegGs; lpVDMContext->SegFs = vcContext.SegFs; lpVDMContext->SegEs = vcContext.SegEs; lpVDMContext->SegDs = vcContext.SegDs; } // // Set integer register contents if specified. // if ((lpVDMContext->ContextFlags & VDMCONTEXT_INTEGER) == VDMCONTEXT_INTEGER) { // // Set integer registers edi, esi, ebx, edx, ecx, eax // lpVDMContext->Edi = vcContext.Edi; lpVDMContext->Esi = vcContext.Esi; lpVDMContext->Ebx = vcContext.Ebx; lpVDMContext->Ecx = vcContext.Ecx; lpVDMContext->Edx = vcContext.Edx; lpVDMContext->Eax = vcContext.Eax; } // // Fetch floating register contents if requested, and type of target // is user. (system frames have no fp state, so ignore request) // if ( (lpVDMContext->ContextFlags & VDMCONTEXT_FLOATING_POINT) == VDMCONTEXT_FLOATING_POINT ) { lpVDMContext->FloatSave.ControlWord = vcContext.FloatSave.ControlWord; lpVDMContext->FloatSave.StatusWord = vcContext.FloatSave.StatusWord; lpVDMContext->FloatSave.TagWord = vcContext.FloatSave.TagWord; lpVDMContext->FloatSave.ErrorOffset = vcContext.FloatSave.ErrorOffset; lpVDMContext->FloatSave.ErrorSelector = vcContext.FloatSave.ErrorSelector; lpVDMContext->FloatSave.DataOffset = vcContext.FloatSave.DataOffset; lpVDMContext->FloatSave.DataSelector = vcContext.FloatSave.DataSelector; lpVDMContext->FloatSave.Cr0NpxState = vcContext.FloatSave.Cr0NpxState; for (i = 0; i < SIZE_OF_80387_REGISTERS; i++) { lpVDMContext->FloatSave.RegisterArea[i] = vcContext.FloatSave.RegisterArea[i]; } } // // Fetch Dr register contents if requested. Values may be trash. // if ((lpVDMContext->ContextFlags & VDMCONTEXT_DEBUG_REGISTERS) == VDMCONTEXT_DEBUG_REGISTERS) { lpVDMContext->Dr0 = vcContext.Dr0; lpVDMContext->Dr1 = vcContext.Dr1; lpVDMContext->Dr2 = vcContext.Dr2; lpVDMContext->Dr3 = vcContext.Dr3; lpVDMContext->Dr6 = vcContext.Dr6; lpVDMContext->Dr7 = vcContext.Dr7; } #else { NT_CPU_INFO nt_cpu_info; BOOL bInNano; ULONG UMask; b = ReadProcessMemory(hProcess, lpNtCpuInfo, &nt_cpu_info, sizeof(NT_CPU_INFO), &lpNumberOfBytesRead ); if ( !b || lpNumberOfBytesRead != sizeof(NT_CPU_INFO) ) { return( FALSE ); } bInNano = ReadDword(hProcess, nt_cpu_info.in_nano_cpu); UMask = ReadDword(hProcess, nt_cpu_info.universe); lpVDMContext->Eax = GetRegValue(hProcess, nt_cpu_info.eax, bInNano, UMask); lpVDMContext->Ecx = GetRegValue(hProcess, nt_cpu_info.ecx, bInNano, UMask); lpVDMContext->Edx = GetRegValue(hProcess, nt_cpu_info.edx, bInNano, UMask); lpVDMContext->Ebx = GetRegValue(hProcess, nt_cpu_info.ebx, bInNano, UMask); lpVDMContext->Ebp = GetRegValue(hProcess, nt_cpu_info.ebp, bInNano, UMask); lpVDMContext->Esi = GetRegValue(hProcess, nt_cpu_info.esi, bInNano, UMask); lpVDMContext->Edi = GetRegValue(hProcess, nt_cpu_info.edi, bInNano, UMask); lpVDMContext->Esp = GetEspValue(hProcess, nt_cpu_info, bInNano); // // nt_cpu_info.flags isn't very much use, because several of the // flags values are not kept in memory, but computed each time. // The emulator doesn't supply us with the right value, so we // try to get it from the code in ntvdmd.dll // lpVDMContext->EFlags = vcContext.EFlags; // // On risc platforms, we don't run in V86 mode, we run in REAL mode. // So the widespread usage of testing the V86 mode bit in EFLAGS // would not correctly determine the address mode. Since there is // no more room in the VDM context structure, the simplest thing // to do is simply pretend to be in V86 mode when we are in REAL mode. // if (ReadDword(hProcess, nt_cpu_info.cr0) & 1) { lpVDMContext->EFlags |= V86FLAGS_V86; } lpVDMContext->Eip = ReadDword(hProcess, nt_cpu_info.eip); lpVDMContext->SegEs = ReadWord(hProcess, nt_cpu_info.es); lpVDMContext->SegCs = ReadWord(hProcess, nt_cpu_info.cs); lpVDMContext->SegSs = ReadWord(hProcess, nt_cpu_info.ss); lpVDMContext->SegDs = ReadWord(hProcess, nt_cpu_info.ds); lpVDMContext->SegFs = ReadWord(hProcess, nt_cpu_info.fs); lpVDMContext->SegGs = ReadWord(hProcess, nt_cpu_info.gs); } #endif return( TRUE ); } //---------------------------------------------------------------------------- // VDMSetContext() // // Interface to set the simulated context. Similar in most respects to // the SetThreadContext API supported by Win NT. Only differences are // in the bits which must be "sanitized". // //---------------------------------------------------------------------------- BOOL WINAPI VDMSetContext( HANDLE hProcess, HANDLE hThread, LPVDMCONTEXT lpVDMContext ) { VDMINTERNALINFO viInfo; VDMCONTEXT vcContext; BOOL b; DWORD lpNumberOfBytes; INT i; BOOL bUseVDMContext = TRUE; #ifdef _X86_ if (hThread) { if (!GetThreadContext(hThread, (CONTEXT*)&vcContext)) { return FALSE; } if ((vcContext.EFlags & V86FLAGS_V86) || (vcContext.SegCs != 0x1b)) { bUseVDMContext = FALSE; } } #endif if (bUseVDMContext) { b = ReadProcessMemory(hProcess, lpVdmContext, &vcContext, sizeof(vcContext), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(vcContext) ) { return( FALSE ); } } if ((lpVDMContext->ContextFlags & VDMCONTEXT_CONTROL) == VDMCONTEXT_CONTROL) { // // Set registers ebp, eip, cs, eflag, esp and ss. // vcContext.Ebp = lpVDMContext->Ebp; vcContext.Eip = lpVDMContext->Eip; // // Don't allow them to modify the mode bit. // // Only allow these bits to get set: 01100000110111110111 // V86FLAGS_CARRY 0x00001 // V86FLAGS_? 0x00002 // V86FLAGS_PARITY 0x00004 // V86FLAGS_AUXCARRY 0x00010 // V86FLAGS_ZERO 0x00040 // V86FLAGS_SIGN 0x00080 // V86FLAGS_TRACE 0x00100 // V86FLAGS_INTERRUPT 0x00200 // V86FLAGS_DIRECTION 0x00400 // V86FLAGS_OVERFLOW 0x00800 // V86FLAGS_RESUME 0x10000 // V86FLAGS_VM86 0x20000 // V86FLAGS_ALIGNMENT 0x40000 // // Commonly flags will be 0x10246 // if ( vcContext.EFlags & V86FLAGS_V86 ) { vcContext.EFlags = V86FLAGS_V86 | (lpVDMContext->EFlags & ( V86FLAGS_CARRY | 0x0002 | V86FLAGS_PARITY | V86FLAGS_AUXCARRY | V86FLAGS_ZERO | V86FLAGS_SIGN | V86FLAGS_TRACE | V86FLAGS_INTERRUPT | V86FLAGS_DIRECTION | V86FLAGS_OVERFLOW | V86FLAGS_RESUME | V86FLAGS_ALIGNMENT | V86FLAGS_IOPL )); } else { vcContext.EFlags = ~V86FLAGS_V86 & (lpVDMContext->EFlags & ( V86FLAGS_CARRY | 0x0002 | V86FLAGS_PARITY | V86FLAGS_AUXCARRY | V86FLAGS_ZERO | V86FLAGS_SIGN | V86FLAGS_TRACE | V86FLAGS_INTERRUPT | V86FLAGS_DIRECTION | V86FLAGS_OVERFLOW | V86FLAGS_RESUME | V86FLAGS_ALIGNMENT | V86FLAGS_IOPL )); } // // CS might only be allowable as a ring 3 selector. // if ( vcContext.EFlags & V86FLAGS_V86 ) { vcContext.SegCs = lpVDMContext->SegCs; } else { #ifdef i386 vcContext.SegCs = lpVDMContext->SegCs | 0x0003; #else vcContext.SegCs = lpVDMContext->SegCs; #endif } vcContext.SegSs = lpVDMContext->SegSs; vcContext.Esp = lpVDMContext->Esp; } // // Set segment register contents if specified. // if ((lpVDMContext->ContextFlags & VDMCONTEXT_SEGMENTS) == VDMCONTEXT_SEGMENTS) { // // Set segment registers gs, fs, es, ds. // vcContext.SegGs = lpVDMContext->SegGs; vcContext.SegFs = lpVDMContext->SegFs; vcContext.SegEs = lpVDMContext->SegEs; vcContext.SegDs = lpVDMContext->SegDs; } // // Set integer register contents if specified. // if ((lpVDMContext->ContextFlags & VDMCONTEXT_INTEGER) == VDMCONTEXT_INTEGER) { // // Set integer registers edi, esi, ebx, edx, ecx, eax // vcContext.Edi = lpVDMContext->Edi; vcContext.Esi = lpVDMContext->Esi; vcContext.Ebx = lpVDMContext->Ebx; vcContext.Ecx = lpVDMContext->Ecx; vcContext.Edx = lpVDMContext->Edx; vcContext.Eax = lpVDMContext->Eax; } // // Fetch floating register contents if requested, and type of target // is user. // if ( (lpVDMContext->ContextFlags & VDMCONTEXT_FLOATING_POINT) == VDMCONTEXT_FLOATING_POINT ) { vcContext.FloatSave.ControlWord = lpVDMContext->FloatSave.ControlWord; vcContext.FloatSave.StatusWord = lpVDMContext->FloatSave.StatusWord; vcContext.FloatSave.TagWord = lpVDMContext->FloatSave.TagWord; vcContext.FloatSave.ErrorOffset = lpVDMContext->FloatSave.ErrorOffset; vcContext.FloatSave.ErrorSelector = lpVDMContext->FloatSave.ErrorSelector; vcContext.FloatSave.DataOffset = lpVDMContext->FloatSave.DataOffset; vcContext.FloatSave.DataSelector = lpVDMContext->FloatSave.DataSelector; vcContext.FloatSave.Cr0NpxState = lpVDMContext->FloatSave.Cr0NpxState; for (i = 0; i < SIZE_OF_80387_REGISTERS; i++) { vcContext.FloatSave.RegisterArea[i] = lpVDMContext->FloatSave.RegisterArea[i]; } } // // Fetch Dr register contents if requested. Values may be trash. // if ((lpVDMContext->ContextFlags & VDMCONTEXT_DEBUG_REGISTERS) == VDMCONTEXT_DEBUG_REGISTERS) { vcContext.Dr0 = lpVDMContext->Dr0; vcContext.Dr1 = lpVDMContext->Dr1; vcContext.Dr2 = lpVDMContext->Dr2; vcContext.Dr3 = lpVDMContext->Dr3; vcContext.Dr6 = lpVDMContext->Dr6; vcContext.Dr7 = lpVDMContext->Dr7; } #ifdef _X86_ if (!bUseVDMContext) { if (!SetThreadContext(hThread, (CONTEXT*)&vcContext)) { return FALSE; } } #endif b = WriteProcessMemory( hProcess, lpVdmContext, &vcContext, sizeof(vcContext), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(vcContext) ) { return( FALSE ); } return( TRUE ); } //---------------------------------------------------------------------------- // VDMBreakThread() // // Interface to interrupt a thread while it is running without any break- // points. An ideal debugger would have this feature. Since it is hard // to implement, we will be doing it later. // //---------------------------------------------------------------------------- BOOL WINAPI VDMBreakThread( HANDLE hProcess, HANDLE hThread ) { return( FALSE ); } //---------------------------------------------------------------------------- // VDMProcessException() // // This function acts as a filter of debug events. Most debug events // should be ignored by the debugger (because they don't have the context // record pointer or the internal info structure setup. Those events // cause this function to return FALSE, which tells the debugger to just // blindly continue the exception. When the function does return TRUE, // the debugger should look at the exception code to determine what to // do (and all the the structures have been set up properly to deal with // calls to the other APIs). // //---------------------------------------------------------------------------- BOOL WINAPI VDMProcessException( LPDEBUG_EVENT lpDebugEvent ) { LPDWORD lpdw; int mode; BOOL fResult = TRUE; lpdw = &(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); mode = LOWORD(lpdw[0]); LastEventFlags = HIWORD(lpdw[0]); switch( mode ) { case DBG_SEGLOAD: case DBG_SEGMOVE: case DBG_SEGFREE: case DBG_MODLOAD: case DBG_MODFREE: ProcessSegmentNotification(lpDebugEvent); fResult = FALSE; break; case DBG_BREAK: ProcessBPNotification(lpDebugEvent); break; } ProcessInitNotification(lpDebugEvent); return( fResult ); } //---------------------------------------------------------------------------- // VDMGetSelectorModule() // // Interface to determine the module and segment associated with a given // selector. This is useful during debugging to associate symbols with // code and data segments. The symbol lookup should be done by the // debugger, given the module and segment number. // // This code was adapted from the Win 3.1 ToolHelp DLL // //---------------------------------------------------------------------------- BOOL WINAPI VDMGetSelectorModule( HANDLE hProcess, HANDLE hUnused, WORD wSelector, PUINT lpSegmentNumber, LPSTR lpModuleName, UINT nNameSize, LPSTR lpModulePath, UINT nPathSize ) { BOOL b; DWORD lpNumberOfBytes; BOOL fResult; DWORD lphMaster; DWORD lphMasterLen; DWORD lphMasterStart; DWORD lpOwner; DWORD lpThisModuleResTab; DWORD lpThisModuleName; DWORD lpPath; DWORD lpThisModulecSeg; DWORD lpThisModuleSegTab; DWORD lpThisSegHandle; WORD wMaster; WORD wMasterLen; DWORD dwMasterStart; DWORD dwArenaOffset; WORD wArenaSlot; DWORD lpArena; WORD wModHandle; WORD wResTab; UCHAR cLength; WORD wPathOffset; UCHAR cPath; WORD cSeg; WORD iSeg; WORD wSegTab; WORD wHandle; // CHAR chName[MAX_MODULE_NAME_LENGTH]; // CHAR chPath[MAX_MODULE_PATH_LENGTH]; UNREFERENCED_PARAMETER(hUnused); if ( lpModuleName != NULL ) *lpModuleName = '\0'; if ( lpModulePath != NULL ) *lpModulePath = '\0'; if ( lpSegmentNumber != NULL ) *lpSegmentNumber = 0; fResult = FALSE; #if 0 if ( wKernelSeg == 0 ) { return( FALSE ); } // Read out the master heap selector lphMaster = InternalGetPointer( hProcess, wKernelSeg, dwOffsetTHHOOK + TOOL_HMASTER, // To hGlobalHeap TRUE ); if ( lphMaster == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lphMaster, &wMaster, sizeof(wMaster), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wMaster) ) goto punt; wMaster |= 1; // Convert to selector // Read out the master heap selector length lphMasterLen = InternalGetPointer( hProcess, wKernelSeg, dwOffsetTHHOOK + TOOL_HMASTLEN, // To SelTableLen TRUE ); if ( lphMasterLen == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lphMasterLen, &wMasterLen, sizeof(wMasterLen), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wMasterLen) ) goto punt; // Read out the master heap selector start lphMasterStart = InternalGetPointer( hProcess, wKernelSeg, dwOffsetTHHOOK + TOOL_HMASTSTART, // To SelTableStart TRUE ); if ( lphMasterStart == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lphMasterStart, &dwMasterStart, sizeof(dwMasterStart), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(dwMasterStart) ) goto punt; // Now make sure the selector provided is in the right range if ( fKernel386 ) { // 386 kernel? wArenaSlot = (WORD)(wSelector & 0xFFF8); // Mask low 3 bits wArenaSlot = wArenaSlot >> 1; // Sel/8*4 if ( (WORD)wArenaSlot > wMasterLen ) goto punt; // Out of range wArenaSlot += (WORD)dwMasterStart; // Ok, Now read out the area header offset dwArenaOffset = (DWORD)0; // Default to 0 lpArena = InternalGetPointer( hProcess, wMaster, wArenaSlot, TRUE ); if ( lpArena == (DWORD)NULL ) goto punt; // 386 Kernel? b = ReadProcessMemory( hProcess, (LPVOID)lpArena, &dwArenaOffset, sizeof(dwArenaOffset), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(dwArenaOffset) ) goto punt; // Read out the owner member lpOwner = InternalGetPointer( hProcess, wMaster, dwArenaOffset+GA_OWNER386, TRUE ); if ( lpOwner == (DWORD)NULL ) goto punt; } else { lpOwner = InternalGetPointer( hProcess, wSelector, 0, TRUE ); if ( lpOwner == (DWORD)NULL ) goto punt; lpOwner -= GA_SIZE; lpOwner += GA_OWNER; } b = ReadProcessMemory( hProcess, (LPVOID)lpOwner, &wModHandle, sizeof(wModHandle), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wModHandle) ) goto punt; // Now read out the owners module name // Name is the first name in the resident names table lpThisModuleResTab = InternalGetPointer( hProcess, wModHandle, NE_RESTAB, TRUE ); if ( lpThisModuleResTab == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleResTab, &wResTab, sizeof(wResTab), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wResTab) ) goto punt; // Get the 1st byte of the resident names table (1st byte of module name) lpThisModuleName = InternalGetPointer( hProcess, wModHandle, wResTab, TRUE ); if ( lpThisModuleName == (DWORD)NULL ) goto punt; // PASCAL string (1st byte is length), read the byte. b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleName, &cLength, sizeof(cLength), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(cLength) ) goto punt; if ( cLength > MAX_MODULE_NAME_LENGTH ) goto punt; // Now go read the text of the name lpThisModuleName += 1; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleName, &chName, cLength, &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != (DWORD)cLength ) goto punt; chName[cLength] = '\0'; // Nul terminate it // Grab out the path name too! lpPath = InternalGetPointer( hProcess, wModHandle, NE_PATHOFFSET, TRUE ); if ( lpPath == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpPath, &wPathOffset, sizeof(wPathOffset), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wPathOffset) ) goto punt; // Get the 1st byte of the path name lpThisModuleName = InternalGetPointer( hProcess, wModHandle, wPathOffset, TRUE ); if ( lpThisModuleName == (DWORD)NULL ) goto punt; // PASCAL string (1st byte is length), read the byte. b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleName, &cPath, sizeof(cPath), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(cPath) ) goto punt; if ( cPath > MAX_MODULE_NAME_LENGTH ) goto punt; lpThisModuleName += 8; // 1st 8 characters are ignored cPath -= 8; // Now go read the text of the name b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleName, &chPath, cPath, &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != (DWORD)cPath ) goto punt; chPath[cPath] = '\0'; // Nul terminate it // Ok, we found the module we need, now grab the right selector for the // segment number passed in. lpThisModulecSeg = InternalGetPointer( hProcess, wModHandle, NE_CSEG, TRUE ); if ( lpThisModulecSeg == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModulecSeg, &cSeg, sizeof(cSeg), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(cSeg) ) goto punt; // Read the segment table pointer for this module lpThisModuleSegTab = InternalGetPointer( hProcess, wModHandle, NE_SEGTAB, TRUE ); if ( lpThisModuleSegTab == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleSegTab, &wSegTab, sizeof(wSegTab), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wSegTab) ) goto punt; // Loop through all of the segments for this module trying to find // one with the right handle. iSeg = 0; wSelector &= 0xFFF8; while ( iSeg < cSeg ) { lpThisSegHandle = InternalGetPointer( hProcess, wModHandle, wSegTab+iSeg*NEW_SEG1_SIZE+NS_HANDLE, TRUE ); if ( lpThisSegHandle == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisSegHandle, &wHandle, sizeof(wHandle), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wHandle) ) goto punt; wHandle &= 0xFFF8; if ( wHandle == (WORD)wSelector ) { break; } iSeg++; } if ( iSeg >= cSeg ) goto punt; // Wasn't found at all! if ( lpModuleName && strlen(chName)+1 > nNameSize ) goto punt; if ( lpModulePath && strlen(chPath)+1 > nPathSize ) goto punt; if ( lpModuleName != NULL ) strcpy( lpModuleName, chName ); if ( lpModulePath != NULL ) strcpy( lpModulePath, chPath ); if ( lpSegmentNumber != NULL ) *lpSegmentNumber = iSeg; fResult = TRUE; punt: #endif return( fResult ); } //---------------------------------------------------------------------------- // VDMGetModuleSelector() // // Interface to determine the selector for a given module's segment. // This is useful during debugging to associate code and data segments // with symbols. The symbol lookup should be done by the debugger, to // determine the module and segment number, which are then passed to us // and we determine the current selector for that module's segment. // // Again, this code was adapted from the Win 3.1 ToolHelp DLL // //---------------------------------------------------------------------------- BOOL WINAPI VDMGetModuleSelector( HANDLE hProcess, HANDLE hUnused, UINT uSegmentNumber, LPSTR lpModuleName, LPWORD lpSelector ) { BOOL b; DWORD lpNumberOfBytes; BOOL fResult; WORD wModHandle; DWORD lpModuleHead; DWORD lpThisModuleName; DWORD lpThisModuleNext; DWORD lpThisModuleResTab; DWORD lpThisModulecSeg; DWORD lpThisModuleSegTab; DWORD lpThisSegHandle; WORD wResTab; UCHAR cLength; WORD cSeg; WORD wSegTab; WORD wHandle; // CHAR chName[MAX_MODULE_NAME_LENGTH]; UNREFERENCED_PARAMETER(hUnused); *lpSelector = 0; fResult = FALSE; #if 0 if ( wKernelSeg == 0 ) { return( FALSE ); } lpModuleHead = InternalGetPointer( hProcess, wKernelSeg, dwOffsetTHHOOK + TOOL_HMODFIRST, TRUE ); if ( lpModuleHead == (DWORD)NULL ) goto punt; // lpModuleHead is a pointer into kernels data segment. It points to the // head of the module list (a chain of near pointers). b = ReadProcessMemory( hProcess, (LPVOID)lpModuleHead, &wModHandle, sizeof(wModHandle), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wModHandle) ) goto punt; while( wModHandle != (WORD)0 ) { wModHandle |= 1; // Name is the first name in the resident names table lpThisModuleResTab = InternalGetPointer( hProcess, wModHandle, NE_RESTAB, TRUE ); if ( lpThisModuleResTab == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleResTab, &wResTab, sizeof(wResTab), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wResTab) ) goto punt; // Get the 1st byte of the resident names table (1st byte of module name) lpThisModuleName = InternalGetPointer( hProcess, wModHandle, wResTab, TRUE ); if ( lpThisModuleName == (DWORD)NULL ) goto punt; // PASCAL string (1st byte is length), read the byte. b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleName, &cLength, sizeof(cLength), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(cLength) ) goto punt; if ( cLength > MAX_MODULE_NAME_LENGTH ) goto punt; lpThisModuleName += 1; // Now go read the text of the name b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleName, &chName, cLength, &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != (DWORD)cLength ) goto punt; chName[cLength] = '\0'; // Nul terminate it if ( _stricmp(chName, lpModuleName) == 0 ) { // Found the name which matches! break; } // Move to the next module in the list. lpThisModuleNext = InternalGetPointer( hProcess, wModHandle, NE_CBENTTAB, TRUE ); if ( lpThisModuleNext == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleNext, &wModHandle, sizeof(wModHandle), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wModHandle) ) goto punt; } if ( wModHandle == (WORD)0 ) { goto punt; } // Ok, we found the module we need, now grab the right selector for the // segment number passed in. lpThisModulecSeg = InternalGetPointer( hProcess, wModHandle, NE_CSEG, TRUE ); if ( lpThisModulecSeg == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModulecSeg, &cSeg, sizeof(cSeg), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(cSeg) ) goto punt; if ( uSegmentNumber > (DWORD)cSeg ) goto punt; // Read the segment table pointer for this module lpThisModuleSegTab = InternalGetPointer( hProcess, wModHandle, NE_SEGTAB, TRUE ); if ( lpThisModuleSegTab == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisModuleSegTab, &wSegTab, sizeof(wSegTab), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wSegTab) ) goto punt; lpThisSegHandle = InternalGetPointer( hProcess, wModHandle, wSegTab+(WORD)uSegmentNumber*NEW_SEG1_SIZE+NS_HANDLE, TRUE ); if ( lpThisSegHandle == (DWORD)NULL ) goto punt; b = ReadProcessMemory( hProcess, (LPVOID)lpThisSegHandle, &wHandle, sizeof(wHandle), &lpNumberOfBytes ); if ( !b || lpNumberOfBytes != sizeof(wHandle) ) goto punt; *lpSelector = (WORD)(wHandle | 1); fResult = TRUE; punt: #endif return( fResult ); } DWORD WINAPI VDMGetDbgFlags( HANDLE hProcess ) { ULONG NtvdmState; ULONG VdmDbgFlags; BOOL b; DWORD lpNumberOfBytes; // // Merge in the two places where our flags are kept // b = ReadProcessMemory(hProcess, lpNtvdmState, &NtvdmState, sizeof(NtvdmState), &lpNumberOfBytes); if ( !b || lpNumberOfBytes != sizeof(NtvdmState) ) { return 0; } b = ReadProcessMemory(hProcess, lpVdmDbgFlags, &VdmDbgFlags, sizeof(VdmDbgFlags), &lpNumberOfBytes); if ( !b || lpNumberOfBytes != sizeof(VdmDbgFlags) ) { return 0; } return ((NtvdmState & (VDMDBG_BREAK_EXCEPTIONS | VDMDBG_BREAK_DEBUGGER)) | (VdmDbgFlags & ~(VDMDBG_BREAK_EXCEPTIONS | VDMDBG_BREAK_DEBUGGER))); } BOOL WINAPI VDMSetDbgFlags( HANDLE hProcess, DWORD VdmDbgFlags ) { ULONG NtvdmState; BOOL b; DWORD lpNumberOfBytes; // // The flags are spread out in two places, so split off the appropriate // bits and write them separately. // b = ReadProcessMemory(hProcess, lpNtvdmState, &NtvdmState, sizeof(NtvdmState), &lpNumberOfBytes); if ( !b || lpNumberOfBytes != sizeof(NtvdmState) ) { return FALSE; } NtvdmState &= ~(VDMDBG_BREAK_EXCEPTIONS | VDMDBG_BREAK_DEBUGGER); NtvdmState |= VdmDbgFlags & (VDMDBG_BREAK_EXCEPTIONS | VDMDBG_BREAK_DEBUGGER); b = WriteProcessMemory(hProcess, lpNtvdmState, &NtvdmState, sizeof(NtvdmState), &lpNumberOfBytes); if ( !b || lpNumberOfBytes != sizeof(NtvdmState) ) { return FALSE; } VdmDbgFlags &= ~(VDMDBG_BREAK_EXCEPTIONS | VDMDBG_BREAK_DEBUGGER); b = WriteProcessMemory(hProcess, lpVdmDbgFlags, &VdmDbgFlags, sizeof(VdmDbgFlags), &lpNumberOfBytes); if ( !b || lpNumberOfBytes != sizeof(VdmDbgFlags) ) { return FALSE; } return TRUE; }