574 lines
15 KiB
C++
574 lines
15 KiB
C++
|
|
#include "precomp.h"
|
|
|
|
#include <crash.h>
|
|
|
|
#include "Debugger.h"
|
|
#include "UserDumpp.h"
|
|
|
|
#define MEM_SIZE (64*1024)
|
|
|
|
|
|
//
|
|
// Private data structure used for communcating
|
|
// crash dump data to the callback function.
|
|
//
|
|
typedef struct _CRASH_DUMP_INFO
|
|
{
|
|
PPROCESS_INFO pProcess;
|
|
EXCEPTION_DEBUG_INFO* ExceptionInfo;
|
|
DWORD MemoryCount;
|
|
DWORD_PTR Address;
|
|
PUCHAR MemoryData;
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
SIZE_T MbiOffset;
|
|
SIZE_T MbiRemaining;
|
|
PTHREAD_INFO pCurrentThread;
|
|
IMAGEHLP_MODULE mi;
|
|
PCRASH_MODULE CrashModule;
|
|
} CRASH_DUMP_INFO, *PCRASH_DUMP_INFO;
|
|
|
|
//
|
|
// Local function prototypes
|
|
//
|
|
|
|
DWORD_PTR GetTeb( HANDLE hThread )
|
|
{
|
|
NTSTATUS Status;
|
|
THREAD_BASIC_INFORMATION ThreadBasicInfo;
|
|
DWORD_PTR Address = 0;
|
|
|
|
Status = NtQueryInformationThread( hThread,
|
|
ThreadBasicInformation,
|
|
&ThreadBasicInfo,
|
|
sizeof( ThreadBasicInfo ),
|
|
NULL );
|
|
if ( NT_SUCCESS(Status) )
|
|
{
|
|
Address = (DWORD_PTR)ThreadBasicInfo.TebBaseAddress;
|
|
}
|
|
|
|
return Address;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CrashDumpCallback(
|
|
IN DWORD DataType, // requested data type
|
|
OUT PVOID* DumpData, // pointer to a pointer to the data
|
|
OUT LPDWORD DumpDataLength, // pointer to the data length
|
|
IN OUT PVOID cdi // private data
|
|
)
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: This function is the callback used by crashlib.
|
|
Its purpose is to provide data to CreateUserDump()
|
|
for writting to the crashdump file.
|
|
--*/
|
|
|
|
{
|
|
PCRASH_DUMP_INFO CrashdumpInfo = (PCRASH_DUMP_INFO)cdi;
|
|
|
|
switch ( DataType )
|
|
{
|
|
case DMP_DEBUG_EVENT:
|
|
*DumpData = &CrashdumpInfo->pProcess->DebugEvent;
|
|
*DumpDataLength = sizeof(DEBUG_EVENT);
|
|
break;
|
|
|
|
case DMP_THREAD_STATE:
|
|
{
|
|
static CRASH_THREAD CrashThread;
|
|
PTHREAD_INFO pCurrentThread;
|
|
|
|
*DumpData = &CrashThread;
|
|
|
|
if ( CrashdumpInfo->pCurrentThread == NULL )
|
|
{
|
|
pCurrentThread = CrashdumpInfo->pProcess->pFirstThreadInfo;
|
|
}
|
|
else
|
|
{
|
|
pCurrentThread = CrashdumpInfo->pCurrentThread->pNext;
|
|
}
|
|
|
|
CrashdumpInfo->pCurrentThread = pCurrentThread;
|
|
|
|
if ( pCurrentThread == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&CrashThread, sizeof(CrashThread));
|
|
|
|
CrashThread.ThreadId = pCurrentThread->dwThreadId;
|
|
CrashThread.SuspendCount = SuspendThread(pCurrentThread->hThread);
|
|
|
|
if ( CrashThread.SuspendCount != (DWORD)-1 )
|
|
{
|
|
ResumeThread(pCurrentThread->hThread);
|
|
}
|
|
|
|
CrashThread.PriorityClass = GetPriorityClass(CrashdumpInfo->pProcess->hProcess);
|
|
CrashThread.Priority = GetThreadPriority(pCurrentThread->hThread);
|
|
CrashThread.Teb = GetTeb(pCurrentThread->hThread);
|
|
|
|
*DumpDataLength = sizeof(CRASH_THREAD);
|
|
break;
|
|
}
|
|
|
|
case DMP_MEMORY_BASIC_INFORMATION:
|
|
while ( TRUE )
|
|
{
|
|
CrashdumpInfo->Address += CrashdumpInfo->mbi.RegionSize;
|
|
|
|
if ( !VirtualQueryEx(CrashdumpInfo->pProcess->hProcess,
|
|
(LPVOID)CrashdumpInfo->Address,
|
|
&CrashdumpInfo->mbi,
|
|
sizeof(MEMORY_BASIC_INFORMATION)) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (CrashdumpInfo->mbi.Protect & PAGE_GUARD) ||
|
|
(CrashdumpInfo->mbi.Protect & PAGE_NOACCESS) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( (CrashdumpInfo->mbi.State & MEM_FREE) ||
|
|
(CrashdumpInfo->mbi.State & MEM_RESERVE) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
*DumpData = &CrashdumpInfo->mbi;
|
|
*DumpDataLength = sizeof(MEMORY_BASIC_INFORMATION);
|
|
break;
|
|
|
|
case DMP_THREAD_CONTEXT:
|
|
{
|
|
PTHREAD_INFO pCurrentThread;
|
|
|
|
if ( CrashdumpInfo->pCurrentThread == NULL )
|
|
{
|
|
pCurrentThread = CrashdumpInfo->pProcess->pFirstThreadInfo;
|
|
}
|
|
else
|
|
{
|
|
pCurrentThread = CrashdumpInfo->pCurrentThread->pNext;
|
|
}
|
|
|
|
CrashdumpInfo->pCurrentThread = pCurrentThread;
|
|
|
|
if ( pCurrentThread == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*DumpData = &CrashdumpInfo->pCurrentThread->Context;
|
|
*DumpDataLength = sizeof(CONTEXT);
|
|
break;
|
|
}
|
|
|
|
case DMP_MODULE:
|
|
if ( CrashdumpInfo->mi.BaseOfImage == 0 )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CrashdumpInfo->CrashModule->BaseOfImage = CrashdumpInfo->mi.BaseOfImage;
|
|
CrashdumpInfo->CrashModule->SizeOfImage = CrashdumpInfo->mi.ImageSize;
|
|
CrashdumpInfo->CrashModule->ImageNameLength = strlen(CrashdumpInfo->mi.ImageName) + 1;
|
|
strcpy( CrashdumpInfo->CrashModule->ImageName, CrashdumpInfo->mi.ImageName );
|
|
|
|
*DumpData = CrashdumpInfo->CrashModule;
|
|
*DumpDataLength = sizeof(CRASH_MODULE) + CrashdumpInfo->CrashModule->ImageNameLength;
|
|
|
|
if ( !SymGetModuleInfo(CrashdumpInfo->pProcess->hProcess,
|
|
(DWORD_PTR)-1,
|
|
&CrashdumpInfo->mi) )
|
|
{
|
|
CrashdumpInfo->mi.BaseOfImage = 0;
|
|
}
|
|
break;
|
|
|
|
case DMP_MEMORY_DATA:
|
|
if ( !CrashdumpInfo->MemoryCount )
|
|
{
|
|
|
|
CrashdumpInfo->Address = 0;
|
|
CrashdumpInfo->MbiOffset = 0;
|
|
CrashdumpInfo->MbiRemaining = 0;
|
|
|
|
ZeroMemory( &CrashdumpInfo->mbi, sizeof(MEMORY_BASIC_INFORMATION) );
|
|
|
|
CrashdumpInfo->MemoryData = (PUCHAR)VirtualAlloc(NULL,
|
|
MEM_SIZE,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
}
|
|
|
|
if ( !CrashdumpInfo->MbiRemaining )
|
|
{
|
|
while ( TRUE )
|
|
{
|
|
CrashdumpInfo->Address += CrashdumpInfo->mbi.RegionSize;
|
|
|
|
if ( !VirtualQueryEx(CrashdumpInfo->pProcess->hProcess,
|
|
(LPVOID)CrashdumpInfo->Address,
|
|
&CrashdumpInfo->mbi,
|
|
sizeof(MEMORY_BASIC_INFORMATION)) )
|
|
{
|
|
|
|
if ( CrashdumpInfo->MemoryData )
|
|
{
|
|
VirtualFree(CrashdumpInfo->MemoryData, MEM_SIZE, MEM_RELEASE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (CrashdumpInfo->mbi.Protect & PAGE_GUARD) ||
|
|
(CrashdumpInfo->mbi.Protect & PAGE_NOACCESS) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( (CrashdumpInfo->mbi.State & MEM_FREE) ||
|
|
(CrashdumpInfo->mbi.State & MEM_RESERVE) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CrashdumpInfo->MbiOffset = 0;
|
|
CrashdumpInfo->MbiRemaining = CrashdumpInfo->mbi.RegionSize;
|
|
CrashdumpInfo->MemoryCount += 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*DumpDataLength = (DWORD)__min( CrashdumpInfo->MbiRemaining, MEM_SIZE );
|
|
CrashdumpInfo->MbiRemaining -= *DumpDataLength;
|
|
|
|
ReadProcessMemory(CrashdumpInfo->pProcess->hProcess,
|
|
(PUCHAR)((DWORD_PTR)CrashdumpInfo->mbi.BaseAddress + CrashdumpInfo->MbiOffset),
|
|
CrashdumpInfo->MemoryData,
|
|
*DumpDataLength,
|
|
NULL);
|
|
|
|
*DumpData = CrashdumpInfo->MemoryData;
|
|
CrashdumpInfo->MbiOffset += *DumpDataLength;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CreateUserDump(
|
|
IN LPTSTR pszFileName,
|
|
IN PDBGHELP_CREATE_USER_DUMP_CALLBACK DmpCallback,
|
|
IN PVOID lpv
|
|
)
|
|
|
|
/*++
|
|
Return: TRUE on success, FALSE otherwise.
|
|
|
|
Desc: Creates the dump file.
|
|
--*/
|
|
{
|
|
OSVERSIONINFOW OsVersion = {0};
|
|
USERMODE_CRASHDUMP_HEADER DumpHeader = {0};
|
|
DWORD cb;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
BOOL rval;
|
|
PVOID DumpData;
|
|
DWORD DumpDataLength;
|
|
SECURITY_ATTRIBUTES SecAttrib;
|
|
SECURITY_DESCRIPTOR SecDescript;
|
|
|
|
//
|
|
// Create a DACL that allows all access to the directory.
|
|
//
|
|
SecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecAttrib.lpSecurityDescriptor = &SecDescript;
|
|
SecAttrib.bInheritHandle = FALSE;
|
|
|
|
InitializeSecurityDescriptor(&SecDescript, SECURITY_DESCRIPTOR_REVISION);
|
|
SetSecurityDescriptorDacl(&SecDescript, TRUE, NULL, FALSE);
|
|
|
|
hFile = CreateFile(pszFileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
&SecAttrib,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Write out an empty header.
|
|
//
|
|
if ( !WriteFile(hFile, &DumpHeader, sizeof(DumpHeader), &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
|
|
//
|
|
// Write the debug event.
|
|
//
|
|
DumpHeader.DebugEventOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
DmpCallback(DMP_DEBUG_EVENT, &DumpData, &DumpDataLength, lpv);
|
|
|
|
if ( !WriteFile(hFile, DumpData, sizeof(DEBUG_EVENT), &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
|
|
//
|
|
// Write the memory map.
|
|
//
|
|
DumpHeader.MemoryRegionOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
do
|
|
{
|
|
__try {
|
|
rval = DmpCallback(DMP_MEMORY_BASIC_INFORMATION,
|
|
&DumpData,
|
|
&DumpDataLength,
|
|
lpv);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
if ( rval )
|
|
{
|
|
DumpHeader.MemoryRegionCount += 1;
|
|
|
|
if ( !WriteFile(hFile, DumpData, sizeof(MEMORY_BASIC_INFORMATION), &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
}
|
|
} while ( rval );
|
|
|
|
//
|
|
// Write the thread contexts.
|
|
//
|
|
DumpHeader.ThreadOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
do
|
|
{
|
|
__try {
|
|
rval = DmpCallback(DMP_THREAD_CONTEXT,
|
|
&DumpData,
|
|
&DumpDataLength,
|
|
lpv);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
if ( rval )
|
|
{
|
|
if ( !WriteFile(hFile, DumpData, DumpDataLength, &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
|
|
DumpHeader.ThreadCount += 1;
|
|
}
|
|
|
|
} while ( rval );
|
|
|
|
//
|
|
// Write the thread states.
|
|
//
|
|
DumpHeader.ThreadStateOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
do
|
|
{
|
|
__try {
|
|
rval = DmpCallback(DMP_THREAD_STATE,
|
|
&DumpData,
|
|
&DumpDataLength,
|
|
lpv);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
if ( rval )
|
|
{
|
|
if ( !WriteFile(hFile, DumpData, sizeof(CRASH_THREAD), &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
}
|
|
|
|
} while ( rval );
|
|
|
|
//
|
|
// Write the module table.
|
|
//
|
|
DumpHeader.ModuleOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
do
|
|
{
|
|
__try {
|
|
rval = DmpCallback(DMP_MODULE,
|
|
&DumpData,
|
|
&DumpDataLength,
|
|
lpv);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
if ( rval )
|
|
{
|
|
if ( !WriteFile(hFile,
|
|
DumpData,
|
|
sizeof(CRASH_MODULE) + ((PCRASH_MODULE)DumpData)->ImageNameLength,
|
|
&cb,
|
|
NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
|
|
DumpHeader.ModuleCount += 1;
|
|
}
|
|
} while ( rval );
|
|
|
|
//
|
|
// Write the virtual memory
|
|
//
|
|
DumpHeader.DataOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
do
|
|
{
|
|
__try {
|
|
rval = DmpCallback(DMP_MEMORY_DATA,
|
|
&DumpData,
|
|
&DumpDataLength,
|
|
lpv);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
if ( rval )
|
|
{
|
|
if ( !WriteFile(hFile, DumpData, DumpDataLength, &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
}
|
|
} while ( rval );
|
|
|
|
//
|
|
// The VersionInfo is optional.
|
|
//
|
|
DumpHeader.VersionInfoOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
|
|
|
|
//
|
|
// re-write the dump header with some valid data.
|
|
//
|
|
GetVersionEx(&OsVersion);
|
|
|
|
DumpHeader.Signature = USERMODE_CRASHDUMP_SIGNATURE;
|
|
DumpHeader.MajorVersion = OsVersion.dwMajorVersion;
|
|
DumpHeader.MinorVersion = OsVersion.dwMinorVersion;
|
|
#ifdef _X86_
|
|
DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_I386;
|
|
DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP32;
|
|
#else
|
|
DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_IA64;
|
|
DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP64;
|
|
#endif
|
|
|
|
SetFilePointer(hFile, 0, 0, FILE_BEGIN);
|
|
|
|
if ( !WriteFile(hFile, &DumpHeader, sizeof(DumpHeader), &cb, NULL) )
|
|
{
|
|
goto bad_file;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return TRUE;
|
|
|
|
bad_file:
|
|
|
|
CloseHandle(hFile);
|
|
|
|
DeleteFile(pszFileName);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
GenerateUserModeDump(
|
|
LPTSTR pszFileName,
|
|
PPROCESS_INFO pProcess,
|
|
LPEXCEPTION_DEBUG_INFO ed
|
|
)
|
|
{
|
|
CRASH_DUMP_INFO CrashdumpInfo = {0};
|
|
BOOL bRet;
|
|
PTHREAD_INFO pThread;
|
|
|
|
CrashdumpInfo.mi.SizeOfStruct = sizeof(CrashdumpInfo.mi);
|
|
CrashdumpInfo.pProcess = pProcess;
|
|
CrashdumpInfo.ExceptionInfo = ed;
|
|
|
|
//
|
|
// Get the thread context for all the threads.
|
|
//
|
|
pThread = pProcess->pFirstThreadInfo;
|
|
|
|
while ( pThread != NULL )
|
|
{
|
|
pThread->Context.ContextFlags = CONTEXT_FULL;
|
|
GetThreadContext(pThread->hThread, &pThread->Context);
|
|
pThread = pThread->pNext;
|
|
}
|
|
|
|
//
|
|
// Get first entry in the module list.
|
|
//
|
|
if ( !SymInitialize(pProcess->hProcess, NULL, FALSE) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !SymGetModuleInfo(pProcess->hProcess, 0, &CrashdumpInfo.mi) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CrashdumpInfo.CrashModule = (PCRASH_MODULE)LocalAlloc(LPTR, 4096);
|
|
|
|
bRet = CreateUserDump(pszFileName, CrashDumpCallback, &CrashdumpInfo);
|
|
|
|
LocalFree(CrashdumpInfo.CrashModule);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|