NT4/private/mvdm/vdmdbg/vdmdbg.c
2020-09-30 17:12:29 +02:00

2300 lines
64 KiB
C

/*++
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;
}