From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/mvdm/vdmdbg/makefile | 6 + private/mvdm/vdmdbg/sources | 81 ++ private/mvdm/vdmdbg/vdmdbg.c | 2299 ++++++++++++++++++++++++++++++++++++++++ private/mvdm/vdmdbg/vdmdbg.def | 24 + private/mvdm/vdmdbg/vdmdbg.rc | 10 + private/mvdm/vdmdbg/vdmdbg.txt | 1071 +++++++++++++++++++ 6 files changed, 3491 insertions(+) create mode 100644 private/mvdm/vdmdbg/makefile create mode 100644 private/mvdm/vdmdbg/sources create mode 100644 private/mvdm/vdmdbg/vdmdbg.c create mode 100644 private/mvdm/vdmdbg/vdmdbg.def create mode 100644 private/mvdm/vdmdbg/vdmdbg.rc create mode 100644 private/mvdm/vdmdbg/vdmdbg.txt (limited to 'private/mvdm/vdmdbg') diff --git a/private/mvdm/vdmdbg/makefile b/private/mvdm/vdmdbg/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/mvdm/vdmdbg/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/mvdm/vdmdbg/sources b/private/mvdm/vdmdbg/sources new file mode 100644 index 000000000..41591cd6e --- /dev/null +++ b/private/mvdm/vdmdbg/sources @@ -0,0 +1,81 @@ +# +# The MAJORCOMP and MINORCOMP variables are defined +# so that $(MAJORCOMP)$(MINORCOMP)filename can be used in +# cross compiling to provide unique filenames in a flat namespace. +# + +MAJORCOMP=mvdm +MINORCOMP=vdmdbg + +# +# The TARGETNAME variable is defined by the developer. It is the name of +# the target (component) that is being built by this makefile. It +# should NOT include any path or file extension information. +# + +TARGETNAME=vdmdbg + +DLLBASE=0x7A000000 + +# +# The TARGETPATH and TARGETTYPE variables are defined by the developer. +# The first specifies where the target is to be build. The second specifies +# the type of target (either PROGRAM, DYNLINK, LIBRARY, UMAPPL_NOLIB or +# BOOTPGM). UMAPPL_NOLIB is used when you're only building user-mode +# apps and don't need to build a library. +# + +TARGETPATH=\nt\public\sdk\lib + +# Pick one of the following and delete the others +TARGETTYPE=DYNLINK + +# +# The TARGETLIBS specifies additional libraries to link with you target +# image. Each library path specification should contain an asterisk (*) +# where the machine specific subdirectory name should go. +# + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\user32.lib + +# +# The INCLUDES variable specifies any include paths that are specific to +# this source directory. Separate multiple directory paths with single +# semicolons. Relative path specifications are okay. +# + +INCLUDES=..\inc;..\..\inc + + +# +# The SOURCES variable is defined by the developer. It is a list of all the +# source files for this component. Each source file should be on a separate +# line using the line continuation character. This will minimize merge +# conflicts if two developers adding source files to the same component. +# + +SOURCES=vdmdbg.c vdmdbg.rc + + +# +# Next specify options for the compiler. +# + +C_DEFINES=-DWIN32 -DNT + + +# +# Next specify a kernel mode test (valid only in NTOS tree) +# + +NTTEST= + +# +# Next specify one or more user mode test programs and their type +# UMTEST is used for optional test programs. UMAPPL is used for +# programs that always get built when the directory is built. +# + +# diff --git a/private/mvdm/vdmdbg/vdmdbg.c b/private/mvdm/vdmdbg/vdmdbg.c new file mode 100644 index 000000000..c4a9def1d --- /dev/null +++ b/private/mvdm/vdmdbg/vdmdbg.c @@ -0,0 +1,2299 @@ +/*++ + +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: + +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef WORD HAND16; + +#define SHAREWOW_MAIN +#include + +#if DBG +#define DEBUG 1 +#endif + +#define TOOL_HMASTER 0 // Offset to hGlobalHeap (in kdata.asm) +#define TOOL_HMODFIRST 4 // Offset to hExeHead (in kdata.asm) +#define TOOL_HEADTDB 14 // Offset to headTDB (in kdata.asm) +#define TOOL_HMASTLEN 22 // Offset to SelTableLen (in kdata.asm) +#define TOOL_HMASTSTART 24 // Offset to SelTableStart (in kdata.asm) + +#define HI_FIRST 6 // Offset to hi_first in heap header +#define HI_SIZE 24 // Size of HeapInfo structure + +#define GI_LRUCHAIN 2 // Offset to gi_lruchain in heap header +#define GI_LRUCOUNT 4 // Offset to gi_lrucount in heap header +#define GI_FREECOUNT 16 // Offset to gi_free_count in heap header + +#define GA_COUNT 0 // Offset to ga_count in arena header +#define GA_OWNER386 18 // Offset to "pga_owner member in globalarena + +#define GA_OWNER 1 // Offset to "owner" member within Arena + +#define GA_FLAGS 5 // Offset to ga_flags in arena header +#define GA_NEXT 9 // Offset to ga_next in arena header +#define GA_HANDLE 10 // Offset to ga_handle in arena header +#define GA_LRUNEXT 14 // Offset to ga_lrunext in arena header +#define GA_FREENEXT GA_LRUNEXT // Offset to ga_freenext in arena header + +#define GA_SIZE 16 // Size of the GlobalArena structure + +#define LI_SIG HI_SIZE+10 // Offset to signature +#define LI_SIZE HI_SIZE+12 // Size of LocalInfo structure +#define LOCALSIG 0x4C48 // 'HL' Signature + +#define TDB_next 0 // Offset to next TDB in TDB +#define TDB_PDB 72 // Offset to PDB in TDB + +#define GF_PDB_OWNER 0x100 // Low byte is kernel flags + +#define NEMAGIC 0x454E // 'NE' Signature + +#define NE_MAGIC 0 // Offset to NE in module header +#define NE_USAGE 2 // Offset to usage +#define NE_CBENTTAB 6 // Offset to cbenttab (really next module ptr) +#define NE_PATHOFFSET 10 // Offset to file path stuff +#define NE_CSEG 28 // Offset to cseg, number of segs in module +#define NE_SEGTAB 34 // Offset to segment table ptr in modhdr +#define NE_RESTAB 38 // Offset to resident names table ptr in modhdr + +#define NS_HANDLE 8 // Offset to handle in seg table +#define NEW_SEG1_SIZE 10 // Size of the NS_ stuff + + +#define MAX_MODULE_NAME_LENGTH 128 +#define MAX_MODULE_PATH_LENGTH 128 + +WORD wKernelSeg = 0; +DWORD dwOffsetTHHOOK = 0L; +LPVOID lpRemoteAddress = NULL; +DWORD lpRemoteBlock = 0; +BOOL fKernel386 = FALSE; + +#define HANDLE_NULL ((HANDLE)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, + HANDLE hThread, + WORD wSelector, + LPVDMLDT_ENTRY lpSelectorEntry +) { +#ifdef i386 + + // Do the nice simple thing for x86 systems. + + return( GetThreadSelectorEntry(hThread,wSelector,lpSelectorEntry) ); +#else + + // For non-intel systems, query the information from the LDT and + // GDT that we have pointers to from the VDMINTERNALINFO that we + // got passed. + + + RtlFillMemory( lpSelectorEntry, sizeof(VDMLDT_ENTRY), (UCHAR)0 ); + + // BUGBUG - Implement a method of determining the LDT on MIPS/ALPHA + // BUGBUG - Also adjust the base value of the selector to account for + // Intel M Memory not based at 0. + + return( FALSE ); +#endif +} + +//---------------------------------------------------------------------------- +// VDMGetThreadSelectorEntry() +// +// Public interface to the InternalGetThreadSelectorEntry, needed because +// that routine requires the process handle. +// +//---------------------------------------------------------------------------- +BOOL +WINAPI +VDMGetThreadSelectorEntry( + HANDLE hProcess, + HANDLE hThread, + WORD wSelector, + LPVDMLDT_ENTRY lpSelectorEntry +) { + BOOL fResult; + + fResult = InternalGetThreadSelectorEntry( + hProcess, + hThread, + wSelector, + lpSelectorEntry ); + + return( fResult ); +} + +//---------------------------------------------------------------------------- +// 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 +WINAPI +InternalGetPointer( + HANDLE hProcess, + HANDLE hThread, + WORD wSelector, + DWORD dwOffset, + BOOL fProtMode +) { + VDMLDT_ENTRY le; + ULONG ulResult; + ULONG base; + ULONG limit; + BOOL b; + + if ( fProtMode ) { + b = InternalGetThreadSelectorEntry( hProcess, + hThread, + 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 + // BUGBUG this should be the start of intel memory + ulResult += 0; +#endif + } + + return( ulResult ); +} + +//---------------------------------------------------------------------------- +// VDMGetPointer() +// +// Public interface to the InternalGetPointer, needed because that +// routine requires the process handle. +// +//---------------------------------------------------------------------------- +ULONG +WINAPI +VDMGetPointer( + HANDLE hProcess, + HANDLE hThread, + WORD wSelector, + DWORD dwOffset, + BOOL fProtMode +) { + ULONG ulResult; + + ulResult = InternalGetPointer( + hProcess, + hThread, + wSelector, + dwOffset, + fProtMode ); + + return( ulResult ); +} + +//---------------------------------------------------------------------------- +// VDMGetThreadContext() +// +// 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 +VDMGetThreadContext( + LPDEBUG_EVENT lpDebugEvent, + LPVDMCONTEXT lpVDMContext +) { + VDMINTERNALINFO viInfo; + VDMCONTEXT vcContext; + LPDWORD lpdw; + DWORD address; + BOOL b; + DWORD lpNumberOfBytesRead; + HANDLE hProcess; + INT i; + + hProcess = OpenProcess( PROCESS_VM_READ, FALSE, lpDebugEvent->dwProcessId ); + + lpdw = &(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); + + address = lpdw[3]; + + b = ReadProcessMemory( + hProcess, + (LPVOID)address, + &viInfo, + sizeof(viInfo), + &lpNumberOfBytesRead + ); + if ( !b || lpNumberOfBytesRead != sizeof(viInfo) ) { + return( FALSE ); + } + + address = (DWORD)viInfo.vdmContext; + + b = ReadProcessMemory( + hProcess, + (LPVOID)address, + &vcContext, + sizeof(vcContext), + &lpNumberOfBytesRead + ); + if ( !b || lpNumberOfBytesRead != sizeof(vcContext) ) { + return( FALSE ); + } + + CloseHandle( hProcess ); + + 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; + } + + return( TRUE ); +} + +//---------------------------------------------------------------------------- +// VDMSetThreadContext() +// +// 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 +VDMSetThreadContext( + LPDEBUG_EVENT lpDebugEvent, + LPVDMCONTEXT lpVDMContext +) { + VDMINTERNALINFO viInfo; + VDMCONTEXT vcContext; + LPDWORD lpdw; + DWORD address; + BOOL b; + DWORD lpNumberOfBytes; + HANDLE hProcess; + INT i; + + + hProcess = OpenProcess( PROCESS_VM_OPERATION | + PROCESS_VM_READ | + PROCESS_VM_WRITE, + FALSE, + lpDebugEvent->dwProcessId ); + + lpdw = &(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); + + address = lpdw[3]; + + b = ReadProcessMemory( + hProcess, + (LPVOID)address, + &viInfo, + sizeof(viInfo), + &lpNumberOfBytes + ); + if ( !b || lpNumberOfBytes != sizeof(viInfo) ) { + return( FALSE ); + } + + address = (DWORD)viInfo.vdmContext; + + b = ReadProcessMemory( + hProcess, + (LPVOID)address, + &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; + } + b = WriteProcessMemory( + hProcess, + (LPVOID)address, + &vcContext, + sizeof(vcContext), + &lpNumberOfBytes + ); + + if ( !b || lpNumberOfBytes != sizeof(vcContext) ) { + return( FALSE ); + } + + CloseHandle( hProcess ); + + return( TRUE ); +} + +//---------------------------------------------------------------------------- +// VDMKillWOW() +// +// Interface to kill the wow sub-system. This may not be needed and is +// certainly not needed now. We are going to look into fixing the +// debugging interface so this is not necessary. +// +//---------------------------------------------------------------------------- +BOOL +WINAPI +VDMKillWOW( + VOID +) { + return( FALSE ); +} + +//---------------------------------------------------------------------------- +// VDMDetectWOW() +// +// Interface to detect whether the wow sub-system has already been started. +// This may not be needed and is certainly not needed now. We are going +// to look into fixing the debugging interface so this is not necessary. +// +//---------------------------------------------------------------------------- +BOOL +WINAPI +VDMDetectWOW( + VOID +) { + return( FALSE ); +} + +//---------------------------------------------------------------------------- +// 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; + + lpdw = &(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0]); + + + mode = LOWORD(lpdw[0]); + + fResult = TRUE; + + switch( mode ) { + case DBG_SEGLOAD: + case DBG_SEGMOVE: + case DBG_SEGFREE: + case DBG_MODLOAD: + case DBG_MODFREE: + fResult = FALSE; + case DBG_SINGLESTEP: + case DBG_BREAK: + case DBG_GPFAULT: + case DBG_DIVOVERFLOW: + case DBG_INSTRFAULT: + case DBG_TASKSTART: + case DBG_TASKSTOP: + case DBG_DLLSTART: + case DBG_DLLSTOP: + default: + if ( wKernelSeg == 0 || lpRemoteAddress == 0 ) { + VDMINTERNALINFO viInfo; + DWORD address; + DWORD lpNumberOfBytesRead; + HANDLE hProcess; + BOOL b; + + hProcess = OpenProcess( PROCESS_VM_READ, FALSE, lpDebugEvent->dwProcessId ); + + if ( hProcess == HANDLE_NULL ) { + fResult = FALSE; + break; + } + address = lpdw[3]; + + b = ReadProcessMemory( + hProcess, + (LPVOID)address, + &viInfo, + sizeof(viInfo), + &lpNumberOfBytesRead + ); + if ( !b || lpNumberOfBytesRead != sizeof(viInfo) ) { + fResult = FALSE; + break; + + } + + if ( wKernelSeg == 0 ) { + wKernelSeg = viInfo.wKernelSeg; + dwOffsetTHHOOK = viInfo.dwOffsetTHHOOK; + } + if ( lpRemoteAddress == NULL ) { + lpRemoteAddress = viInfo.lpRemoteAddress; + } + if ( lpRemoteBlock == 0 ) { + lpRemoteBlock = viInfo.lpRemoteBlock; + } + fKernel386 = viInfo.f386; + + CloseHandle( hProcess ); + } + break; + } + + return( fResult ); +} + + +//---------------------------------------------------------------------------- +// 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, + HANDLE hThread, + WORD wSeg, + DWORD dwOffset, + LPVOID lpitem, + UINT nSize +) { + LPVOID lp; + BOOL b; + DWORD dwBytes; + + if ( nSize == 0 ) { + return( FALSE ); + } + + lp = (LPVOID)InternalGetPointer( + hProcess, + hThread, + (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, + HANDLE hThread, + WORD wSeg, + DWORD dwOffset, + LPVOID lpitem, + UINT nSize +) { + LPVOID lp; + BOOL b; + DWORD dwBytes; + + if ( nSize == 0 ) { + return( FALSE ); + } + + lp = (LPVOID)InternalGetPointer( + hProcess, + hThread, + (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 ); +} + +#define READ_FIXED_ITEM(seg,offset,item) \ + if ( ReadItem(hProcess,hThread,seg,offset,&item,sizeof(item)) ) goto punt; + +#define WRITE_FIXED_ITEM(seg,offset,item) \ + if ( WriteItem(hProcess,hThread,seg,offset,&item,sizeof(item)) ) goto punt; + +#define LOAD_FIXED_ITEM(seg,offset,item) \ + ReadItem(hProcess,hThread,seg,offset,&item,sizeof(item)) + +#define READ_SIZED_ITEM(seg,offset,item,size) \ + if ( ReadItem(hProcess,hThread,seg,offset,item,size) ) goto punt; + +#define WRITE_SIZED_ITEM(seg,offset,item,size) \ + if ( WriteItem(hProcess,hThread,seg,offset,item,size) ) goto punt; + +//---------------------------------------------------------------------------- +// 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 hThread, + 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]; + + if ( lpModuleName != NULL ) *lpModuleName = '\0'; + if ( lpModulePath != NULL ) *lpModulePath = '\0'; + if ( lpSegmentNumber != NULL ) *lpSegmentNumber = 0; + + fResult = FALSE; + + if ( wKernelSeg == 0 ) { + return( FALSE ); + } + + // Read out the master heap selector + + lphMaster = InternalGetPointer( + hProcess, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + wMaster, + dwArenaOffset+GA_OWNER386, + TRUE ); + if ( lpOwner == (DWORD)NULL ) goto punt; + + } else { + lpOwner = InternalGetPointer( + hProcess, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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: + 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 hThread, + 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]; + + *lpSelector = 0; + + fResult = FALSE; + + if ( wKernelSeg == 0 ) { + return( FALSE ); + } + + lpModuleHead = InternalGetPointer( + hProcess, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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, + hThread, + 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: + return( fResult ); +} + +#define LONG_TIMEOUT INFINITE + +BOOL VDMCallRemote16( + HANDLE hProcess, + HANDLE hThread, + 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 += uModuleLength; + + WRITE_SIZED_ITEM( wRemoteSeg, wOff, lpEntryName, uEntryLength ); + wOff += 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 +WINAPI +VDMGetRemoteBlock16( + HANDLE hProcess, + HANDLE hThread +) { + if ( lpRemoteBlock == 0 ) { + return( 0 ); + } + return( ((DWORD)lpRemoteBlock) + sizeof(COM_HEADER) ); +} + + +typedef struct { + DWORD dwSize; + DWORD dwAddress; + DWORD dwBlockSize; + WORD hBlock; + WORD wcLock; + WORD wcPageLock; + WORD wFlags; + WORD wHeapPresent; + WORD hOwner; + WORD wType; + WORD wData; + DWORD dwNext; + DWORD dwNextAlt; +} GLOBALENTRY16, *LPGLOBALENTRY16; + +VOID CopyToGlobalEntry16( + LPGLOBALENTRY lpGlobalEntry, + LPGLOBALENTRY16 lpGlobalEntry16 +) { + if ( lpGlobalEntry == NULL || lpGlobalEntry16 == NULL ) { + return; + } + lpGlobalEntry16->dwSize = sizeof(GLOBALENTRY16); + lpGlobalEntry16->dwAddress = lpGlobalEntry->dwAddress; + lpGlobalEntry16->dwBlockSize = lpGlobalEntry->dwBlockSize; + lpGlobalEntry16->hBlock = (WORD)lpGlobalEntry->hBlock; + lpGlobalEntry16->wcLock = lpGlobalEntry->wcLock; + lpGlobalEntry16->wcPageLock = lpGlobalEntry->wcPageLock; + lpGlobalEntry16->wFlags = lpGlobalEntry->wFlags; + lpGlobalEntry16->wHeapPresent = lpGlobalEntry->wHeapPresent; + lpGlobalEntry16->hOwner = (WORD)lpGlobalEntry->hOwner; + lpGlobalEntry16->wType = lpGlobalEntry->wType; + lpGlobalEntry16->wData = lpGlobalEntry->wData; + lpGlobalEntry16->dwNext = lpGlobalEntry->dwNext; + lpGlobalEntry16->dwNextAlt = lpGlobalEntry->dwNextAlt; +} + +VOID CopyFromGlobalEntry16( + LPGLOBALENTRY lpGlobalEntry, + LPGLOBALENTRY16 lpGlobalEntry16 +) { + if ( lpGlobalEntry == NULL || lpGlobalEntry16 == NULL ) { + return; + } + lpGlobalEntry->dwSize = sizeof(GLOBALENTRY); + lpGlobalEntry->dwAddress = lpGlobalEntry16->dwAddress; + lpGlobalEntry->dwBlockSize = lpGlobalEntry16->dwBlockSize; + lpGlobalEntry->hBlock = (HANDLE)lpGlobalEntry16->hBlock; + lpGlobalEntry->wcLock = lpGlobalEntry16->wcLock; + lpGlobalEntry->wcPageLock = lpGlobalEntry16->wcPageLock; + lpGlobalEntry->wFlags = lpGlobalEntry16->wFlags; + lpGlobalEntry->wHeapPresent = lpGlobalEntry16->wHeapPresent; + lpGlobalEntry->hOwner = (HANDLE)lpGlobalEntry16->hOwner; + lpGlobalEntry->wType = lpGlobalEntry16->wType; + lpGlobalEntry->wData = lpGlobalEntry16->wData; + lpGlobalEntry->dwNext = lpGlobalEntry16->dwNext; + lpGlobalEntry->dwNextAlt = lpGlobalEntry16->dwNextAlt; +} + + +BOOL +WINAPI +VDMGlobalFirst( + HANDLE hProcess, + HANDLE hThread, + LPGLOBALENTRY lpGlobalEntry, + WORD wFlags, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData +) { +#define GF_SIZE 6 // 6 bytes are passed to GlobalFirst + BYTE Args[GF_SIZE+sizeof(GLOBALENTRY16)]; + LPBYTE lpbyte; + DWORD vpBuff; + DWORD dwResult; + BOOL b; + + if ( lpGlobalEntry->dwSize != sizeof(GLOBALENTRY) ) { + return( FALSE ); + } + + vpBuff = VDMGetRemoteBlock16( hProcess, hThread ); + vpBuff += GF_SIZE; + + lpbyte = Args; + + // Push the flags + (*(LPWORD)lpbyte) = wFlags; + lpbyte += sizeof(WORD); + + // Push the pointer to the pointer to the GLOBALENTRY16 structure + (*(LPWORD)lpbyte) = LOWORD(vpBuff); + lpbyte += sizeof(WORD); + + (*(LPWORD)lpbyte) = HIWORD(vpBuff); + lpbyte += sizeof(WORD); + + CopyToGlobalEntry16( lpGlobalEntry, (LPGLOBALENTRY16)lpbyte ); + + b = VDMCallRemote16( + hProcess, + hThread, + "TOOLHELP.DLL", + "GlobalFirst", + Args, + GF_SIZE, + sizeof(Args), + &dwResult, + lpEventProc, + lpData ); + + if ( !b ) { + return( FALSE ); + } + CopyFromGlobalEntry16( lpGlobalEntry, (LPGLOBALENTRY16)lpbyte ); + + + return( (BOOL)((WORD)dwResult) ); + +punt: + return( FALSE ); +} + + +BOOL +WINAPI +VDMGlobalNext( + HANDLE hProcess, + HANDLE hThread, + LPGLOBALENTRY lpGlobalEntry, + WORD wFlags, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData +) { +#define GN_SIZE 6 // 6 bytes are passed to GlobalNext + BYTE Args[GN_SIZE+sizeof(GLOBALENTRY16)]; + LPBYTE lpbyte; + DWORD vpBuff; + DWORD dwResult; + BOOL b; + + if ( lpGlobalEntry->dwSize != sizeof(GLOBALENTRY) ) { + return( FALSE ); + } + + vpBuff = VDMGetRemoteBlock16( hProcess, hThread ); + vpBuff += GN_SIZE; + + lpbyte = Args; + + // Push the flags + (*(LPWORD)lpbyte) = wFlags; + lpbyte += sizeof(WORD); + + // Push the pointer to the pointer to the GLOBALENTRY16 structure + (*(LPWORD)lpbyte) = LOWORD(vpBuff); + lpbyte += sizeof(WORD); + + (*(LPWORD)lpbyte) = HIWORD(vpBuff); + lpbyte += sizeof(WORD); + + CopyToGlobalEntry16( lpGlobalEntry, (LPGLOBALENTRY16)lpbyte ); + + b = VDMCallRemote16( + hProcess, + hThread, + "TOOLHELP.DLL", + "GlobalNext", + Args, + GN_SIZE, + sizeof(Args), + &dwResult, + lpEventProc, + lpData ); + + if ( !b ) { + return( FALSE ); + } + CopyFromGlobalEntry16( lpGlobalEntry, (LPGLOBALENTRY16)lpbyte ); + + return( (BOOL)((WORD)dwResult) ); + +punt: + return( FALSE ); +} + +#pragma pack(2) +typedef struct { + DWORD dwSize; + char szModule[MAX_MODULE_NAME+1]; + WORD hModule; + WORD wcUsage; + char szExePath[MAX_PATH16+1]; + WORD wNext; +} MODULEENTRY16, *LPMODULEENTRY16; +#pragma pack() + +VOID CopyToModuleEntry16( + LPMODULEENTRY lpModuleEntry, + LPMODULEENTRY16 lpModuleEntry16 +) { + if ( lpModuleEntry == NULL || lpModuleEntry16 == NULL ) { + return; + } + lpModuleEntry16->dwSize = sizeof(MODULEENTRY16); + lpModuleEntry16->hModule = (WORD)lpModuleEntry->hModule; + lpModuleEntry16->wcUsage = lpModuleEntry->wcUsage; + lpModuleEntry16->wNext = lpModuleEntry->wNext; + strncpy( lpModuleEntry16->szModule, lpModuleEntry->szModule, MAX_MODULE_NAME ); + strncpy( lpModuleEntry16->szExePath, lpModuleEntry->szExePath, MAX_PATH16 ); +} + +VOID CopyFromModuleEntry16( + LPMODULEENTRY lpModuleEntry, + LPMODULEENTRY16 lpModuleEntry16 +) { + if ( lpModuleEntry == NULL || lpModuleEntry16 == NULL ) { + return; + } + lpModuleEntry->dwSize = sizeof(MODULEENTRY); + lpModuleEntry->hModule = (HANDLE)lpModuleEntry16->hModule; + lpModuleEntry->wcUsage = lpModuleEntry16->wcUsage; + lpModuleEntry->wNext = lpModuleEntry16->wNext; + strncpy( lpModuleEntry->szModule, lpModuleEntry16->szModule, MAX_MODULE_NAME ); + strncpy( lpModuleEntry->szExePath, lpModuleEntry16->szExePath, MAX_PATH16 ); +} + +BOOL +WINAPI +VDMModuleFirst( + HANDLE hProcess, + HANDLE hThread, + LPMODULEENTRY lpModuleEntry, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData +) { +#define MF_SIZE 4 // 4 bytes are passed to ModuleFirst + BYTE Args[GF_SIZE+sizeof(MODULEENTRY16)]; + LPBYTE lpbyte; + DWORD vpBuff; + DWORD dwResult; + BOOL b; + + if ( lpModuleEntry->dwSize != sizeof(MODULEENTRY) ) { + return( FALSE ); + } + + vpBuff = VDMGetRemoteBlock16( hProcess, hThread ); + vpBuff += MF_SIZE; + + lpbyte = Args; + + // Push the pointer to the pointer to the MODULEENTRY16 structure + (*(LPWORD)lpbyte) = LOWORD(vpBuff); + lpbyte += sizeof(WORD); + + (*(LPWORD)lpbyte) = HIWORD(vpBuff); + lpbyte += sizeof(WORD); + + CopyToModuleEntry16( lpModuleEntry, (LPMODULEENTRY16)lpbyte ); + + b = VDMCallRemote16( + hProcess, + hThread, + "TOOLHELP.DLL", + "ModuleFirst", + Args, + MF_SIZE, + sizeof(Args), + &dwResult, + lpEventProc, + lpData ); + + if ( !b ) { + return( FALSE ); + } + CopyFromModuleEntry16( lpModuleEntry, (LPMODULEENTRY16)lpbyte ); + + return( (BOOL)((WORD)dwResult) ); + +punt: + return( FALSE ); +} + +BOOL +WINAPI +VDMModuleNext( + HANDLE hProcess, + HANDLE hThread, + LPMODULEENTRY lpModuleEntry, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData +) { +#define MN_SIZE 4 // 4 bytes are passed to ModuleNext + BYTE Args[GF_SIZE+sizeof(MODULEENTRY16)]; + LPBYTE lpbyte; + DWORD vpBuff; + DWORD dwResult; + BOOL b; + + if ( lpModuleEntry->dwSize != sizeof(MODULEENTRY) ) { + return( FALSE ); + } + + vpBuff = VDMGetRemoteBlock16( hProcess, hThread ); + vpBuff += MN_SIZE; + + lpbyte = Args; + + // Push the pointer to the pointer to the MODULEENTRY16 structure + (*(LPWORD)lpbyte) = LOWORD(vpBuff); + lpbyte += sizeof(WORD); + + (*(LPWORD)lpbyte) = HIWORD(vpBuff); + lpbyte += sizeof(WORD); + + CopyToModuleEntry16( lpModuleEntry, (LPMODULEENTRY16)lpbyte ); + + b = VDMCallRemote16( + hProcess, + hThread, + "TOOLHELP.DLL", + "ModuleNext", + Args, + MN_SIZE, + sizeof(Args), + &dwResult, + lpEventProc, + lpData ); + + if ( !b ) { + return( FALSE ); + } + CopyFromModuleEntry16( lpModuleEntry, (LPMODULEENTRY16)lpbyte ); + + return( (BOOL)((WORD)dwResult) ); + +punt: + return( FALSE ); +} + +INT +WINAPI +VDMEnumProcessWOW( + PROCESSENUMPROC fp, + LPARAM lparam +) { + LPSHAREDTASKMEM lpstm; + LPSHAREDPROCESS lpsp; + DWORD dwOffset; + INT count; + BOOL f; + HANDLE hProcess; + + /* + ** Open the shared memory window + */ + lpstm = LOCKSHAREWOW(); + if ( lpstm == NULL ) { + // Wow must not be running + return( 0 ); + } + + // + // Now traverse through all of the processes in the + // list, calling the callback function for each. + // + count = 0; + dwOffset = lpstm->dwFirstProcess; + + while ( dwOffset != 0 ) { + lpsp = (LPSHAREDPROCESS)((CHAR *)lpstm + dwOffset); + + if ( lpsp->dwType != SMO_PROCESS ) { + // Some memory corruption problem + OutputDebugString("VDMDBG: Shared memory object is not a process? (memory corruption)\n"); + return( 0 ); + } + + // + // Make sure the process hasn't gone away because of a + // crash or other rude shutdown that prevents cleanup. + // + + hProcess = OpenProcess( + SYNCHRONIZE, + FALSE, + lpsp->dwProcessId + ); + + if (hProcess) { + + CloseHandle(hProcess); + + count++; + if ( fp ) { + f = (*fp)( lpsp->dwProcessId, lpsp->dwAttributes, lparam ); + if ( f ) { + UNLOCKSHAREWOW(); + return( count ); + } + } + + } else { + + // + // This is a ghost entry, change the process ID to zero + // so that the next WOW started will be sure to remove + // this entry even if the process ID is recycled. + // + + lpsp->dwProcessId = 0; + } + + dwOffset = lpsp->dwNextProcess; + } + + UNLOCKSHAREWOW(); + return( count ); +} + + +INT +WINAPI +VDMEnumTaskWOWWorker( + DWORD dwProcessId, + void * fp, + LPARAM lparam, + BOOL fEx +) { + LPSHAREDTASKMEM lpstm; + LPSHAREDPROCESS lpsp; + LPSHAREDTASK lpst; + DWORD dwOffset; + INT count = 0; + BOOL f; + + // + // Open the shared memory window + // + lpstm = LOCKSHAREWOW(); + if ( lpstm == NULL ) { + // Wow must not be running + return( 0 ); + } + + // + // Now traverse through all of the processes in the + // list, looking for the one with the proper id. + // + + dwOffset = lpstm->dwFirstProcess; + while ( dwOffset != 0 ) { + lpsp = (LPSHAREDPROCESS)((CHAR *)lpstm + dwOffset); + + if ( lpsp->dwType != SMO_PROCESS ) { + // Some memory corruption problem + OutputDebugString("VDMDBG: shared memory object is not a process? (memory corruption)\n"); + UNLOCKSHAREWOW(); + return( 0 ); + } + if ( lpsp->dwProcessId == dwProcessId ) { + break; + } + dwOffset = lpsp->dwNextProcess; + } + + if ( dwOffset == 0 ) { // We must not have found this Id. + UNLOCKSHAREWOW(); + return( 0 ); + } + + // + // Now enumerate all of the tasks for this process + // + dwOffset = lpsp->dwFirstTask; + while( dwOffset != 0 ) { + lpst = (LPSHAREDTASK)((CHAR *)lpstm + dwOffset ); + + if ( lpst->dwType != SMO_TASK ) { + // Some memory corruption problem + OutputDebugString("VDMDBG: shared memory object is not a task? (memory corruption)\n"); + UNLOCKSHAREWOW(); + return( 0 ); + } + count++; + if ( fp && lpst->hMod16 ) { + if (fEx) { + f = ((TASKENUMPROCEX)fp)( lpst->dwThreadId, lpst->hMod16, lpst->hTask16, + lpst->szModName, lpst->szFilePath, lparam ); + } else { + f = ((TASKENUMPROC)fp)( lpst->dwThreadId, lpst->hMod16, lpst->hTask16, lparam ); + } + if ( f ) { + UNLOCKSHAREWOW(); + return( count ); + } + } + dwOffset = lpst->dwNextTask; + } + + UNLOCKSHAREWOW(); + return( count ); +} + + +INT +WINAPI +VDMEnumTaskWOW( + DWORD dwProcessId, + TASKENUMPROC fp, + LPARAM lparam +) { + return VDMEnumTaskWOWWorker(dwProcessId, (void *)fp, lparam, 0); +} + + +INT +WINAPI +VDMEnumTaskWOWEx( + DWORD dwProcessId, + TASKENUMPROCEX fp, + LPARAM lparam +) { + return VDMEnumTaskWOWWorker(dwProcessId, (void *)fp, lparam, 1); +} + + +BOOL +WINAPI +VDMTerminateTaskWOW( + DWORD dwProcessId, + WORD htask +) +{ + BOOL fRet = FALSE; + LPSHAREDTASKMEM lpstm; + LPSHAREDPROCESS lpsp; + LPSHAREDTASK lpst; + DWORD dwOffset; + INT count; + HANDLE hProcess; + HANDLE hRemoteThread; + DWORD dwThreadId; + + // + // Open the shared memory window + // + lpstm = LOCKSHAREWOW(); + if ( lpstm == NULL ) { + // Wow must not be running + return( 0 ); + } + + // + // Now traverse through all of the processes in the + // list, looking for the one with the proper id. + // + + dwOffset = lpstm->dwFirstProcess; + while ( dwOffset != 0 ) { + lpsp = (LPSHAREDPROCESS)((CHAR *)lpstm + dwOffset); + + if ( lpsp->dwType != SMO_PROCESS ) { + // Some memory corruption problem + OutputDebugString("VDMDBG: shared memory object is not a process? (memory corruption)\n"); + goto UnlockReturn; + } + if ( lpsp->dwProcessId == dwProcessId ) { + break; + } + dwOffset = lpsp->dwNextProcess; + } + + if ( dwOffset == 0 ) { // We must not have found this Id. + goto UnlockReturn; + } + + // + // Get a handle to the process and start W32HungAppNotifyThread + // running with htask as the parameter. + // + + hProcess = OpenProcess( + PROCESS_ALL_ACCESS, + FALSE, + lpsp->dwProcessId + ); + + if (hProcess) { + + hRemoteThread = CreateRemoteThread( + hProcess, + NULL, + 0, + lpsp->pfnW32HungAppNotifyThread, + (LPVOID) htask, + 0, + &dwThreadId + ); + + if (hRemoteThread) { + fRet = TRUE; + CloseHandle(hRemoteThread); + } + + CloseHandle(hProcess); + } + + +UnlockReturn: + UNLOCKSHAREWOW(); + + return fRet; +} + + +BOOL +VDMStartTaskInWOW( + DWORD pidTarget, + LPSTR lpCommandLine, + WORD wShow +) +{ + HWND hwnd = NULL; + DWORD pid; + BOOL fRet; + + do { + + hwnd = FindWindowEx(NULL, hwnd, TEXT("WowExecClass"), NULL); + + if (hwnd) { + + pid = 0; + GetWindowThreadProcessId(hwnd, &pid); + } + + } while (hwnd && pid != pidTarget); + + + if (hwnd && pid == pidTarget) { + +#define WM_WOWEXEC_START_TASK (WM_USER+2) + PostMessage(hwnd, WM_WOWEXEC_START_TASK, GlobalAddAtom(lpCommandLine), wShow); + fRet = TRUE; + + } else { + + fRet = FALSE; + } + + return fRet; +} diff --git a/private/mvdm/vdmdbg/vdmdbg.def b/private/mvdm/vdmdbg/vdmdbg.def new file mode 100644 index 000000000..3f5acbd63 --- /dev/null +++ b/private/mvdm/vdmdbg/vdmdbg.def @@ -0,0 +1,24 @@ +LIBRARY VDMDBG + +DESCRIPTION 'VDM Debugging Support DLL' + +EXPORTS + VDMProcessException + VDMGetPointer + VDMGetThreadSelectorEntry + VDMGetThreadContext + VDMSetThreadContext + VDMKillWOW + VDMDetectWOW + VDMBreakThread + VDMGetSelectorModule + VDMGetModuleSelector + VDMModuleFirst + VDMModuleNext + VDMGlobalFirst + VDMGlobalNext + VDMEnumProcessWOW + VDMEnumTaskWOW + VDMEnumTaskWOWEx + VDMTerminateTaskWOW + VDMStartTaskInWOW diff --git a/private/mvdm/vdmdbg/vdmdbg.rc b/private/mvdm/vdmdbg/vdmdbg.rc new file mode 100644 index 000000000..b5fc7d0b4 --- /dev/null +++ b/private/mvdm/vdmdbg/vdmdbg.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "VDMDBG.DLL" +#define VER_INTERNALNAME_STR "VDMDBG\0" + +#include "common.ver" + diff --git a/private/mvdm/vdmdbg/vdmdbg.txt b/private/mvdm/vdmdbg/vdmdbg.txt new file mode 100644 index 000000000..5b2e11578 --- /dev/null +++ b/private/mvdm/vdmdbg/vdmdbg.txt @@ -0,0 +1,1071 @@ + + An Explanation of VDMDBG.DLL + + BobDay - 10 Jun 93 + +Section 1.0 - Overview +Section 2.0 - The Simulated Environment +Section 3.0 - The 32-bit Environment +Section 4.0 - Communication Protocol via Exception +Section 5.0 - The VDM Debugging APIs + +1.0 Overview + + This document describes the mechanism used to support debugging DOS and + WIN16 applications under Windows NT. The mechanism involves 2 parts + in the simulated environment, one for v86 mode (or simulated real mode) + and one for protected mode. It also involves 3 parts in the 32-bit NT + environment, one for fielding information from the simulated enviroment + and sending it to the debugger, one for code in the debugger, and one + for code in a DLL used by the debugger. + + 16-bit simulated environment 32-bit environment + + NTIO.SYS DBG.LIB (in NTVDM.EXE) + KRNL286.EXE NTSD (WinDbg or equivalent) + VDMDBG.DLL + + +2.0 Simulated enviroment + + Once the simulated environment is up and running, if one of the normal + debugging type events occurs it gets simulated to happen exactly the + way it would on a normal PC. This means that if an INT 3 occurs, it + pushes the flags, CS, IP, looks up the address in the IVT and then + jumps to it. Same goes for INT 1's and INT D's (GP Faults). If the + processor is in protected mode, these interrupts are normally trapped + by the DOS extender (most of the time DOSX.EXE) and then reflected + down into the real mode interrupts (except for GP Faults). + + In order to debug in the simulated enviroment, it is necessary to + catch and process these events. The simplest way to do this is to + install our own routines to watch these interrupts and that is + what NTIO.SYS and KRNL286.EXE do. They are nothing but small + stubs which insert themselves into the interrupt chains (NTIO.SYS + inserts itself into the real mode interrupt chain, and KRNL286.EXE + inserts itself into the protected mode interrupt chain). It is important + to install oneself as early as possible so that other programs (such + as DEBUG.COM) can install themselves ahead of these small stubs. + In this way the events are only detected when they are not handled by some + program in the simulated environment. + + Also, segment loading and unloading notifications are routed to + these small stubs so that the debugger can be notified when selectors + are associated with programs and their symbols. + + The small stubs will perform a BOP (a method of transitioning from + the simulated environment back to the real 32-bit NT environment and + notifying it that a debug event has occurred). After the BOP, they will + either perform an IRET instruction to return control back to the simulated + program generating the interrupt, or pass the interrupt down to the + previous interrupt handler (continue down the chain). The decision + whether to return from or pass the interrupt will be made on the basis + of the ContinueDebugEvent continue status parameter. DBG_CONTINUE will + IRET, DBG_EXCEPTION_NOT_HANDLE will pass it back down the chain. + +3.0 32-bit environment + + Once the 32-bit environment has been notified that a debug event has + occurred, it begins executing code in NTVDM (DBG.C) which parcels up the + register context (based on the type of event which occurred) and + communicates all the information to the debugger. + + Since the debugger is in another process's context, communication + is done through exceptions. NTVDM will raise an exception with the + status STATUS_VDM_EVENT. Exception information will be passed via the + "lpArguments" parameter to the API RaiseException(). + + The lpArguments parameter will cause an array of 4 DWORD values to + be passed to the debugger. The values of the meanings of the array + will be discussed in section 4.0 + + The debugger should receive this exception and return from the call + WaitForDebugEvent (debuggers should always have some thread waiting + for debug events). By examining the events dwDebugEventCode member, + the debugger can determine the type of the debug event. If this type + is EXCEPTION_DEBUG_EVENT, then the + + u.Exception.ExceptionRecord.ExceptionCode + + member of the debug event structure will contain the exception type. + If this value is STATUS_VDM_EVENT then the exception is coming from the + 16-bit environment. + + When an exception of this type is detected, a debugger should load (if + it hasn't done so already) the VDMDBG.DLL and determine the addresses + of the key functions needed. The key functions are: + + VDMProcessException + VDMGetThreadSelectorEntry + VDMGetPointer + VDMGetThreadContext + VDMSetThreadContext + VDMGetSelectorModule + VDMGetModuleSelector + VDMKillWOW + VDMDetectWOW + VDMBreakThread + VDMModuleFirst + VDMModuleNext + VDMGlobalFirst + VDMGlobalNext + + + The prototypes and structures used by this DLL are prototyped in the header + file VDMDBG.H. Section 5.0 explains each of these functions. + + Debuggers should not use these APIs to deal with the 16-bit environment: + + GetThreadSelectoryEntry + GetThreadContext + SetThreadContext + + The APIs ReadProcessMemory and WriteProcessMemory are still useful except + that the debugger must convert the 16-bit addresses into 32-bit addresses + (using VDMGetPointer). + + Each and every exception with the exception code of STATUS_VDM_EVENT + should be passed to the function VDMProcessException. This function + filters out the extraneous communication between the 16-bit environment + and the VDMDBG.DLL. Most of this extraneous communication a debugger + can ignore (deals with segment & module loading & unloading, this + information will be provided via another interface). If the event is + part of this communication process, VDMProcessException will return + FALSE, if not, it will return TRUE. + + If the function VDMProcessException returns FALSE, the debugger can + immediately call the API ContinueDebugEvent with a continue status of + DBG_CONTINUE and return to waiting for additional debug events. + + If the function VDMProcessException returns TRUE, then the lpArguments + parameter of the exception should be processed to determine the type of + event (INT 1, INT 3, INT D, etc.). The debugger can then act accordingly. + When it has completed operating with the thread, the debugger should + call the API ContinueDebugEvent with a continue status of DBG_CONTINUE + or DBG_EXCEPTION_NOT_HANDLED. + + For the processing of each of the debug events, the debugger should use + the API VDMGetThreadSelectorEntry, VDMGetThreadContext, and + VDMSetThreadContext instead of the likewise named functions listed above + that are exported from KERNEL32.DLL. These functions operate on an x86 + CONTEXT structure, even when running on a non-x86 machine. The debugger + should likewise present an x86 debugging view (x86 register dump, x86 + dis-assembly, breakpoints are INT 3's, etc.) + +4.0 Communication Protocol via. Exceptions + + The method of communicating between the application and debugger + will be exceptions. NTVDM will raise an exception and debugger should + receive it. NTVDM can detect whether or not it is being debugged, and + will conditionally raise the exception. + + If a debugger attaches itself to an existing NTVDM process, the + 32-bit environment detects this and comminicates an acknowledgement + of this attachment. This allows debuggers to perform initialization + dealing with the 16-bit environment before beginning any new 16-bit tasks. + + For the simulated 16-bit environment, the exception code will always be + STATUS_VDM_EVENT. + + On NT, each exception can only contain up to 4 DWORD values. Here is + how they are used for this communication. The first 2 DWORDs contain + 4 WORD fields. The last two DWORDs are just DWORDs. + + +---------+ + | W1 | W2 | = DW1 + +---------+ + | W3 | W4 | = DW2 + +---------+ + | DW3 | + +---------+ + | DW4 | + +---------+ + + The header file VDMDBG.H contains macros for isolating the individual + parts of the exception information. + + The W1 field is a WORD which specifies which type of event has occurred. + This can be one of the following: + + W1 == 0 - Segment Load Notification (DBG_SEGLOAD) + W1 == 1 - Segment Move Notification (DBG_SEGMOVE) + W1 == 2 - Segment Free Notification (DBG_SEGFREE) + W1 == 3 - Module Load Notification (DBG_MODLOAD) + W1 == 4 - Module Free Notification (DBG_MODFREE) + W1 == 5 - Int 01h break (DBG_SINGLESTEP) + W1 == 6 - Int 03h break (DBG_BREAKPOINT) + W1 == 7 - Int 0Dh break (GP Fault) (DBG_GPFAULT) + W1 == 8 - Divide Overflow (DBG_DIVOVERFLOW) + W1 == 9 - Invalid Opcode Fault (DBG_INSTRFAULT) + W1 == 10 - Task starting (DBG_TASKSTART) + W1 == 11 - Task stop (DBG_TASKSTOP) + W1 == 12 - DLL starting (DBG_DLLSTART) + W1 == 13 - DLL stop (DBG_DLLSTOP) + + They are described below. + + The debugger will probably need to be smart enough to know how to manage + both protected mode selectors and segment numbers from simulated real mode. + + Segment/selector to symbol lookup should be mode sensitive and only use + segments from the appropriate mode. + +4.1 Segment Load Notification + + Under Win16, this event is used to indicate that a selector has just + been created and that it maps to a module's segment. + + When a .EXE or .DLL is loaded, many of these events will be received. + No module load notification event will occur (this is the way it is + done under Windows 3.1 too). + + Under DOS, no segment load notifications will occur. + + W1 = DBG_SEGLOAD (0) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to a SEGMENT_NOTE structure in NTVDM address space + DW4 = Reserved + + SEGMENT_NOTE structure field definitions will be: + + Selector1 - Selector assigned to new segment + Selector2 - Unused + Segment - Segment within module + Module - Null terminated module name + FileName - Null terminated path to executable image + Type - Code/data information from segment definition + Length - Unused + + VDMProcessException will return FALSE for this event. + +4.2 Segment Move Notification + + A segment has changed from one selector number to another. If the + new selector number is 0, this should be considered the same as + discarding (freeing) the segment. + + This event only happens under Win16. As such, these selectors should be + tagged as protected mode only selectors. + + W1 = DBG_SEGMOVE (1) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to a SEGMENT_NOTE structure in NTVDM address space + DW4 = Reserved + + SEGMENT_NOTE structure field definitions will be: + + Selector1 - Old selector number + Selector2 - New selector number (0 to discard segment) + Segment - Unused + Module - Unused + FileName - Unused + Type - Unused + Length - Unused + + VDMProcessException will return FALSE for this event. + +4.3 Segment Free Notification + + When a segment is being released, this event will be received. + + This event only happens under Win16. + + W1 = DBG_SEGFREE (2) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to a SEGMENT_NOTE structure in NTVDM address space + DW4 = Reserved + + SEGMENT_NOTE structure field definitions will be: + + Selector1 - Selector number + Selector2 - Unused + Segment - Unused + Module - Unused + FileName - Unused + Type - Unused + Length - Unused + + VDMProcessException will return FALSE for this event. + +4.4 Module Load Notification + + This event is used to indicate that a module is going to take up a + range of memory. + + This event is used only under DOS. As such, these segment numbers + should be tagged as simulated real mode only selectors. + + W1 = DBG_MODLOAD (3) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to a SEGMENT_NOTE structure in NTVDM address space + DW4 = Reserved + + SEGMENT_NOTE structure field definitions will be: + + Selector1 - Unused + Selector2 - Unused + Segment - Starting segment + Module - Null terminated module name + FileName - Null terminated path to executable image + Type - Unused + Length - Length of module (in bytes) + + VDMProcessException will return FALSE for this event. + +4.5 Module Free Notification + + Module freeing notifications happen under both DOS and Win16. For + to determine which selectors to free for a Win16 application, all of the + selectors must be scanned to determine if it was associated with this + module. Again, this is the way it is done under Windows 3.1. + + W1 = DBG_MODFREE (4) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to a SEGMENT_NOTE structure in NTVDM address space + DW4 = Reserved + + SEGMENT_NOTE structure field definitions will be: + + Selector1 - Unused + Selector2 - Unused + Segment - Unused + Module - Null terminated module name + FileName - Null terminated path to executable image + Type - Unused + Length - Unused + + VDMProcessException will return FALSE for this event. + +4.6 Int 01h break + + This event probably requires interaction with the debugger and its + internal breakpoint, trace bit setting mechanisms. + + W1 = DBG_SINGLE_STEP (5) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Unused + DW4 = Reserved + + VDMProcessException will return TRUE for this event. + +4.7 Int 03h break + + This event probably requires interaction with the debugger and its + internal breakpoints. + + W1 = DBG_BREAKPOINT (6) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Unused + DW4 = Reserved + + VDMProcessException will return TRUE for this event. + +4.8 Int 0Dh break (GP Fault) + + This event probably requires interaction with the debugger and its + internal breakpoints. + + W1 = DBG_GPFAULT (7) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Unused + DW4 = Reserved + + It is also important to note that all GP Faults will not be sent via + this interface. The Win16 subsystem intercepts some of the GP faults + in its parameter validation code. Faults in the parameter validation + code indicated that an invalid parameter is being passed to one of the + 16-bit APIs. There is currently no way to intercept these faults, the + APIs will just return errors in the same mechanism as under Windows 3.1. + + VDMProcessException will return TRUE for this event. + +4.9 Divide Overflow break (Int 0) + + This event probably requires interaction with the debugger and its + internal breakpoints. + + W1 = DBG_DIVOVERFLOW (8) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Unused + DW4 = Reserved + + VDMProcessException will return TRUE for this event. + +4.A Invalid Opcode Fault (Int 6) + + W1 = DBG_INSTRFAULT (9) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Unused + DW4 = Reserved + + VDMProcessException will return TRUE for this event. + +4.B Task starting + + After all of the image has been loaded for the application, but + before executing the first instruction, this event will occur. + + W1 = DBG_TASKSTART (0Ah) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to an IMAGE_NOTE structure in NTVDM address space + DW4 = Reserved + + IMAGE_NOTE structure field definitions will be: + + Module - Null terminated module name + FileName - Null terminated path to executable image + + VDMProcessException will return TRUE for this event. + +4.C Task stopping + + After all of the image has been unloaded for the application + this event will occur. None of the segments for the application + will be valid. This is provided for the debugger to clean up any + internal data it keeps on a per task basis. + + W1 = DBG_TASKSTOP (0Bh) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to an IMAGE_NOTE structure in NTVDM address space + DW4 = Reserved + + IMAGE_NOTE structure field definitions will be: + + Module - Null terminated module name + FileName - Null terminated path to executable image + + VDMProcessException will return TRUE for this event. + +4.D Dll starting + + After the image of the DLL has been loaded, but before the + Dll's initialization code is executed, this event will occur. + + W1 = DBG_DLLSTART (0Ch) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to an IMAGE_NOTE structure in NTVDM address space + DW4 = Reserved + + IMAGE_NOTE structure field definitions will be: + + Module - Null terminated module name + FileName - Null terminated path to executable image + + VDMProcessException will return TRUE for this event. + +4.E Dll stopping + + After all of the image of the DLL has been unloaded for the Dll + this event will occur. None of the segments for the Dll will be + valid. This is provided for the debugger to clean up any internal + data it keeps on a per Dll basis. + + W1 = DBG_DLLSTOP (0Ch) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Pointer to an IMAGE_NOTE structure in NTVDM address space + DW4 = Reserved + + IMAGE_NOTE structure field definitions will be: + + Module - Null terminated module name + FileName - Null terminated path to executable image + + VDMProcessException will return TRUE for this event. + +4.F Attach Acknowlegement + + Once the 16-bit environment has detected that a debugger is present + it sends this event to allow the debugger to perform any initialization + processing specific to the 16-bit environment. + + W1 = DBG_ATTACH (0Dh) + W2 = Unused + W3 = Unused + W4 = Unused + DW3 = Unused + DW4 = Reserved + + VDMProcessException will return TRUE for this event. + +5.0 The VDM debugging APIs + + These APIs are described below: + + VDMProcessException + VDMGetThreadSelectorEntry + VDMGetPointer + VDMGetThreadContext + VDMSetThreadContext + VDMGetSelectorModule + VDMGetModuleSelector + VDMEnumProcessWOW + VDMEnumTaskWOW + + The following APIs require WOWDEB.EXE to be loaded and running in the + Win16 subsystem. WOWDEB.EXE is started automatically if the debugger + is present at process creation time (CreateProcess with debug options + set). If the debugger is attached to the Win16 subsystem process after + the process has already been created (with the DebugActiveProcess API), + then the debugger should spawn WOWDEB.EXE before starting the Win16 task + to be debugged. + + VDMModuleFirst + VDMModuleNext + VDMGlobalFirst + VDMGlobalNext + + This following APIs are obsolete. They return failure conditions. + + VDMKillWOW + VDMDetectWOW + VDMBreakThread + +5.1 VDMProcessException + + BOOL VDMProcessException( + LPDEBUG_EVENT lpDebugEvent + ); + + The VDMProcessExecption function performs the pre-processing needed to + prepare the debug event for handling by the debugger. + + This function filters all VDM debugee/debugger communication for + information which is only used by the VDM debugging DLL (VDMDBG.DLL). + This function is only valid in the context of processing for debug + events that are of type STATUS_VDM_EVENT. + + lpDebugEvent Points to a DEBUG_EVENT structure that was returned + from WaitForDebugEvent. + + The return value is TRUE if the debug event should be processed by the + debugger. + + The return value is FALSE if the debug event should be ignored. This + This indicates that no processing should occur for this debug event. + The event should be continued using ContinueDebugEvent with a continue + status of DBG_CONTINUE. + +5.2 VDMGetThreadSelectorEntry + + BOOL VDMGetThreadSelectorEntry( + HANDLE hProcess, + HANDLE hThread, + WORD wSelector + LPLDT_ENTRY lpSelectorEntry + ); + + This function is used to return a descriptor table entry for the + specified VDM thread corresponding to the specified selector. This + function is simimlar to the API GetThreadSelectorEntry except that + it works on the simulated DOS/WIN16 environment, and it works on all + systems, not just x86 systems. + + This API returns a simulated descriptor entry on non-x86 systems. + Simulated descriptor entrys may (on some systems) have the base value + adjusted to account for the fact that the simulated address space may + not begin at linear (32-bit) address 0. + + It is also important to note that 16-bit applications may modify + the contents of the LDT entries. For example, the Win16 subsystem + may change the selector's base value to coalesce discontinous memory + segments. Also, please see the description in VDMGetPointer. + + hProcess Supplies a handle to the DOS/WIN16 sub-system process. + The handle must have been created with PROCESS_VM_READ + access. + + hThread Supplies a handle to the thread that contains the + specified selector. The handle must have been created + with THREAD_QUERY_INFORMATION access. + + wSelector Supplies the selector value to look up. The selector + value may be a global selector (GDT) or a local selector + (LDT). + + lpSelectorEntry If the specifed selector is contained within the + thread's descriptor tables, its descriptor table entry + is copied into the data structure pointed to by this + parameter. This data can be used to compute the linear + base address that segment relative addresses refer to. + + The return value is TRUE if the operation was successful. In that case, + the data structure pointed to by lpSelectorEntry receives a copy of the + specified descriptor table entry. + + Refer to the WinHelp entry for the structure of an LDT_ENTRY. + +5.3 VDMGetPointer + + ULONG VDMGetPointer( + HANDLE hProcess, + HANDLE hThread, + WORD wSelector, + DWORD dwOffset, + BOOL fProtMode + ); + + This function is used to convert a 16-bit address into a flat 32-bit + address. + + It is also very important to note that under the WIN16 environment, + pointers derived from protected mode selectors may change. WIN16 does + this by changing selector's base value to coalesce memory during + compaction. For this reason, it is necessary that any addresses + evaluated in the 16-bit environment should be reevaluated each time + an access into 16-bit memory is needed. An example would be + placing and removing 16-bit breakpoint instructions. If the debugger + is told to place a breakpoint at a given address of SEL:OFFSET, then + when the breakpoint is needs to be removed, the debugger must reevaluate + the SEL:OFFSET address since it might have moved in terms of the linear + 32-bit address. + + hProcess Supplies a handle to the DOS/WIN16 sub-system process. + The handle must have been created with PROCESS_VM_READ + access. + + hThread Supplies a handle to the thread that contains the + specified selector. The handle must have been created + with THREAD_QUERY_INFORMATION access. + + wSelector Supplies the selector value to determine the pointer for. + + dwOffset Supplies the offset value to determine the pointer for. + + fProtMode Indicates whether the 16-bit address specified is a + real mode (v86 mode) address or a protected mode address. + Protected mode addresses are translated through the + descriptor tables. + + The return value is a 32-bit linear address pointing to the memory + in the simulated DOS/WIN16 environment that represents the 16-bit address + specified. The return value is NULL, if the address specified is invalid. + On some systems, NULL may be returned for the address 0:0. + + To determine the address of the simulated 16-bit memory, VDMGetPointer + may be called with the address 0:0 and an fProtMode of FALSE. + +5.4 VDMGetThreadContext + + BOOL VDMGetThreadContext( + LPDEBUG_EVENT lpDebugEvent, + LPVDMCONTEXT lpVDMContext + ); + + The context of a specified simulated DOS or WIN16 thread can be + retrieved using VDMGetThreadContext. The context returned will + always be that of an x86 system. + + This API returns a simulated context for x86 and non-x86 systems. + Under some systems, values within the context are meaningless. For + example, the CONTEXT_DEBUG_REGISTERS portions on RISC systems have + no effect. + + Release 1 of Windows NT has a 286 emulator on RISC systems. For this + reason, only the 16-bit registers can be supported on RISC systems. + + lpDebugEvent Points to a DEBUG_EVENT structure that was returned + from WaitForDebugEvent. + + lpVDMContext If the specified thread is a simulated DOS or WIN16 + thread, its context is copied into the data structure + pointed to by this parameter. + + The return value is TRUE if the operation was successful. In that case, + the data structure pointed to by receives a copy of the + simulated context. + + Refer to the WinHelp for the structure of a VDMCONTEXT (same as x86 + CONTEXT structure in NTI386.H). + +5.5 VDMSetThreadContext + + BOOL VDMSetThreadContext( + LPDEBUG_EVENT lpDebugEvent, + LPVDMCONTEXT lpVDMContext + ); + + The VDMSetThreadContext function sets the simulated context in the + specified DOS or WIN16 thread. The function allows selective context + to be set based on the value of the ContextFlags member of the context + structure. This API operates only when debugging a simulated DOS or + WIN16 thread. The caller must have a thread handle which was created + with THREAD_SET_CONTEXT access. + + The context set will always be that of an x86 system. + + lpDebugEvent Points to a DEBUG_EVENT structure that was returned + from WaitForDebugEvent. + + lpVDMContext Supplies the address of a context structure that + contains the context that is to be set in the specified + thread. The value of the ContextFlags member of this + structure specifies which portions of a thread's context + are to be set. Some values in the context structure are + not settable and are silently set to the correct values. + This include CPU status register bits that specify + processor mode, debug register global enabling bits, and + other state that must be completely controlled by the + system. + + The return value is TRUE if the context was set; otherwise it is FALSE if + an error occurred. + + Refer to the WinHelp for the structure of a VDMCONTEXT (same as x86 + CONTEXT structure in NTI386.H). + +5.6 VDMGetSelectorModule + + BOOL VDMGetSelectorModule( + HANDLE hProcess, + HANDLE hThread, + WORD wSelector, + PUINT lpSegmentNumber, + LPSTR lpModuleName, + UINT nSize, + LPSTR lpModulePath, + UINT nPathSize + ); + + The VDMGetSelectorModule function is intended to provide an interface + such that debuggers can determine which module belongs to a code or + data address. Given the selector for that address, the function will + return the module name, and the segment number (0 based) of the segment + that corresponds to that selector. If the selector is not a selector + which directly corresponds to a module, the function will return FALSE. + + hProcess Supplies a handle to the process of the 16-bit environment + + hThread Supplies a handle to a thread in the 16-bit environment + + wSelector Supplies the selector value to look up. + + lpSegmentNumber Returns the segment number within the module. + + lpModuleName Buffer to receive the module name. + + nModuleSize Size of the buffer. + + lpModulePath Buffer to receive the module path name. + + nPathSize Size of the buffer. + + The return value is TRUE if the selector is mapped directly to a module. + This means it must be either a code or data segment. Selectors allocated + using the global memory management functions are not mapped directly to + a module. The return value is FALSE if the function is not successful. + + The function returns the segment number in the address specified by + the lpSegmentNumber parameter, and the module name in the address + specified by the lpModuleName parameter. + + It is up to the debugger to determine from the module and segment number + information the correct symbol, if a symbol lookup is needed. + +5.7 VDMGetModuleSelector + + BOOL VDMGetModuleSelector( + HANDLE hProcess, + HANDLE hThread, + UINT uSegmentNumber, + LPSTR lpModuleName, + LPWORD lpSelector + ); + + The VDMGetModuleSelector function is the reverse operation of the + VDMGetSelectorModule function. A module name and segment number + are converted into a selector number. + + hProcess Supplies a handle to the process of the 16-bit environment + + hThread Supplies a handle to a thread in the 16-bit environment + + uSegmentNumber Supplies the segment number to look up. + + lpModuleName Specifies the module name of the segment. + + lpSelector Returns the selector value. + + The return value is TRUE if the module and segment are found. Also, + the lpSelector value is filled-in with the selector of that segment. + Otherwise, the return value is FALSE. + + It is up to the debugger to convert symbol names and expressions into + modules, segment numbers and offsets. Usings the modules and segment + numbers, a selector value can be determined. In combination with the + offset, the selector can be used to index into the simulated Win16 + environment for reading, writing, etc. + +5.8 VDMEnumProcessWOW + + INT VDMEnumProcessWOW( + PROCESSENUMPROC fp, + LPARAM lparam + ); + + The VDMEnumProcessWOW function enumerates all of the processes in + the system which are Win16 subsystem processes. In NT's first release, + there is only one Win16 subsystem. For later releases, there may be + more than one process to support address space seperation for Win16 + tasks. + + fp Supplies the address of a callback routine. + + lparam Supplies a parameter to the callback routine. + + The return value is the number of Win16 subsystem processes, or the + number enumerated before enumeration was terminated. + + BOOL ProcessEnumProc( + DWORD dwProcessId, + DWORD dwAttributes, + LPARAM lparam + ); + + The callback function will be called once for each Win16 subsystem process + in the system. + + dwProcessId Provides the process id of a Win16 subsystem. + + dwAttributes Provides flags indicating information about the process. + + lparam Provides the parameter passed to VDMEnumProcessWOW + + The callback function should return a non-zero value to terminate + enumeration. + + The dwAttributes field should be compared with the bit mask WOW_SYSTEM + to determine if the process is the main Win16 subsystem process. The + main Win16 subsystem process will be the process that the next Win16 + task that specifies no address space seperation requirements. + + Win16 subsystem processes which are created due to address space + seperation will not have the WOW_SYSTEM bit enabled. + +5.9 VDMEnumTaskWOW + + INT + VDMEnumTaskWOW( + DWORD dwProcessId, + TASKENUMPROC fp, + LPARAM lparam + ); + + The VDMEnumTaskWOW function enumerates all of the Win16 tasks currently + running in the Win16 subsystem process id specified. + + dwProcessId Supplies the process id of the Win16 subsystem. + + fp Supplies the address of a callback routine. + + lparam Supplies a parameter to the callback routine. + + The return value is the number of Win16 tasks or the number of + tasks enumerated before terminating. + + BOOL TaskEnumProc( + DWORD dwThreadId, + WORD hMod16, + WORD hTask16, + LPARAM lparam + ); + + The callback function will be called once for each Win16 task. + + dwThreadId Provides the thread id of the Win16 task + + hMod16 Provides the Win16 module handle for the task + + hTask16 Provides the Win16 task handle for the task + + lparam Provides the parameter passed to VDMEnumTaskWOW + + The callback function should return a non-zero value to terminate + enumeration. + +5.A VDMModuleFirst + + BOOL VDMModuleFirst( + HANDLE hProcess, + HANDLE hThread, + LPMODULEENTRY lpModuleEntry, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData + ); + + This function is used to start enumerating all of the modules currently + loaded in the 16-bit Windows environment. It behaves in the same manner + as the Windows 3.1 ToolHelp API ModuleFirst(). + + hProcess Supplies a handle to the process of the 16-bit environment + + hThread Supplies a handle to a thread in the 16-bit environment + + lpModuleEntry Specifies the address of a MODULEENTRY structure which + will be filled in with the first module in the module + list. + + lpEventProc Specifies the address of a procedure which might be + called in the event of a debug event occuring while + the communication session with the 16-bit enviroment + is occurring. + + lpData Specifies a parameter to pass to the debug event procudure + + The return value is TRUE if the operation was successful. In that case, + the MODULEENTRY structure will be filled in with information for the + first module in the module list. + +5.B VDMModuleNext + + BOOL VDMModuleNext( + HANDLE hProcess, + HANDLE hThread, + LPMODULEENTRY lpModuleEntry, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData + ); + + This function is used to continue enumerating all of the modules currently + loaded in the 16-bit Windows environment. It behaves in the same manner + as the Windows 3.1 ToolHelp API ModuleNext(). + + hProcess Supplies a handle to the process of the 16-bit environment + + hThread Supplies a handle to a thread in the 16-bit environment + + lpModuleEntry Specifies the address of a MODULEENTRY structure which + will be used to determine the next module and which will + be filled in with the next module in the module list. + + lpEventProc Specifies the address of a procedure which might be + called in the event of a debug event occuring while + the communication session with the 16-bit enviroment + is occurring. + + lpData Specifies a parameter to pass to the debug event procudure + + The return value is TRUE if the operation was successful. In that case, + the MODULEENTRY structure will be filled in with information for the + next module in the module list. + +5.C VDMGlobalFirst + + BOOL VDMGlobalFirst( + HANDLE hProcess, + HANDLE hThread, + LPGLOBALENTRY lpGlobalEntry, + WORD wFlags, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData + ); + + This function is used to continue enumerating all of the global memory + blocks currently allocated in the 16-bit Windows environment. It + behaves in the same manner as the Windows 3.1 ToolHelp API GlobalFirst(). + + hProcess Supplies a handle to the process of the 16-bit environment + + hThread Supplies a handle to a thread in the 16-bit environment + + lpModuleEntry Specifies the address of a GLOBALENTRY structure which + will be filled in with information for the first global + memory block matching the specified flags + + wFlags Specifies which types of global memory blocks to enumerate + + lpEventProc Specifies the address of a procedure which might be + called in the event of a debug event occuring while + the communication session with the 16-bit enviroment + is occurring. + + lpData Specifies a parameter to pass to the debug event procudure + + The return value is TRUE if the operation was successful. In that case, + the GLOBALENTRY structure will be filled in with information for the + first global memory block that matches the specified flags. + +5.D VDMGlobalNext + + BOOL VDMGlobalNext( + HANDLE hProcess, + HANDLE hThread, + LPGLOBALENTRY lpGlobalEntry, + WORD wFlags, + DEBUGEVENTPROC lpEventProc, + LPVOID lpData + ); + + This function is used to continue enumerating all of the global memory + blocks currently allocated in the 16-bit Windows environment. It behaves + in the same manner as the Windows 3.1 ToolHelp API GlobalNext(). + + hProcess Supplies a handle to the process of the 16-bit environment + + hThread Supplies a handle to a thread in the 16-bit environment + + lpModuleEntry Specifies the address of a GLOBALENTRY structure which + will be used to determine the next global memory block + and which will be filled in with information for the + next global memory block matching the specified flags + + wFlags Specifies which types of global memory blocks to enumerate + + lpEventProc Specifies the address of a procedure which might be + called in the event of a debug event occuring while + the communication session with the 16-bit enviroment + is occurring. + + lpData Specifies a parameter to pass to the debug event procudure + + The return value is TRUE if the operation was successful. In that case, + the GLOBALENTRY structure will be filled in with information for the + next global memory block that matches the specified flags. + +5.E VDMKillWOW + + BOOL VDMKillWOW(void); + + This function is obsolete and performs no operation. It returns FALSE. + +5.F VDMDetectWOW + + BOOL VDMDetectWOW(void); + + This function is obsolete and performs no operation. It returns FALSE. + +5.G VDMBreakThread + + BOOL VDMBreakThread( + HANDLE hProcess + HANDLE hThread + ); + + This function is obsolete and performs no operation. It returns FALSE. -- cgit v1.2.3