2024-12-21 10:04:04 +08:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
#include <tchar.h>
|
|
|
|
|
|
#include <DbgHelp.h>
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
|
|
#include "StackWalker.h"
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
|
|
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int MAX_SYM_SIZE = 512;
|
|
|
|
|
|
const int BUFF_SIZE = 8192;
|
|
|
|
|
|
const int SYM_BUFF_SIZE = 1024;
|
|
|
|
|
|
|
|
|
|
|
|
// The stack frame used in walking the stack
|
|
|
|
|
|
static BOOL g_bSymEngInit = FALSE ;
|
|
|
|
|
|
static STACKFRAME64 g_stFrame;
|
|
|
|
|
|
static CONTEXT g_stContext;
|
|
|
|
|
|
|
|
|
|
|
|
// The static buffer returned by various functions. This buffer
|
|
|
|
|
|
// allows data to be transferred without using the stack.
|
|
|
|
|
|
static CHAR g_szBuff[BUFF_SIZE] = "";
|
|
|
|
|
|
|
|
|
|
|
|
// The static symbol lookup buffer
|
|
|
|
|
|
static BYTE g_stSymbol [ SYM_BUFF_SIZE ] ;
|
|
|
|
|
|
|
|
|
|
|
|
// The static source file and line number structure
|
|
|
|
|
|
static IMAGEHLP_LINE64 g_stLine ;
|
|
|
|
|
|
|
|
|
|
|
|
#define TRACE __noop
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
Typedefs
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
// The typedefs for the PSAPI.DLL functions used by this module.
|
|
|
|
|
|
typedef BOOL (WINAPI *ENUMPROCESSMODULES) ( HANDLE hProcess ,
|
|
|
|
|
|
HMODULE * lphModule ,
|
|
|
|
|
|
DWORD cb ,
|
|
|
|
|
|
LPDWORD lpcbNeeded ) ;
|
|
|
|
|
|
|
|
|
|
|
|
typedef DWORD (WINAPI *GETMODULEBASENAMEW) ( HANDLE hProcess ,
|
|
|
|
|
|
HMODULE hModule ,
|
|
|
|
|
|
LPWSTR lpBaseName ,
|
|
|
|
|
|
DWORD nSize ) ;
|
|
|
|
|
|
|
|
|
|
|
|
typedef DWORD (WINAPI *GETMODULEFILENAMEEXW) ( HANDLE hProcess ,
|
|
|
|
|
|
HMODULE hModule ,
|
|
|
|
|
|
LPWSTR lpFilename ,
|
|
|
|
|
|
DWORD nSize ) ;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
File Static Data
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
// Has the function stuff here been initialized? This is only to be
|
|
|
|
|
|
// used by the InitPSAPI function and nothing else.
|
|
|
|
|
|
static BOOL g_bInitialized = FALSE ;
|
|
|
|
|
|
// The pointer to EnumProcessModules.
|
|
|
|
|
|
static ENUMPROCESSMODULES g_pEnumProcessModules = NULL ;
|
|
|
|
|
|
// The pointer to GetModuleBaseName.
|
|
|
|
|
|
static GETMODULEBASENAMEW g_pGetModuleBaseName = NULL ;
|
|
|
|
|
|
// The pointer to GetModuleFileNameEx.
|
|
|
|
|
|
static GETMODULEFILENAMEEXW g_pGetModuleFileNameEx = NULL ;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char*
|
|
|
|
|
|
GetFaultReason(EXCEPTION_POINTERS* pExPtrs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (::IsBadReadPtr(pExPtrs, sizeof(EXCEPTION_POINTERS)))
|
|
|
|
|
|
return ("bad exception pointers");
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ڵ<EFBFBD><DAB5><EFBFBD><EFBFBD><EFBFBD> <20>׳<EFBFBD> <20><>ȯ<EFBFBD><C8AF> <20><> <20>ִ<EFBFBD>.
|
|
|
|
|
|
switch (pExPtrs->ExceptionRecord->ExceptionCode)
|
|
|
|
|
|
{
|
|
|
|
|
|
case EXCEPTION_ACCESS_VIOLATION: return ("EXCEPTION_ACCESS_VIOLATION");
|
|
|
|
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT: return ("EXCEPTION_DATATYPE_MISALIGNMENT");
|
|
|
|
|
|
case EXCEPTION_BREAKPOINT: return ("EXCEPTION_BREAKPOINT");
|
|
|
|
|
|
case EXCEPTION_SINGLE_STEP: return ("EXCEPTION_SINGLE_STEP");
|
|
|
|
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return ("EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
|
|
|
|
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND: return ("EXCEPTION_FLT_DENORMAL_OPERAND");
|
|
|
|
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return ("EXCEPTION_FLT_DIVIDE_BY_ZERO");
|
|
|
|
|
|
case EXCEPTION_FLT_INEXACT_RESULT: return ("EXCEPTION_FLT_INEXACT_RESULT");
|
|
|
|
|
|
case EXCEPTION_FLT_INVALID_OPERATION: return ("EXCEPTION_FLT_INVALID_OPERATION");
|
|
|
|
|
|
case EXCEPTION_FLT_OVERFLOW: return ("EXCEPTION_FLT_OVERFLOW");
|
|
|
|
|
|
case EXCEPTION_FLT_STACK_CHECK: return ("EXCEPTION_FLT_STACK_CHECK");
|
|
|
|
|
|
case EXCEPTION_FLT_UNDERFLOW: return ("EXCEPTION_FLT_UNDERFLOW");
|
|
|
|
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO: return ("EXCEPTION_INT_DIVIDE_BY_ZERO");
|
|
|
|
|
|
case EXCEPTION_INT_OVERFLOW: return ("EXCEPTION_INT_OVERFLOW");
|
|
|
|
|
|
case EXCEPTION_PRIV_INSTRUCTION: return ("EXCEPTION_PRIV_INSTRUCTION");
|
|
|
|
|
|
case EXCEPTION_IN_PAGE_ERROR: return ("EXCEPTION_IN_PAGE_ERROR");
|
|
|
|
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION: return ("EXCEPTION_ILLEGAL_INSTRUCTION");
|
|
|
|
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return ("EXCEPTION_NONCONTINUABLE_EXCEPTION");
|
|
|
|
|
|
case EXCEPTION_STACK_OVERFLOW: return ("EXCEPTION_STACK_OVERFLOW");
|
|
|
|
|
|
case EXCEPTION_INVALID_DISPOSITION: return ("EXCEPTION_INVALID_DISPOSITION");
|
|
|
|
|
|
case EXCEPTION_GUARD_PAGE: return ("EXCEPTION_GUARD_PAGE");
|
|
|
|
|
|
case EXCEPTION_INVALID_HANDLE: return ("EXCEPTION_INVALID_HANDLE");
|
|
|
|
|
|
//case EXCEPTION_POSSIBLE_DEADLOCK: return ("EXCEPTION_POSSIBLE_DEADLOCK");
|
|
|
|
|
|
case CONTROL_C_EXIT: return ("CONTROL_C_EXIT");
|
|
|
|
|
|
case 0xE06D7363: return ("Microsoft C++ Exception");
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD> <20><> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>...
|
|
|
|
|
|
static CHAR szFaultReason[2048] = "";
|
|
|
|
|
|
::strcpy(szFaultReason, ("Unknown"));
|
|
|
|
|
|
::FormatMessageA(
|
|
|
|
|
|
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
|
|
::GetModuleHandleA(("ntdll.dll")),
|
|
|
|
|
|
pExPtrs->ExceptionRecord->ExceptionCode,
|
|
|
|
|
|
0,
|
|
|
|
|
|
szFaultReason,
|
|
|
|
|
|
0,
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
|
|
return szFaultReason;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char*
|
|
|
|
|
|
GetRegisterString(EXCEPTION_POINTERS* pExPtrs)
|
|
|
|
|
|
{
|
|
|
|
|
|
static CHAR szBuff[8192*4] = {0,};
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _X86_
|
|
|
|
|
|
// This call puts 48 bytes on the stack, which could be a problem if
|
|
|
|
|
|
// the stack is blown.
|
|
|
|
|
|
sprintf(szBuff ,
|
|
|
|
|
|
"EAX=%08X EBX=%08X ECX=%08X EDX=%08X ESI=%08X\n"\
|
|
|
|
|
|
"EDI=%08X EBP=%08X ESP=%08X EIP=%08X FLG=%08X\n"\
|
|
|
|
|
|
"CS=%04X DS=%04X SS=%04X ES=%04X "\
|
|
|
|
|
|
"FS=%04X GS=%04X" ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Eax ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Ebx ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Ecx ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Edx ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Esi ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Edi ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Ebp ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Esp ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Eip ,
|
|
|
|
|
|
pExPtrs->ContextRecord->EFlags ,
|
|
|
|
|
|
pExPtrs->ContextRecord->SegCs ,
|
|
|
|
|
|
pExPtrs->ContextRecord->SegDs ,
|
|
|
|
|
|
pExPtrs->ContextRecord->SegSs ,
|
|
|
|
|
|
pExPtrs->ContextRecord->SegEs ,
|
|
|
|
|
|
pExPtrs->ContextRecord->SegFs ,
|
|
|
|
|
|
pExPtrs->ContextRecord->SegGs ) ;
|
|
|
|
|
|
#elif _AMD64_
|
|
|
|
|
|
sprintf ( szBuff ,
|
|
|
|
|
|
"RAX=%016X RBX=%016X RCX=%016X RDX=%016X RSI=%016X\n"\
|
|
|
|
|
|
"RDI=%016X RBP=%016X RSP=%016X RIP=%016X FLG=%016X\n"\
|
|
|
|
|
|
" R8=%016X R9=%016X R10=%016X R11=%016X R12=%016X\n"\
|
|
|
|
|
|
"R13=%016X R14=%016X R15=%016X" ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rax ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rbx ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rcx ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rdx ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rsi ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rdi ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rbp ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rsp ,
|
|
|
|
|
|
pExPtrs->ContextRecord->Rip ,
|
|
|
|
|
|
pExPtrs->ContextRecord->EFlags ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R8 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R9 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R10 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R11 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R12 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R13 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R14 ,
|
|
|
|
|
|
pExPtrs->ContextRecord->R15 ) ;
|
|
|
|
|
|
#elif _IA64_
|
|
|
|
|
|
#pragma message ( "IA64 NOT DEFINED!!" )
|
|
|
|
|
|
#pragma FORCE COMPILATION ABORT!
|
|
|
|
|
|
#else
|
|
|
|
|
|
#pragma message ( "CPU NOT DEFINED!!" )
|
|
|
|
|
|
#pragma FORCE COMPILATION ABORT!
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
return ( szBuff ) ;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char* __stdcall
|
|
|
|
|
|
GetFirstStackTraceString( DWORD dwOpts, EXCEPTION_POINTERS * pExPtrs )
|
|
|
|
|
|
{
|
|
|
|
|
|
assert ( FALSE == IsBadReadPtr ( pExPtrs , sizeof(EXCEPTION_POINTERS*)) );
|
|
|
|
|
|
|
|
|
|
|
|
if ( TRUE == IsBadReadPtr ( pExPtrs, sizeof(EXCEPTION_POINTERS*)) )
|
|
|
|
|
|
{
|
|
|
|
|
|
return ("GetFirstStackTraceString - invalid pExPtrs!\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get the stack frame filled in.
|
|
|
|
|
|
FillInStackFrame ( pExPtrs->ContextRecord ) ;
|
|
|
|
|
|
|
|
|
|
|
|
// Copy over the exception pointers fields so I don't corrupt the
|
|
|
|
|
|
// real one.
|
|
|
|
|
|
g_stContext = *(pExPtrs->ContextRecord) ;
|
|
|
|
|
|
|
|
|
|
|
|
return ( InternalGetStackTraceString ( dwOpts ) ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char* __stdcall
|
|
|
|
|
|
GetNextStackTraceString( DWORD dwOpts, EXCEPTION_POINTERS* pExPtrs )
|
|
|
|
|
|
{
|
|
|
|
|
|
// All error checking is in InternalGetStackTraceString.
|
|
|
|
|
|
// Assume that GetFirstStackTraceString has already initialized the
|
|
|
|
|
|
// stack frame information.
|
|
|
|
|
|
return ( InternalGetStackTraceString ( dwOpts ) ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function to isolate filling out the stack frame, which is CPU
|
|
|
|
|
|
// specific.
|
|
|
|
|
|
void FillInStackFrame ( PCONTEXT pCtx )
|
|
|
|
|
|
{
|
|
|
|
|
|
// Initialize the STACKFRAME structure.
|
|
|
|
|
|
ZeroMemory ( &g_stFrame , sizeof ( STACKFRAME64 ) ) ;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _X86_
|
|
|
|
|
|
g_stFrame.AddrPC.Offset = pCtx->Eip ;
|
|
|
|
|
|
g_stFrame.AddrPC.Mode = AddrModeFlat ;
|
|
|
|
|
|
g_stFrame.AddrStack.Offset = pCtx->Esp ;
|
|
|
|
|
|
g_stFrame.AddrStack.Mode = AddrModeFlat ;
|
|
|
|
|
|
g_stFrame.AddrFrame.Offset = pCtx->Ebp ;
|
|
|
|
|
|
g_stFrame.AddrFrame.Mode = AddrModeFlat ;
|
|
|
|
|
|
#elif _AMD64_
|
|
|
|
|
|
g_stFrame.AddrPC.Offset = pCtx->Rip ;
|
|
|
|
|
|
g_stFrame.AddrPC.Mode = AddrModeFlat ;
|
|
|
|
|
|
g_stFrame.AddrStack.Offset = pCtx->Rsp ;
|
|
|
|
|
|
g_stFrame.AddrStack.Mode = AddrModeFlat ;
|
|
|
|
|
|
g_stFrame.AddrFrame.Offset = pCtx->Rbp ;
|
|
|
|
|
|
g_stFrame.AddrFrame.Mode = AddrModeFlat ;
|
|
|
|
|
|
#elif _IA64_
|
|
|
|
|
|
#pragma message ( "IA64 NOT DEFINED!!" )
|
|
|
|
|
|
#pragma FORCE COMPILATION ABORT!
|
|
|
|
|
|
#else
|
|
|
|
|
|
#pragma message ( "CPU NOT DEFINED!!" )
|
|
|
|
|
|
#pragma FORCE COMPILATION ABORT!
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Initializes the symbol engine if needed
|
|
|
|
|
|
void InitSymEng ( void )
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( FALSE == g_bSymEngInit )
|
|
|
|
|
|
{
|
|
|
|
|
|
// Set up the symbol engine.
|
|
|
|
|
|
DWORD dwOpts = SymGetOptions ( ) ;
|
|
|
|
|
|
|
|
|
|
|
|
// Turn on line loading.
|
|
|
|
|
|
SymSetOptions ( dwOpts |
|
|
|
|
|
|
SYMOPT_LOAD_LINES ) ;
|
|
|
|
|
|
|
|
|
|
|
|
// Force the invade process flag on.
|
|
|
|
|
|
BOOL bRet = SymInitialize ( GetCurrentProcess ( ) ,
|
|
|
|
|
|
NULL ,
|
|
|
|
|
|
TRUE ) ;
|
|
|
|
|
|
assert ( TRUE == bRet ) ;
|
|
|
|
|
|
g_bSymEngInit = bRet ;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cleans up the symbol engine if needed
|
|
|
|
|
|
void CleanupSymEng ( void )
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( TRUE == g_bSymEngInit )
|
|
|
|
|
|
{
|
|
|
|
|
|
assert ( SymCleanup ( GetCurrentProcess ( ) ) ) ;
|
|
|
|
|
|
g_bSymEngInit = FALSE ;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL __stdcall CH_ReadProcessMemory ( HANDLE ,
|
|
|
|
|
|
DWORD64 qwBaseAddress ,
|
|
|
|
|
|
PVOID lpBuffer ,
|
|
|
|
|
|
DWORD nSize ,
|
|
|
|
|
|
LPDWORD lpNumberOfBytesRead )
|
|
|
|
|
|
{
|
|
|
|
|
|
return ( ReadProcessMemory ( GetCurrentProcess ( ) ,
|
|
|
|
|
|
(LPCVOID)qwBaseAddress ,
|
|
|
|
|
|
lpBuffer ,
|
|
|
|
|
|
nSize ,
|
|
|
|
|
|
(SIZE_T*)lpNumberOfBytesRead ) ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The internal function that does all the stack walking
|
|
|
|
|
|
const char* InternalGetStackTraceString ( DWORD dwOpts )
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
// The value that is returned
|
|
|
|
|
|
const char* szRet ;
|
|
|
|
|
|
// The module base address. I look this up right after the stack
|
|
|
|
|
|
// walk to ensure that the module is valid.
|
|
|
|
|
|
DWORD64 dwModBase ;
|
|
|
|
|
|
|
|
|
|
|
|
__try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Initialize the symbol engine in case it isn't initialized.
|
|
|
|
|
|
InitSymEng ( ) ;
|
|
|
|
|
|
|
|
|
|
|
|
// Note: If the source file and line number functions are used,
|
|
|
|
|
|
// StackWalk can cause an access violation.
|
|
|
|
|
|
BOOL bSWRet = StackWalk64 ( CH_MACHINE ,
|
|
|
|
|
|
GetCurrentProcess ( ) ,
|
|
|
|
|
|
GetCurrentThread ( ) ,
|
|
|
|
|
|
&g_stFrame ,
|
|
|
|
|
|
&g_stContext ,
|
|
|
|
|
|
CH_ReadProcessMemory ,
|
|
|
|
|
|
SymFunctionTableAccess64 ,
|
|
|
|
|
|
SymGetModuleBase64 ,
|
|
|
|
|
|
NULL );
|
|
|
|
|
|
if ( ( FALSE == bSWRet ) || ( 0 == g_stFrame.AddrFrame.Offset ))
|
|
|
|
|
|
{
|
|
|
|
|
|
szRet = NULL ;
|
|
|
|
|
|
__leave ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Before I get too carried away and start calculating
|
|
|
|
|
|
// everything, I need to double-check that the address returned
|
|
|
|
|
|
// by StackWalk really exists. I've seen cases in which
|
|
|
|
|
|
// StackWalk returns TRUE but the address doesn't belong to
|
|
|
|
|
|
// a module in the process.
|
|
|
|
|
|
dwModBase = SymGetModuleBase64 ( GetCurrentProcess ( ) ,
|
|
|
|
|
|
g_stFrame.AddrPC.Offset ) ;
|
|
|
|
|
|
if ( 0 == dwModBase )
|
|
|
|
|
|
{
|
|
|
|
|
|
szRet = NULL ;
|
|
|
|
|
|
__leave ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int iCurr = 0 ;
|
|
|
|
|
|
|
|
|
|
|
|
// At a minimum, put in the address.
|
|
|
|
|
|
#ifdef _WIN64
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr ,
|
|
|
|
|
|
( "0x%016X" ) ,
|
|
|
|
|
|
g_stFrame.AddrPC.Offset ) ;
|
|
|
|
|
|
#else
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr ,
|
|
|
|
|
|
( "%04X:%08X" ) ,
|
|
|
|
|
|
g_stContext.SegCs ,
|
|
|
|
|
|
g_stFrame.AddrPC.Offset ) ;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// Output the parameters?
|
|
|
|
|
|
if ( GSTSO_PARAMS == ( dwOpts & GSTSO_PARAMS ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr ,
|
|
|
|
|
|
k_PARAMFMTSTRING ,
|
|
|
|
|
|
g_stFrame.Params[ 0 ] ,
|
|
|
|
|
|
g_stFrame.Params[ 1 ] ,
|
|
|
|
|
|
g_stFrame.Params[ 2 ] ,
|
|
|
|
|
|
g_stFrame.Params[ 3 ] ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Output the module name.
|
|
|
|
|
|
if ( GSTSO_MODULE == ( dwOpts & GSTSO_MODULE ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr , ( " " ) ) ;
|
|
|
|
|
|
|
|
|
|
|
|
assert ( iCurr < ( BUFF_SIZE - MAX_PATH ) ) ;
|
|
|
|
|
|
iCurr += BSUGetModuleBaseNameA ( GetCurrentProcess ( ) ,
|
|
|
|
|
|
(HINSTANCE)dwModBase ,
|
|
|
|
|
|
g_szBuff + iCurr ,
|
|
|
|
|
|
BUFF_SIZE - iCurr ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
assert ( iCurr < ( BUFF_SIZE - MAX_PATH ) ) ;
|
|
|
|
|
|
DWORD64 dwDisp ;
|
|
|
|
|
|
|
|
|
|
|
|
// Output the symbol name?
|
|
|
|
|
|
if ( GSTSO_SYMBOL == ( dwOpts & GSTSO_SYMBOL ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
// Start looking up the exception address.
|
|
|
|
|
|
PIMAGEHLP_SYMBOL64 pSym = (PIMAGEHLP_SYMBOL64)&g_stSymbol ;
|
|
|
|
|
|
ZeroMemory ( pSym , SYM_BUFF_SIZE ) ;
|
|
|
|
|
|
pSym->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL64 ) ;
|
|
|
|
|
|
pSym->MaxNameLength = SYM_BUFF_SIZE -
|
|
|
|
|
|
sizeof ( IMAGEHLP_SYMBOL64 ) ;
|
|
|
|
|
|
pSym->Address = g_stFrame.AddrPC.Offset ;
|
|
|
|
|
|
|
|
|
|
|
|
if ( TRUE ==
|
|
|
|
|
|
SymGetSymFromAddr64 ( GetCurrentProcess ( ) ,
|
|
|
|
|
|
g_stFrame.AddrPC.Offset ,
|
|
|
|
|
|
&dwDisp ,
|
|
|
|
|
|
pSym ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( dwOpts & ~GSTSO_SYMBOL )
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr , ( "," ));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Copy no more symbol information than there's room
|
|
|
|
|
|
// for. Symbols are ANSI
|
|
|
|
|
|
int iLen = (int)strlen ( pSym->Name ) ;
|
|
|
|
|
|
if ( iLen > ( BUFF_SIZE - iCurr -
|
|
|
|
|
|
( MAX_SYM_SIZE + 50 ) ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
strncpy ( g_szBuff + iCurr ,
|
|
|
|
|
|
pSym->Name ,
|
|
|
|
|
|
BUFF_SIZE - iCurr - 1 ) ;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Gotta leave now
|
|
|
|
|
|
szRet = g_szBuff ;
|
|
|
|
|
|
__leave ;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( dwDisp > 0 )
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr ,
|
|
|
|
|
|
k_NAMEDISPFMT ,
|
|
|
|
|
|
pSym->Name ,
|
|
|
|
|
|
dwDisp ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr ,
|
|
|
|
|
|
k_NAMEFMT ,
|
|
|
|
|
|
pSym->Name ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// If the symbol wasn't found, the source file and line
|
|
|
|
|
|
// number won't be found either, so leave now.
|
|
|
|
|
|
szRet = g_szBuff ;
|
|
|
|
|
|
__leave ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
assert ( iCurr < ( BUFF_SIZE - MAX_PATH ) ) ;
|
|
|
|
|
|
|
|
|
|
|
|
// Output the source file and line number information?
|
|
|
|
|
|
if ( GSTSO_SRCLINE == ( dwOpts & GSTSO_SRCLINE ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
ZeroMemory ( &g_stLine , sizeof ( IMAGEHLP_LINE64 ) ) ;
|
|
|
|
|
|
g_stLine.SizeOfStruct = sizeof ( IMAGEHLP_LINE64 ) ;
|
|
|
|
|
|
|
|
|
|
|
|
DWORD dwLineDisp ;
|
|
|
|
|
|
if ( TRUE == SymGetLineFromAddr64 ( GetCurrentProcess ( ) ,
|
|
|
|
|
|
g_stFrame.AddrPC.Offset,
|
|
|
|
|
|
&dwLineDisp ,
|
|
|
|
|
|
&g_stLine ))
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( dwOpts & ~GSTSO_SRCLINE )
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr , ( "," ));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Copy no more of the source file and line number
|
|
|
|
|
|
// information than there's room for.
|
|
|
|
|
|
int iLen = lstrlenA ( g_stLine.FileName ) ;
|
|
|
|
|
|
if ( iLen > ( BUFF_SIZE - iCurr -
|
|
|
|
|
|
( MAX_PATH + 50 ) ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
strncpy ( g_szBuff + iCurr ,
|
|
|
|
|
|
g_stLine.FileName ,
|
|
|
|
|
|
BUFF_SIZE - iCurr - 1 ) ;
|
|
|
|
|
|
|
|
|
|
|
|
// Gotta leave now
|
|
|
|
|
|
szRet = g_szBuff ;
|
|
|
|
|
|
__leave ;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( dwLineDisp > 0 )
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf( g_szBuff + iCurr ,
|
|
|
|
|
|
k_FILELINEDISPFMT ,
|
|
|
|
|
|
g_stLine.FileName ,
|
|
|
|
|
|
g_stLine.LineNumber ,
|
|
|
|
|
|
dwLineDisp ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
iCurr += sprintf ( g_szBuff + iCurr ,
|
|
|
|
|
|
k_FILELINEFMT ,
|
|
|
|
|
|
g_stLine.FileName ,
|
|
|
|
|
|
g_stLine.LineNumber ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
szRet = g_szBuff ;
|
|
|
|
|
|
}
|
|
|
|
|
|
__except ( EXCEPTION_EXECUTE_HANDLER )
|
|
|
|
|
|
{
|
|
|
|
|
|
assert ( !"Crashed in InternalGetStackTraceString" ) ;
|
|
|
|
|
|
szRet = NULL ;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ( szRet ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper define to let code compile on pre W2K systems.
|
|
|
|
|
|
#if _WIN32 >= 0x500
|
|
|
|
|
|
#define GET_THREAD_ACP() CP_THREAD_ACP
|
|
|
|
|
|
#else
|
|
|
|
|
|
#define GET_THREAD_ACP() GetACP ()
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DWORD __stdcall
|
|
|
|
|
|
BSUWide2Ansi ( const wchar_t * szWide ,
|
|
|
|
|
|
char * szANSI ,
|
|
|
|
|
|
int iANSILen)
|
|
|
|
|
|
{
|
|
|
|
|
|
assert ( NULL != szWide ) ;
|
|
|
|
|
|
assert ( NULL != szANSI ) ;
|
|
|
|
|
|
assert ( FALSE == IsBadStringPtrW ( szWide , MAX_PATH ) ) ;
|
|
|
|
|
|
|
|
|
|
|
|
int iRet = WideCharToMultiByte ( GET_THREAD_ACP() ,
|
|
|
|
|
|
0 ,
|
|
|
|
|
|
szWide ,
|
|
|
|
|
|
-1 ,
|
|
|
|
|
|
szANSI ,
|
|
|
|
|
|
iANSILen ,
|
|
|
|
|
|
NULL ,
|
|
|
|
|
|
NULL ) ;
|
|
|
|
|
|
return ( iRet ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DWORD __stdcall
|
|
|
|
|
|
BSUAnsi2Wide ( const char * szANSI ,
|
|
|
|
|
|
wchar_t * szWide ,
|
|
|
|
|
|
int iWideLen )
|
|
|
|
|
|
{
|
|
|
|
|
|
assert ( NULL != szWide ) ;
|
|
|
|
|
|
assert ( NULL != szANSI ) ;
|
|
|
|
|
|
assert ( FALSE == IsBadStringPtrA ( szANSI , MAX_PATH ) ) ;
|
|
|
|
|
|
|
|
|
|
|
|
int iRet = MultiByteToWideChar ( GET_THREAD_ACP ( ) ,
|
|
|
|
|
|
0 ,
|
|
|
|
|
|
szANSI ,
|
|
|
|
|
|
-1 ,
|
|
|
|
|
|
szWide ,
|
|
|
|
|
|
iWideLen ) ;
|
|
|
|
|
|
return ( iRet ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DWORD __stdcall NTGetModuleBaseNameW ( HANDLE hProcess ,
|
|
|
|
|
|
HMODULE hModule ,
|
|
|
|
|
|
LPWSTR lpBaseName ,
|
|
|
|
|
|
DWORD nSize )
|
|
|
|
|
|
{
|
|
|
|
|
|
// Initialize PSAPI.DLL, if needed.
|
|
|
|
|
|
if ( FALSE == InitPSAPI ( ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
assert ( !"InitiPSAPI failed!" ) ;
|
|
|
|
|
|
SetLastErrorEx ( ERROR_DLL_INIT_FAILED , SLE_ERROR ) ;
|
|
|
|
|
|
return ( FALSE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ( g_pGetModuleBaseName ( hProcess ,
|
|
|
|
|
|
hModule ,
|
|
|
|
|
|
lpBaseName ,
|
|
|
|
|
|
nSize ) ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
Function Implementation
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
DWORD __stdcall BSUGetModuleBaseNameA ( HANDLE hProcess ,
|
|
|
|
|
|
HMODULE hModule ,
|
|
|
|
|
|
LPSTR lpBaseName ,
|
|
|
|
|
|
DWORD nSize )
|
|
|
|
|
|
{
|
|
|
|
|
|
wchar_t * pWideName = (wchar_t*)HeapAlloc ( GetProcessHeap ( ) ,
|
|
|
|
|
|
HEAP_GENERATE_EXCEPTIONS|
|
|
|
|
|
|
HEAP_ZERO_MEMORY ,
|
|
|
|
|
|
nSize * sizeof(wchar_t));
|
|
|
|
|
|
DWORD dwRet = BSUGetModuleBaseNameW ( hProcess ,
|
|
|
|
|
|
hModule ,
|
|
|
|
|
|
pWideName ,
|
|
|
|
|
|
nSize ) ;
|
|
|
|
|
|
if ( 0 != dwRet )
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( FALSE == BSUWide2Ansi ( pWideName , lpBaseName , nSize ) )
|
|
|
|
|
|
{
|
|
|
|
|
|
dwRet = 0 ;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//VERIFY ( HeapFree ( GetProcessHeap ( ) , 0 , pWideName ) ) ;
|
|
|
|
|
|
return ( dwRet ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DWORD __stdcall BSUGetModuleBaseNameW ( HANDLE hProcess ,
|
|
|
|
|
|
HMODULE hModule ,
|
|
|
|
|
|
LPWSTR lpBaseName ,
|
|
|
|
|
|
DWORD nSize )
|
|
|
|
|
|
{
|
|
|
|
|
|
// Call the NT version. It is in NT4ProcessInfo because that is
|
|
|
|
|
|
// where all the PSAPI wrappers are kept.
|
|
|
|
|
|
return ( NTGetModuleBaseNameW ( hProcess ,
|
|
|
|
|
|
hModule ,
|
|
|
|
|
|
lpBaseName ,
|
|
|
|
|
|
nSize ) ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------
|
|
|
|
|
|
FUNCTION : InitPSAPI
|
|
|
|
|
|
DISCUSSION :
|
|
|
|
|
|
Loads PSAPI.DLL and initializes all the pointers needed by this
|
|
|
|
|
|
file. If BugslayerUtil.DLL statically linked to PSAPI.DLL, it would not
|
|
|
|
|
|
work on Windows9x.
|
|
|
|
|
|
Note that I conciously chose to allow the resource leak on loading
|
|
|
|
|
|
PSAPI.DLL.
|
|
|
|
|
|
PARAMETERS :
|
|
|
|
|
|
None.
|
|
|
|
|
|
RETURNS :
|
|
|
|
|
|
TRUE - Everything initialized properly.
|
|
|
|
|
|
FALSE - There was a problem.
|
|
|
|
|
|
----------------------------------------------------------------------*/
|
|
|
|
|
|
static BOOL InitPSAPI ( void )
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( TRUE == g_bInitialized )
|
|
|
|
|
|
{
|
|
|
|
|
|
return ( TRUE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Load up PSAPI.DLL.
|
|
|
|
|
|
HINSTANCE hInst = LoadLibraryA( "PSAPI.DLL" ) ;
|
|
|
|
|
|
assert ( NULL != hInst ) ;
|
|
|
|
|
|
if ( NULL == hInst )
|
|
|
|
|
|
{
|
|
|
|
|
|
TRACE ( "Unable to load PSAPI.DLL!\n" ) ;
|
|
|
|
|
|
return ( FALSE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now do the GetProcAddress stuff.
|
|
|
|
|
|
g_pEnumProcessModules =
|
|
|
|
|
|
(ENUMPROCESSMODULES)GetProcAddress ( hInst ,
|
|
|
|
|
|
"EnumProcessModules" ) ;
|
|
|
|
|
|
assert ( NULL != g_pEnumProcessModules ) ;
|
|
|
|
|
|
if ( NULL == g_pEnumProcessModules )
|
|
|
|
|
|
{
|
|
|
|
|
|
TRACE ( "GetProcAddress failed on EnumProcessModules!\n" ) ;
|
|
|
|
|
|
FreeLibrary(hInst);
|
|
|
|
|
|
return ( FALSE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
g_pGetModuleBaseName =
|
|
|
|
|
|
(GETMODULEBASENAMEW)GetProcAddress ( hInst ,
|
|
|
|
|
|
"GetModuleBaseNameW" ) ;
|
|
|
|
|
|
assert ( NULL != g_pGetModuleBaseName ) ;
|
|
|
|
|
|
if ( NULL == g_pGetModuleBaseName )
|
|
|
|
|
|
{
|
|
|
|
|
|
TRACE ( "GetProcAddress failed on GetModuleBaseNameW!\n" ) ;
|
|
|
|
|
|
FreeLibrary(hInst);
|
|
|
|
|
|
return ( FALSE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
g_pGetModuleFileNameEx =
|
|
|
|
|
|
(GETMODULEFILENAMEEXW)GetProcAddress ( hInst ,
|
|
|
|
|
|
"GetModuleFileNameExW" );
|
|
|
|
|
|
assert ( NULL != g_pGetModuleFileNameEx ) ;
|
|
|
|
|
|
if ( NULL == g_pGetModuleFileNameEx )
|
|
|
|
|
|
{
|
|
|
|
|
|
TRACE ( "GetProcAddress failed on GetModuleFileNameExW\n" ) ;
|
|
|
|
|
|
FreeLibrary(hInst);
|
|
|
|
|
|
return ( FALSE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// All OK, Jumpmaster!
|
|
|
|
|
|
g_bInitialized = TRUE ;
|
|
|
|
|
|
return ( TRUE ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void NxGetCallStack(OUT std::string& szString, EXCEPTION_POINTERS* pExPtrs, DWORD dwOpt )
|
|
|
|
|
|
{
|
|
|
|
|
|
szString = "";
|
|
|
|
|
|
|
|
|
|
|
|
#define _ALL (GSTSO_PARAMS|GSTSO_MODULE|GSTSO_SYMBOL|GSTSO_SRCLINE)
|
|
|
|
|
|
|
|
|
|
|
|
const char* szBuff = GetFirstStackTraceString ( dwOpt , pExPtrs ) ;
|
|
|
|
|
|
while ( NULL != szBuff )
|
|
|
|
|
|
{
|
|
|
|
|
|
szString += szBuff;
|
|
|
|
|
|
szString += "\n";
|
|
|
|
|
|
szBuff = GetNextStackTraceString ( dwOpt , pExPtrs ) ;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|