summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdmdbg
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/vdmdbg
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/mvdm/vdmdbg')
-rw-r--r--private/mvdm/vdmdbg/makefile6
-rw-r--r--private/mvdm/vdmdbg/sources81
-rw-r--r--private/mvdm/vdmdbg/vdmdbg.c2299
-rw-r--r--private/mvdm/vdmdbg/vdmdbg.def24
-rw-r--r--private/mvdm/vdmdbg/vdmdbg.rc10
-rw-r--r--private/mvdm/vdmdbg/vdmdbg.txt1071
6 files changed, 3491 insertions, 0 deletions
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 <nt.h>
+#include <ntdbg.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <vdmdbg.h>
+#include <dbginfo.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef WORD HAND16;
+
+#define SHAREWOW_MAIN
+#include <sharewow.h>
+
+#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 <windows.h>
+#include <ntverp.h>
+
+#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 <lpVDMContext> 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.