590 lines
13 KiB
C
590 lines
13 KiB
C
/*++
|
|
|
|
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 <precomp.h>
|
|
#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
|