/*++ Copyright (c) 1990 Microsoft Corporation Module Name: util.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 wKernelSeg = 0; DWORD dwOffsetTHHOOK = 0L; LPVOID lpRemoteAddress = NULL; DWORD lpRemoteBlock = 0; BOOL fKernel386 = FALSE; DWORD dwLdtBase = 0; DWORD dwIntelBase = 0; LPVOID lpNtvdmState = NULL; LPVOID lpVdmDbgFlags = NULL; LPVOID lpVdmContext = NULL; LPVOID lpNtCpuInfo = NULL; LPVOID lpVdmBreakPoints = NULL; //---------------------------------------------------------------------------- // InternalGetThreadSelectorEntry() // // Routine to return a LDT_ENTRY structure for the passed in selector number. // Its is assumed that we are talking about protect mode selectors. // For x86 systems, take the easy way and just call the system. For non-x86 // systems, we get some information from softpc and index into them as the // LDT and GDT tables. // //---------------------------------------------------------------------------- BOOL InternalGetThreadSelectorEntry( HANDLE hProcess, WORD wSelector, LPVDMLDT_ENTRY lpSelectorEntry ) { BOOL bResult = FALSE; DWORD lpNumberOfBytesRead; // For non-intel systems, query the information from the LDT // that we have a pointer to from the VDMINTERNALINFO that we // got passed. if (!dwLdtBase) { RtlFillMemory( lpSelectorEntry, sizeof(VDMLDT_ENTRY), (UCHAR)0 ); } else { bResult = ReadProcessMemory( hProcess, (LPVOID)(dwLdtBase+((wSelector&~7))), lpSelectorEntry, sizeof(VDMLDT_ENTRY), &lpNumberOfBytesRead ); } return( bResult ); } //---------------------------------------------------------------------------- // InternalGetPointer() // // Routine to convert a 16-bit address into a 32-bit address. If fProtMode // is TRUE, then the selector table lookup is performed. Otherwise, simple // real mode address calculations are performed. On non-x86 systems, the // base of real memory is added into the // //---------------------------------------------------------------------------- ULONG InternalGetPointer( HANDLE hProcess, WORD wSelector, DWORD dwOffset, BOOL fProtMode ) { VDMLDT_ENTRY le; ULONG ulResult; ULONG base; ULONG limit; BOOL b; if ( fProtMode ) { b = InternalGetThreadSelectorEntry( hProcess, wSelector, &le ); if ( !b ) { return( 0 ); } base = ((ULONG)le.HighWord.Bytes.BaseHi << 24) + ((ULONG)le.HighWord.Bytes.BaseMid << 16) + ((ULONG)le.BaseLow); limit = (ULONG)le.LimitLow + ((ULONG)le.HighWord.Bits.LimitHi << 16); if ( le.HighWord.Bits.Granularity ) { limit <<= 12; limit += 0xFFF; } } else { base = wSelector << 4; limit = 0xFFFF; } if ( dwOffset > limit ) { ulResult = 0; } else { ulResult = base + dwOffset; #ifndef i386 ulResult += dwIntelBase; #endif } return( ulResult ); } //---------------------------------------------------------------------------- // ReadItem // // Internal routine used to read items out of the debugee's address space. // The routine returns TRUE for failure. This allows easy failure testing. // //---------------------------------------------------------------------------- BOOL ReadItem( HANDLE hProcess, WORD wSeg, DWORD dwOffset, LPVOID lpitem, UINT nSize ) { LPVOID lp; BOOL b; DWORD dwBytes; if ( nSize == 0 ) { return( FALSE ); } lp = (LPVOID)InternalGetPointer( hProcess, (WORD)(wSeg | 1), dwOffset, TRUE ); if ( lp == NULL ) return( TRUE ); b = ReadProcessMemory( hProcess, lp, lpitem, nSize, &dwBytes ); if ( !b || dwBytes != nSize ) return( TRUE ); return( FALSE ); } //---------------------------------------------------------------------------- // WriteItem // // Internal routine used to write items into the debugee's address space. // The routine returns TRUE for failure. This allows easy failure testing. // //---------------------------------------------------------------------------- BOOL WriteItem( HANDLE hProcess, WORD wSeg, DWORD dwOffset, LPVOID lpitem, UINT nSize ) { LPVOID lp; BOOL b; DWORD dwBytes; if ( nSize == 0 ) { return( FALSE ); } lp = (LPVOID)InternalGetPointer( hProcess, (WORD)(wSeg | 1), dwOffset, TRUE ); if ( lp == NULL ) return( TRUE ); b = WriteProcessMemory( hProcess, lp, lpitem, nSize, &dwBytes ); if ( !b || dwBytes != nSize ) return( TRUE ); return( FALSE ); } BOOL CallRemote16( HANDLE hProcess, LPSTR lpModuleName, LPSTR lpEntryName, LPBYTE lpArgs, WORD wArgsPassed, WORD wArgsSize, LPDWORD lpdwReturnValue, DEBUGEVENTPROC lpEventProc, LPVOID lpData ) { HANDLE hRemoteThread; DWORD dwThreadId; DWORD dwContinueCode; DEBUG_EVENT de; BOOL b; BOOL fContinue; COM_HEADER comhead; WORD wRemoteSeg; WORD wRemoteOff; WORD wOff; UINT uModuleLength; UINT uEntryLength; if ( lpRemoteAddress == NULL || lpRemoteBlock == 0 ) { #ifdef DEBUG OutputDebugString("Remote address or remote block not initialized\n"); #endif return( FALSE ); } wRemoteSeg = HIWORD(lpRemoteBlock); wRemoteOff = LOWORD(lpRemoteBlock); wOff = wRemoteOff; // Fill in the communications buffer header READ_FIXED_ITEM( wRemoteSeg, wOff, comhead ); comhead.wArgsPassed = wArgsPassed; comhead.wArgsSize = wArgsSize; uModuleLength = strlen(lpModuleName) + 1; uEntryLength = strlen(lpEntryName) + 1; // // If this call won't fit into the buffer, then fail. // if ( (UINT)comhead.wBlockLength < sizeof(comhead) + wArgsSize + uModuleLength + uEntryLength ) { #ifdef DEBUG OutputDebugString("Block won't fit\n"); #endif return( FALSE ); } WRITE_FIXED_ITEM( wRemoteSeg, wOff, comhead ); wOff += sizeof(comhead); // Fill in the communications buffer arguments WRITE_SIZED_ITEM( wRemoteSeg, wOff, lpArgs, wArgsSize ); wOff += wArgsSize; // Fill in the communications buffer module name and entry name WRITE_SIZED_ITEM( wRemoteSeg, wOff, lpModuleName, uModuleLength ); wOff += (WORD) uModuleLength; WRITE_SIZED_ITEM( wRemoteSeg, wOff, lpEntryName, uEntryLength ); wOff += (WORD) uEntryLength; hRemoteThread = CreateRemoteThread( hProcess, NULL, (DWORD)0, lpRemoteAddress, NULL, 0, &dwThreadId ); if ( hRemoteThread == (HANDLE)0 ) { // Fail if we couldn't creaet thrd #ifdef DEBUG OutputDebugString("CreateRemoteThread failed\n"); #endif return( FALSE ); } // // Wait for the EXIT_THREAD_DEBUG_EVENT. // fContinue = TRUE; while ( fContinue ) { b = WaitForDebugEvent( &de, LONG_TIMEOUT ); if (!b) { TerminateThread( hRemoteThread, 0 ); CloseHandle( hRemoteThread ); return( FALSE ); } if ( de.dwThreadId == dwThreadId && de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT ) { fContinue = FALSE; } if ( lpEventProc ) { dwContinueCode = (* lpEventProc)( &de, lpData ); } else { dwContinueCode = DBG_CONTINUE; } ContinueDebugEvent( de.dwProcessId, de.dwThreadId, dwContinueCode ); } b = WaitForSingleObject( hRemoteThread, LONG_TIMEOUT ); CloseHandle( hRemoteThread ); if (b) { #ifdef DEBUG OutputDebugString("Wait for remote thread failed\n"); #endif return( FALSE ); } // // Get the return value and returned arguments // wOff = wRemoteOff; READ_FIXED_ITEM( wRemoteSeg, wOff, comhead ); wOff += sizeof(comhead); *lpdwReturnValue = comhead.dwReturnValue; // Read back the communications buffer arguments READ_SIZED_ITEM( wRemoteSeg, wOff, lpArgs, wArgsSize ); return( comhead.wSuccess ); punt: return( FALSE ); } DWORD GetRemoteBlock16( VOID ) { if ( lpRemoteBlock == 0 ) { return( 0 ); } return( ((DWORD)lpRemoteBlock) + sizeof(COM_HEADER) ); } VOID ProcessInitNotification( LPDEBUG_EVENT lpDebugEvent ) { VDMINTERNALINFO viInfo; DWORD lpNumberOfBytesRead; HANDLE hProcess; BOOL b; LPDWORD lpdw; lpdw = &(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); hProcess = OpenProcess( PROCESS_VM_READ, FALSE, lpDebugEvent->dwProcessId ); if ( hProcess == HANDLE_NULL ) { return; } b = ReadProcessMemory(hProcess, (LPVOID)lpdw[3], &viInfo, sizeof(viInfo), &lpNumberOfBytesRead ); if ( !b || lpNumberOfBytesRead != sizeof(viInfo) ) { return; } if ( wKernelSeg == 0 ) { wKernelSeg = viInfo.wKernelSeg; dwOffsetTHHOOK = viInfo.dwOffsetTHHOOK; } if ( lpRemoteAddress == NULL ) { lpRemoteAddress = viInfo.lpRemoteAddress; } if ( lpRemoteBlock == 0 ) { lpRemoteBlock = viInfo.lpRemoteBlock; } dwLdtBase = viInfo.dwLdtBase; dwIntelBase = viInfo.dwIntelBase; fKernel386 = viInfo.f386; lpNtvdmState = viInfo.lpNtvdmState; lpVdmDbgFlags = viInfo.lpVdmDbgFlags; lpVdmContext = viInfo.vdmContext; lpNtCpuInfo = viInfo.lpNtCpuInfo; lpVdmBreakPoints = viInfo.lpVdmBreakPoints; CloseHandle( hProcess ); } VOID ParseModuleName( LPSTR szName, LPSTR szPath ) /*++ Routine Description: This routine strips off the 8 character file name from a path Arguments: szName - pointer to buffer of 8 characters (plus null) szPath - full path of file Return Value None. --*/ { LPSTR lPtr = szPath; LPSTR lDest = szName; int BufferSize = 9; while(*lPtr) lPtr++; // scan to end while( ((DWORD)lPtr > (DWORD)szPath) && ((*lPtr != '\\') && (*lPtr != '/'))) lPtr--; if (*lPtr) lPtr++; while((*lPtr) && (*lPtr!='.')) { if (!--BufferSize) break; *lDest++ = *lPtr++; } *lDest = 0; } #ifndef i386 WORD ReadWord( HANDLE hProcess, PVOID lpAddress ) { NTSTATUS bResult; WORD value; ULONG NumberOfBytesRead; bResult = ReadProcessMemory( hProcess, lpAddress, &value, sizeof(WORD), &NumberOfBytesRead ); return value; } DWORD ReadDword( HANDLE hProcess, PVOID lpAddress ) { NTSTATUS bResult; DWORD value; ULONG NumberOfBytesRead; bResult = ReadProcessMemory( hProcess, lpAddress, &value, sizeof(DWORD), &NumberOfBytesRead ); return value; } // // The following two routines implement the very funky way that we // have to get register values on the 486 emulator. // ULONG GetRegValue( HANDLE hProcess, NT_CPU_REG reg, BOOL bInNano, ULONG UMask ) { if (bInNano) { return(ReadDword(hProcess, reg.nano_reg)); } else if (UMask & reg.universe_8bit_mask) { return (ReadDword(hProcess, reg.saved_reg) & 0xFFFFFF00 | ReadDword(hProcess, reg.reg) & 0xFF); } else if (UMask & reg.universe_16bit_mask) { return (ReadDword(hProcess, reg.saved_reg) & 0xFFFF0000 | ReadDword(hProcess, reg.reg) & 0xFFFF); } else { return (ReadDword(hProcess, reg.reg)); } } ULONG GetEspValue( HANDLE hProcess, NT_CPU_INFO nt_cpu_info, BOOL bInNano ) { if (bInNano) { return (ReadDword(hProcess, nt_cpu_info.nano_esp)); } else { if (ReadDword(hProcess, nt_cpu_info.stack_is_big)) { return (ReadDword(hProcess, nt_cpu_info.host_sp) - ReadDword(hProcess, nt_cpu_info.ss_base)); } else { return (ReadDword(hProcess, nt_cpu_info.esp_sanctuary) & 0xFFFF0000 | (ReadDword(hProcess, nt_cpu_info.host_sp) - ReadDword(hProcess, nt_cpu_info.ss_base) & 0xFFFF)); } } } #endif