windows-nt/Source/XPSP1/NT/windows/appcompat/appverifier/userdump.cpp
2020-09-26 16:20:57 +08:00

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;
}