windows-nt/Source/XPSP1/NT/sdktools/appverif/debugger.cpp
2020-09-26 16:20:57 +08:00

862 lines
22 KiB
C++

/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
Debugger.cpp
Abstract:
This module implements the AppVerifier.exe debugger.
Author:
clupu created 01/25/2001
Revision History:
--*/
#include "stdafx.h"
#include "appverif.h"
#include "Log.h"
#include <memory.h>
#include <stdlib.h>
#include "wdbgexts.h"
#include "Debugger.h"
#include "Setting.h"
#include "UserDump.h"
#include "DbgHelp.h"
#include "ChooseExe.h"
#ifdef BP_SIZE
ULONG g_BpSize = BP_SIZE;
#endif
#ifdef BP_INSTR
ULONG g_BpInstr = BP_INSTR;
#endif
typedef void (*PFNDBGEXT)(HANDLE hCurrentProcess,
HANDLE hCurrentThread,
DWORD dwUnused,
PWINDBG_EXTENSION_APIS lpExtensionApis,
LPSTR lpArgumentString);
PFNDBGEXT g_pfnPhextsInit;
BOOL g_bInitialBreakpoint = TRUE;
PROCESS_INFORMATION g_ProcessInformation;
WINDBG_EXTENSION_APIS ExtensionAPIs;
typedef struct tagLOADEDDLL
{
struct tagLOADEDDLL* pNext;
LPTSTR pszName;
LPVOID lpBaseOfDll;
} LOADEDDLL, *PLOADEDDLL;
PLOADEDDLL g_pFirstDll;
PPROCESS_INFO g_pProcessHead = NULL;
PPROCESS_INFO g_pFirstProcess = NULL;
//
// BUGBUG: what's this for ?
//
TCHAR g_szDbgString[1024];
//
// Local function prototypes
//
void DebuggerLoop(void);
DWORD WINAPI ExecuteAppThread( LPVOID lParam )
/*++
Return: TRUE if successful, FALSE otherwise.
Desc: Launch the debuggee.
--*/
{
BOOL bRet;
STARTUPINFO StartupInfo;
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
//
// Set some pageheap flags
//
AVSetVerifierFlagsForExe(g_szAppShortName, (DWORD)(LPARAM)lParam);
bRet = CreateProcess(NULL,
g_szAppFullPath,
NULL,
NULL,
FALSE,
CREATE_SEPARATE_WOW_VDM | DEBUG_ONLY_THIS_PROCESS,
NULL,
NULL,
&StartupInfo,
&g_ProcessInformation);
if ( !bRet )
{
LogMessage(LOG_ERROR, _T("[ExecuteApp] Couldn't start \"%s\""), g_szAppFullPath);
return 0;
}
LogMessage(LOG_INFO,
_T("[ExecuteApp] CreateProcess hProcess 0x%X. \"%s\""),
g_ProcessInformation.hProcess,
g_szAppFullPath);
if ( g_ProcessInformation.hProcess != NULL )
{
//
// Start the debugger loop.
//
DebuggerLoop();
//
// Free the memory.
//
PPROCESS_INFO pProcess = g_pProcessHead;
PPROCESS_INFO pProcessNext;
while ( pProcess != NULL )
{
pProcessNext = pProcess->pNext;
PTHREAD_INFO pThread = pProcess->pFirstThreadInfo;
PTHREAD_INFO pThreadNext;
while ( pThread != NULL )
{
pThreadNext = pThread->pNext;
HeapFree(GetProcessHeap(), 0, pThread);
pThread = pThreadNext;
}
HeapFree(GetProcessHeap(), 0, pProcess);
pProcess = pProcessNext;
}
g_pProcessHead = NULL;
PLOADEDDLL pDll = g_pFirstDll;
PLOADEDDLL pDllNext;
while ( pDll != NULL )
{
pDllNext = pDll->pNext;
HeapFree(GetProcessHeap(), 0, pDll->pszName);
HeapFree(GetProcessHeap(), 0, pDll);
pDll = pDllNext;
}
g_pFirstDll = NULL;
g_bDebuggeeExited = TRUE;
CloseHandle(g_ProcessInformation.hProcess);
CloseHandle(g_ProcessInformation.hThread);
ZeroMemory(&g_ProcessInformation, sizeof(PROCESS_INFORMATION));
}
//
// Delete the pageheap flags
//
AVSetVerifierFlagsForExe(g_szAppShortName, 0);
return 1;
}
PPROCESS_INFO GetProcessInfo( HANDLE hProcess )
{
PPROCESS_INFO pProcess = g_pProcessHead;
while ( pProcess != NULL )
{
if ( pProcess->hProcess == hProcess )
{
return pProcess;
}
}
return NULL;
}
SIZE_T
ReadMemoryDbg(
HANDLE hProcess,
LPVOID Address,
LPVOID Buffer,
SIZE_T Length
)
{
SIZE_T cbRead;
LPVOID AddressBp;
PPROCESS_INFO pProcess;
if ( !ReadProcessMemory(hProcess,
Address,
Buffer,
Length,
&cbRead) )
{
return 0;
}
pProcess = GetProcessInfo(hProcess);
if ( pProcess == NULL )
{
return 0;
}
#if defined(BP_INSTR) && defined(BP_SIZE)
for ( int i = 0; i < MAX_BREAKPOINTS; i++ )
{
AddressBp = pProcess->bp[i].Address;
if ( (ULONG_PTR)AddressBp >= (ULONG_PTR)Address &&
(ULONG_PTR)AddressBp < (ULONG_PTR)Address + Length )
{
CopyMemory((LPVOID)((ULONG_PTR)Buffer + (ULONG_PTR)AddressBp - (ULONG_PTR)Address),
&pProcess->bp[i].OriginalInstr,
g_BpSize);
}
}
#endif
return cbRead;
}
BOOL
WriteMemoryDbg(
HANDLE hProcess,
PVOID Address,
PVOID Buffer,
SIZE_T Length
)
{
SIZE_T cb = 0;
BOOL bSuccess;
bSuccess = WriteProcessMemory(hProcess, Address, Buffer, Length, &cb);
if ( !bSuccess || cb != Length )
{
return FALSE;
}
return TRUE;
}
BOOL
GetImageName(
HANDLE hProcess,
ULONG_PTR ImageBase,
PVOID ImageNamePtr,
LPTSTR ImageName,
DWORD ImageNameLength
)
/*++
Return: TRUE if successful, FALSE otherwise.
Desc: BUGBUG: give details here
--*/
{
DWORD_PTR i;
BYTE UnicodeBuf[256 * 2];
IMAGE_DOS_HEADER dh;
IMAGE_NT_HEADERS nh;
IMAGE_EXPORT_DIRECTORY expdir;
if ( !ReadMemoryDbg(hProcess,
(ULONG*)ImageNamePtr,
&i,
sizeof(i)) )
{
goto GetFromExports;
}
if ( !ReadMemoryDbg(hProcess,
(ULONG*)i,
(ULONG*)UnicodeBuf,
sizeof(UnicodeBuf)) )
{
goto GetFromExports;
}
ZeroMemory(ImageName, ImageNameLength);
#ifdef UNICODE
lstrcpyW(ImageName, (LPCWSTR)UnicodeBuf);
#else
WideCharToMultiByte(CP_ACP,
0,
(LPWSTR)UnicodeBuf,
wcslen((LPWSTR)UnicodeBuf),
ImageName,
ImageNameLength,
NULL,
NULL);
#endif // UNICODE
if ( lstrlen(ImageName) == 0 )
{
goto GetFromExports;
}
return TRUE;
GetFromExports:
if ( !ReadMemoryDbg(hProcess,
(ULONG*)ImageBase,
(ULONG*)&dh,
sizeof(IMAGE_DOS_HEADER)) )
{
return FALSE;
}
if ( dh.e_magic != IMAGE_DOS_SIGNATURE )
{
return FALSE;
}
if ( !ReadMemoryDbg(hProcess,
(ULONG*)(ImageBase + dh.e_lfanew),
(ULONG*)&nh,
sizeof(IMAGE_NT_HEADERS)) )
{
return FALSE;
}
if ( nh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 )
{
return FALSE;
}
if ( !ReadMemoryDbg(hProcess,
(ULONG*)(ImageBase +
nh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress),
(ULONG*)&expdir,
sizeof(IMAGE_EXPORT_DIRECTORY)) )
{
return FALSE;
}
if ( !ReadMemoryDbg(hProcess,
(ULONG*)(ImageBase + expdir.Name),
#ifdef UNICODE
(ULONG*)UnicodeBuf,
#else
(ULONG*)ImageName,
#endif // UNICODE
ImageNameLength) )
{
return FALSE;
}
#ifdef UNICODE
MultiByteToWideChar(CP_ACP,
0,
(LPCSTR)UnicodeBuf,
-1,
ImageName,
ImageNameLength);
#endif // UNICODE
return TRUE;
}
void
AddDll(
LPVOID lpBaseOfDll,
LPCTSTR pszDllName
)
/*++
Return: void.
Desc: BUGBUG: give details here
--*/
{
PLOADEDDLL pDll;
pDll = (PLOADEDDLL)HeapAlloc(GetProcessHeap(), 0, sizeof(LOADEDDLL));
if ( pDll == NULL )
{
LogMessage(LOG_ERROR, _T("[AddDll] Failed to allocate %d bytes"), sizeof(LOADEDDLL));
return;
}
pDll->pszName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, (lstrlen(pszDllName) + 1) * sizeof(TCHAR));
if ( pDll->pszName == NULL )
{
HeapFree(GetProcessHeap(), 0, pDll);
LogMessage(LOG_ERROR,
_T("[AddDll] Failed to allocate %d bytes"),
(lstrlen(pszDllName) + 1) * sizeof(TCHAR));
return;
}
lstrcpy(pDll->pszName, pszDllName);
pDll->lpBaseOfDll = lpBaseOfDll;
pDll->pNext = g_pFirstDll;
g_pFirstDll = pDll;
}
LPTSTR GetDll( LPVOID lpBaseOfDll )
/*++
Return: The name of the DLL with the specified base address.
Desc: BUGBUG
--*/
{
PLOADEDDLL pDll = g_pFirstDll;
while ( pDll != NULL )
{
if ( pDll->lpBaseOfDll == lpBaseOfDll )
{
return pDll->pszName;
}
pDll = pDll->pNext;
}
return NULL;
}
void RemoveDll( LPVOID lpBaseOfDll )
/*++
Return: void.
Desc: BUGBUG: give details here
--*/
{
PLOADEDDLL* ppDll = &g_pFirstDll;
PLOADEDDLL pDllFree;
while ( *ppDll != NULL )
{
if ( (*ppDll)->lpBaseOfDll == lpBaseOfDll )
{
HeapFree(GetProcessHeap(), 0, (*ppDll)->pszName);
pDllFree = *ppDll;
*ppDll = (*ppDll)->pNext;
HeapFree(GetProcessHeap(), 0, pDllFree);
break;
}
ppDll = &((*ppDll)->pNext);
}
}
BOOL ProcessModuleLoad( PPROCESS_INFO pProcess, DEBUG_EVENT* pde )
/*++
Return: TRUE on success, FALSE otherwise
Desc: Process all module load debug events, create process & dll load.
The purpose is to allocate a MODULEINFO structure, fill in the
necessary values, and load the symbol table.
--*/
{
HANDLE hFile=NULL;
DWORD_PTR dwBaseOfImage;
if ( pde->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT )
{
hFile = pde->u.CreateProcessInfo.hFile;
dwBaseOfImage = (DWORD_PTR)pde->u.CreateProcessInfo.lpBaseOfImage;
SymInitialize(pProcess->hProcess, NULL, FALSE);
}
else if ( pde->dwDebugEventCode == LOAD_DLL_DEBUG_EVENT )
{
hFile = pde->u.LoadDll.hFile;
dwBaseOfImage = (DWORD_PTR)pde->u.LoadDll.lpBaseOfDll;
}
if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE )
{
return FALSE;
}
if ( dwBaseOfImage==(DWORD_PTR)NULL )
{
return FALSE;
}
if ( !SymLoadModule(pProcess->hProcess, hFile, NULL, NULL, dwBaseOfImage, 0) )
{
return FALSE;
}
return TRUE;
}
void DebuggerLoop( void )
/*++
Return: void.
Desc: BUGBUG: give details here
--*/
{
DEBUG_EVENT DebugEv; // debugging event information
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
PPROCESS_INFO pProcess = NULL;
PTHREAD_INFO pThread = NULL;
while ( TRUE )
{
//
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
//
WaitForDebugEvent(&DebugEv, INFINITE);
//
// Update the processes and threads lists.
//
pProcess = g_pProcessHead;
while ( pProcess != NULL )
{
if ( pProcess->dwProcessId == DebugEv.dwProcessId )
{
break;
}
pProcess = pProcess->pNext;
}
if ( pProcess == NULL )
{
//
// New process.
//
pProcess = (PPROCESS_INFO)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(PROCESS_INFO));
if ( pProcess == NULL )
{
LogMessage(LOG_ERROR,
_T("[DebuggerLoop] Failed to allocate memory for the new process. Fatal !"));
break;
}
pProcess->dwProcessId = DebugEv.dwProcessId;
pProcess->pNext = g_pProcessHead;
g_pProcessHead = pProcess;
}
pThread = pProcess->pFirstThreadInfo;
while ( pThread != NULL )
{
if ( pThread->dwThreadId == DebugEv.dwThreadId )
{
break;
}
pThread = pThread->pNext;
}
if ( pThread == NULL )
{
//
// New thread.
//
pThread = (PTHREAD_INFO)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(THREAD_INFO));
if ( pThread == NULL )
{
LogMessage(LOG_ERROR,
_T("[DebuggerLoop] Failed to allocate memory for the new thread. Fatal !"));
break;
}
pThread->dwThreadId = DebugEv.dwThreadId;
pThread->pNext = pProcess->pFirstThreadInfo;
pProcess->pFirstThreadInfo = pThread;
}
dwContinueStatus = DBG_CONTINUE;
if ( DebugEv.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT )
{
goto EndProcess;
}
switch ( DebugEv.dwDebugEventCode )
{
case EXCEPTION_DEBUG_EVENT:
switch ( DebugEv.u.Exception.ExceptionRecord.ExceptionCode )
{
case EXCEPTION_BREAKPOINT:
//
// Hit a breakpoint.
//
if ( !pProcess->bSeenLdrBp )
{
pProcess->bSeenLdrBp = TRUE;
//
// When the initial breakpoint is hit all the
// staticly linked DLLs are already loaded so
// we can go ahead and set breakpoints.
//
LogMessage(LOG_INFO, _T("[DebuggerLoop] Hit initial breakpoint."));
//
// Now it would be a good time to initialize the debugger extensions.
//
break;
}
LogMessage(LOG_INFO, _T("[DebuggerLoop] Hit breakpoint."));
LogAVStatus(AVS_PAGEHEAP_DOUBLEFREE);
if ( MessageBox(NULL,
_T("An Access Violation occured. Do you want to create")
_T(" a crash dump file so you can later debug this problem ?"),
_T("Error"),
MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) == IDYES )
{
pProcess->DebugEvent = DebugEv;
GenerateUserModeDump(g_szCrashDumpFile,
pProcess,
&DebugEv.u.Exception);
}
if ( MessageBox(NULL,
_T("Do you want to continue running the program ?"),
_T("Error"),
MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) != IDYES )
{
goto EndProcess;
}
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
case STATUS_SINGLE_STEP:
LogMessage(LOG_INFO, _T("[DebuggerLoop] Hit single step breakpoint."));
break;
case EXCEPTION_ACCESS_VIOLATION:
LogMessage(LOG_INFO,
_T("[DebuggerLoop] AV. Addr: 0x%08X, firstChance %d"),
DebugEv.u.Exception.ExceptionRecord.ExceptionAddress,
DebugEv.u.Exception.dwFirstChance);
LogAVStatus(AVS_PAGEHEAP_DOUBLEFREE);
if ( MessageBox(NULL,
_T("An Access Violation occured. Do you want to create")
_T(" a crash dump file so you can later debug this problem ?"),
_T("Error"),
MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) == IDYES )
{
pProcess->DebugEvent = DebugEv;
GenerateUserModeDump(g_szCrashDumpFile,
pProcess,
&DebugEv.u.Exception);
}
if ( MessageBox(NULL,
_T("Do you want to continue running the program ?"),
_T("Error"),
MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON1) != IDYES )
{
goto EndProcess;
}
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
default:
LogMessage(LOG_INFO, _T("[DebuggerLoop] Unknown debugger exception."));
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
pThread->hProcess = pProcess->hProcess;
pThread->hThread = DebugEv.u.CreateThread.hThread;
LogMessage(LOG_INFO,
_T("[DebuggerLoop] new thread. StartAddress: 0x%x"),
DebugEv.u.CreateThread.lpStartAddress);
break;
case EXIT_THREAD_DEBUG_EVENT:
LogMessage(LOG_INFO,
_T("[DebuggerLoop] exiting thread with code: 0x%x"),
DebugEv.u.ExitThread.dwExitCode);
break;
case CREATE_PROCESS_DEBUG_EVENT:
pProcess->hProcess = DebugEv.u.CreateProcessInfo.hProcess;
pThread->hProcess = pProcess->hProcess;
pThread->hThread = DebugEv.u.CreateProcessInfo.hThread;
if ( g_pFirstProcess == NULL )
{
g_pFirstProcess = pProcess;
}
pProcess->EntryPoint = DebugEv.u.CreateProcessInfo.lpStartAddress;
ProcessModuleLoad(pProcess, &DebugEv);
LogMessage(LOG_INFO,
_T("[DebuggerLoop] new process. BaseImage: 0x%x StartAddress: 0x%x"),
DebugEv.u.CreateProcessInfo.lpBaseOfImage,
DebugEv.u.CreateProcessInfo.lpStartAddress);
break;
case LOAD_DLL_DEBUG_EVENT:
{
TCHAR szAsciiBuf[256];
TCHAR szDllName[128];
TCHAR szExt[16];
BOOL bRet;
bRet = GetImageName(pProcess->hProcess,
(ULONG_PTR)DebugEv.u.LoadDll.lpBaseOfDll,
DebugEv.u.LoadDll.lpImageName,
szAsciiBuf,
sizeof(szAsciiBuf));
if (!bRet) {
LogMessage(LOG_INFO,
_T("[DebuggerLoop] DLL LOADED. BaseDll: 0x%X cannot get the name."),
DebugEv.u.LoadDll.lpBaseOfDll);
AddDll(DebugEv.u.LoadDll.lpBaseOfDll, NULL);
} else {
_tsplitpath(szAsciiBuf, NULL, NULL, szDllName, szExt);
lstrcat(szDllName, szExt);
AddDll(DebugEv.u.LoadDll.lpBaseOfDll, szDllName);
LogMessage(LOG_INFO,
_T("[DebuggerLoop] DLL LOADED. BaseDll: 0x%X \"%s\"."),
DebugEv.u.LoadDll.lpBaseOfDll,
szDllName);
}
ProcessModuleLoad(pProcess, &DebugEv);
CloseHandle(DebugEv.u.LoadDll.hFile);
break;
}
case UNLOAD_DLL_DEBUG_EVENT:
{
LPTSTR pszName = GetDll(DebugEv.u.UnloadDll.lpBaseOfDll);
if ( pszName == NULL )
{
LogMessage(LOG_INFO,
_T("[DebuggerLoop] DLL UNLOADED. BaseDll: 0x%X unknown."),
DebugEv.u.UnloadDll.lpBaseOfDll);
}
else
{
LogMessage(LOG_INFO,
_T("[DebuggerLoop] DLL UNLOADED. BaseDll: 0x%X \"%s\"."),
DebugEv.u.UnloadDll.lpBaseOfDll, pszName);
}
RemoveDll(DebugEv.u.UnloadDll.lpBaseOfDll);
break;
}
case OUTPUT_DEBUG_STRING_EVENT:
ReadMemoryDbg(pThread->hProcess,
DebugEv.u.DebugString.lpDebugStringData,
g_szDbgString,
DebugEv.u.DebugString.nDebugStringLength);
#ifdef UNICODE
LogMessage(LOG_INFO, _T("DPF - %S"), g_szDbgString);
#else
LogMessage(LOG_INFO, _T("DPF - %s"), g_szDbgString);
#endif // UNICODE
break;
default:
LogMessage(LOG_ERROR,
_T("[DebuggerLoop] Unknown event 0x%X"),
DebugEv.dwDebugEventCode);
break;
}
//
// Resume executing the thread that reported the debugging event.
//
ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId,
dwContinueStatus);
}
EndProcess:
LogMessage(LOG_INFO, _T("[DebuggerLoop] The debugged process has exited."));
LogAVStatus(AVS_APP_TERMINATED);
}