/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: userdump.c Abstract: This module implements full user-mode dump writing. --*/ #include "private.h" // hack to make it build typedef ULONG UNICODE_STRING32; typedef ULONG UNICODE_STRING64; #include #include // This should match INVALID_SET_FILE_POINTER on 32-bit systems. #define DMPP_INVALID_OFFSET ((DWORD_PTR)-1) DWORD_PTR DmppGetFilePointer( HANDLE hFile ) { #ifdef _WIN64 LONG dwHigh = 0; DWORD dwLow; dwLow = SetFilePointer(hFile, 0, &dwHigh, FILE_CURRENT); if (dwLow == INVALID_SET_FILE_POINTER && GetLastError()) { return DMPP_INVALID_OFFSET; } else { return dwLow | ((DWORD_PTR)dwHigh << 32); } #else return SetFilePointer(hFile, 0, NULL, FILE_CURRENT); #endif } BOOL DmppWriteAll( HANDLE hFile, LPVOID pBuffer, DWORD dwLength ) { DWORD dwDone; if (!WriteFile(hFile, pBuffer, dwLength, &dwDone, NULL)) { return FALSE; } if (dwDone != dwLength) { SetLastError(ERROR_WRITE_FAULT); return FALSE; } return TRUE; } WCHAR * DmppGetHotFixString( ) { WCHAR *pszBigBuffer = NULL; HKEY hkey = 0; // // Get the hot fixes. Concat hotfixes into a list that looks like: // "Qxxxx, Qxxxx, Qxxxx, Qxxxx" // RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix", 0, KEY_READ, &hkey); if (hkey) { DWORD dwMaxKeyNameLen = 0; DWORD dwNumSubKeys = 0; WCHAR *pszNameBuffer = NULL; if (ERROR_SUCCESS != RegQueryInfoKeyW(hkey, // handle of key to query NULL, // address of buffer for class string NULL, // address of size of class string buffer 0, // reserved &dwNumSubKeys, // address of buffer for number of subkeys &dwMaxKeyNameLen, // address of buffer for longest subkey name length NULL, // address of buffer for longest class string length NULL, // address of buffer for number of value entries NULL, // address of buffer for longest value name length NULL, // address of buffer for longest value data length NULL, // address of buffer for security descriptor length NULL)) { // address of buffer for last write time); pszNameBuffer = (WCHAR *) calloc(dwMaxKeyNameLen, sizeof(WCHAR)); pszBigBuffer = (WCHAR *) calloc(dwMaxKeyNameLen * dwNumSubKeys // Factor in the space required for each ", " between the hotfixes + (dwNumSubKeys -1) * 2, sizeof(WCHAR)); if (!pszNameBuffer || !pszBigBuffer) { if (pszBigBuffer) { free(pszBigBuffer); pszBigBuffer = NULL; } } else { DWORD dw; // So far so good, get each entry for (dw=0; dwImageNameLength )) { goto bad_file; } DumpHeader.ModuleCount += 1; } } while( rval ); // // write the virtual memory // DumpHeader.DataOffset = DmppGetFilePointer( hFile ); if (DumpHeader.DataOffset == DMPP_INVALID_OFFSET) { goto bad_file; } do { __try { rval = DmpCallback( DMP_MEMORY_DATA, &DumpData, &DumpDataLength, lpv ); } __except (EXCEPTION_EXECUTE_HANDLER) { rval = FALSE; } if (rval) { if (!DmppWriteAll( hFile, DumpData, DumpDataLength )) { goto bad_file; } } } while( rval ); // // VersionInfoOffset will be an offset into the dump file that will contain // misc information about drwatson. The format of the information // will be a series of NULL terminated strings with two zero // terminating the multistring. The string will be UNICODE. // // FORMAT: // This data refers to the specific data about Dr. Watson // DRW: OS version: XX.XX // OS version of headers // DRW: build: XXXX // Build number of Dr. Watson binary // DRW: QFE: X // QFE number of the Dr. Watson binary // Refers to info describing the OS on which the app crashed, // including Service pack, hotfixes, etc... // CRASH: OS SP: X // Service Pack number of the OS where the app AV'd (we // already store the build number, but not the SP) // DumpHeader.VersionInfoOffset = DmppGetFilePointer( hFile ); if (DumpHeader.VersionInfoOffset == DMPP_INVALID_OFFSET) { goto bad_file; } OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx( &OsVersion )) { goto bad_file; } { WCHAR szBuf[1024] = {0}; WCHAR * psz = szBuf; ULONG Left = DIMA(szBuf) - 1; ULONG Len; WCHAR * pszHotfixes; #define ADVANCE() \ Len = wcslen(psz) + 1; \ if (Len <= Left) { psz += Len; Left -= Len; } else { Left = 0; } CatStringW(psz, L"DRW: OS version", Left); ADVANCE(); // Let the printf function convert it from ANSI to unicode PrintStringW(psz, Left, L"%S", VER_PRODUCTVERSION_STRING); ADVANCE(); CatStringW(psz, L"DRW: build", Left); ADVANCE(); PrintStringW(psz, Left, L"%d", (int) VER_PRODUCTBUILD); ADVANCE(); CatStringW(psz, L"DRW: QFE", Left); ADVANCE(); PrintStringW(psz, Left, L"%d", (int) VER_PRODUCTBUILD_QFE); ADVANCE(); CatStringW(psz, L"CRASH: OS SP", Left); ADVANCE(); if (OsVersion.szCSDVersion[0]) { // Let the printf function convert it from ANSI to unicode PrintStringW(psz, Left, L"%S", OsVersion.szCSDVersion); } else { CatStringW(psz, L"none", Left); } ADVANCE(); CatStringW(psz, L"CRASH: Hotfixes", Left); ADVANCE(); pszHotfixes = DmppGetHotFixString (); if (pszHotfixes) { CatStringW(psz, pszHotfixes, Left); free(pszHotfixes); } else { CatStringW(psz, L"none", Left); } ADVANCE(); // Include last terminating zero *psz++ = 0; // Calc length of data. This should always fit in a ULONG. DumpDataLength = (ULONG)((PBYTE) psz - (PBYTE) szBuf); if (!DmppWriteAll( hFile, szBuf, DumpDataLength )) { goto bad_file; } } // // re-write the dump header with some valid data // DumpHeader.Signature = USERMODE_CRASHDUMP_SIGNATURE; DumpHeader.MajorVersion = OsVersion.dwMajorVersion; DumpHeader.MinorVersion = (OsVersion.dwMinorVersion & 0xffff) | (OsVersion.dwBuildNumber << 16); #if defined(_M_IX86) DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_I386; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP32; #elif defined(_M_IA64) DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_IA64; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP64; #elif defined(_M_AXP64) DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_AXP64; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP64; #elif defined(_M_ALPHA) DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_ALPHA; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP32; #elif defined(_M_AMD64) DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_AMD64; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP64; #else #error( "unknown target machine" ); #endif if (SetFilePointer( hFile, 0, 0, FILE_BEGIN ) == INVALID_SET_FILE_POINTER) { goto bad_file; } if (!DmppWriteAll( hFile, &DumpHeader, sizeof(DumpHeader) )) { goto bad_file; } // // close the file // if (CrashDumpName) { CloseHandle( hFile ); } return TRUE; bad_file: if (CrashDumpName) { CloseHandle( hFile ); DeleteFileW( CrashDumpName ); } return FALSE; }