Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

803 lines
24 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
savedump.c
Abstract:
This module contains the code to recover a dump from the system paging
file.
Author:
Darryl E. Havens (darrylh) 5-jan-1994
Environment:
Kernel mode
Revision History:
--*/
#ifndef UNICODE
#define UNICODE
#endif
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <assert.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <memory.h>
#include <lmcons.h>
#include <lmalert.h>
#include <ntiodump.h>
#include <sdevents.h>
#include "..\..\..\inc\alertmsg.h"
//
// Define physical memory blocks.
//
typedef struct _PHYSICAL_MEMORY_RUN {
ULONG BasePage;
ULONG PageCount;
} PHYSICAL_MEMORY_RUN, *PPHYSICAL_MEMORY_RUN;
typedef struct _PHYSICAL_MEMORY_DESCRIPTOR {
ULONG NumberOfRuns;
ULONG NumberOfPages;
PHYSICAL_MEMORY_RUN Run[1];
} PHYSICAL_MEMORY_DESCRIPTOR, *PPHYSICAL_MEMORY_DESCRIPTOR;
//
// Administrative alert information buffer.
//
WCHAR VariableInfo[256];
//
// Read/write copy of dump header.
//
CHAR HeaderBuffer[64 * 1024];
#if DBG
//
// Debug values:
//
// 1 - Basic debug information
// 2 - Registry & informational control information
// 4 - Flow control debug information
//
ULONG SdDebug = 4;
#endif // DBG
VOID __cdecl
main(
int argc,
char *argv[]
)
/*++
Routine Description:
This is the main driving routine for the dump recovery process.
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status;
SYSTEM_CRASH_DUMP_INFORMATION crashDumpInfo;
HANDLE sectionHandle;
HANDLE fileHandle;
SYSTEM_BASIC_INFORMATION basicInfo;
ULONG pageSize;
PDUMP_HEADER viewBase;
ULONG viewSize;
PULONG block;
PPHYSICAL_MEMORY_DESCRIPTOR memory;
BOOLEAN summary;
ULONG winStatus;
HKEY controlKey;
ULONG type;
ULONG crashDumpEnabled;
ULONG logEvent;
ULONG sendAlert;
ULONG returnedLength;
CHAR buffer1[256];
CHAR buffer2[256];
ANSI_STRING ansiString1;
ANSI_STRING ansiString2;
UNICODE_STRING unicodeString1;
UNICODE_STRING unicodeString2;
WCHAR fileName[MAX_PATH];
WCHAR expandedName[MAX_PATH];
ULONG i;
//
// Begin by determining whether or not there is a valid dump in the
// system's paging file. If not, then exit immediately.
//
status = NtQuerySystemInformation( SystemCrashDumpInformation,
&crashDumpInfo,
sizeof( SYSTEM_CRASH_DUMP_INFORMATION ),
(PULONG) NULL );
if (!NT_SUCCESS( status ) || !crashDumpInfo.CrashDumpSection) {
#if DBG
if (!NT_SUCCESS( status ) && SdDebug) {
DbgPrint( "SAVEDUMP: Error getting dump handle; error = %x\n", status );
}
#endif // DBG
return;
}
sectionHandle = crashDumpInfo.CrashDumpSection;
//
// Get the page size of the machine to determine the size of the header
// stored in the paging file.
//
status = NtQuerySystemInformation( SystemBasicInformation,
&basicInfo,
sizeof( SYSTEM_BASIC_INFORMATION ),
(PULONG) NULL );
pageSize = basicInfo.PageSize;
//
// Map the header of the section to determine whether what was written
// was a full dump or a summary dump. The dump is a summary dump if the
// size of the dump is a single page.
//
viewBase = NULL;
viewSize = pageSize;
status = NtMapViewOfSection( sectionHandle,
NtCurrentProcess(),
(PVOID *) &viewBase,
0,
0,
(PLARGE_INTEGER) NULL,
&viewSize,
ViewShare,
0,
PAGE_READONLY );
if (!NT_SUCCESS( status )) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to map view of paging file section; error = %x\n", status );
}
#endif // DBG
return;
}
RtlCopyMemory( HeaderBuffer, viewBase, pageSize );
block = (PULONG) viewBase;
memory = (PPHYSICAL_MEMORY_DESCRIPTOR) &block[DH_PHYSICAL_MEMORY_BLOCK];
summary = memory->NumberOfPages == 1;
//
// Open the base registry node for crash control information and get the
// actions for what needs to occur next.
//
winStatus = RegOpenKey( HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\CrashControl",
&controlKey );
if (winStatus != ERROR_SUCCESS) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to open CrashControl key; error = %d\n", winStatus );
}
#endif // DBG
return;
}
//
// Get the values of the following from the registry. Each are longwords
// so the returned length should always be 4 bytes.
//
// CrashDumpEnabled
// LogEvent
// SendAlert
//
// Note that it is possible for the values to not be there, if they were
// never initialized. In this case, they are taken to be zeroes.
//
crashDumpEnabled = 0;
logEvent = 0;
sendAlert = 0;
returnedLength = 4;
winStatus = RegQueryValueEx( controlKey,
L"CrashDumpEnabled",
(LPDWORD) NULL,
&type,
(LPBYTE) &crashDumpEnabled,
&returnedLength );
winStatus = RegQueryValueEx( controlKey,
L"LogEvent",
(LPDWORD) NULL,
&type,
(LPBYTE) &logEvent,
&returnedLength );
winStatus = RegQueryValueEx( controlKey,
L"SendAlert",
(LPDWORD) NULL,
&type,
(LPBYTE) &sendAlert,
&returnedLength );
#if DBG
if (SdDebug & 2) {
DbgPrint( "SAVEDUMP: CDE = %d, LE = %d, SA = %d\n",
crashDumpEnabled, logEvent, sendAlert );
DbgPrint( "SAVEDUMP: Number of pages = %d\n", memory->NumberOfPages );
}
#endif // DBG
//
// If crash dumping is enabled, and a dump was actually written to the file,
// copy the file to the specified location.
//
fileHandle = INVALID_HANDLE_VALUE;
if (crashDumpEnabled && !summary) {
LARGE_INTEGER sectionOffset;
ULONG overwrite;
ULONG numberOfPages;
IO_STATUS_BLOCK ioStatus;
FILE_ALLOCATION_INFORMATION allocInfo;
PCHAR bytes;
PCHAR sectionBase;
BOOLEAN partialMap;
//
// A crash dump is available and is to be copied to some other location.
// Determine whether or not the output file is to be overwritten, if
// it already exists.
//
overwrite = 0;
winStatus = RegQueryValueEx( controlKey,
L"Overwrite",
(LPDWORD) NULL,
&type,
(LPBYTE) &overwrite,
&returnedLength );
//
// Get the name of the target file. Note that because dumps are
// enabled, the filename must be present. If it is not, it is an
// error that cannot be properly dealt with, so skip attempting to
// save a dump.
//
returnedLength = sizeof( fileName );
winStatus = RegQueryValueEx( controlKey,
L"DumpFile",
(LPDWORD) NULL,
&type,
(LPBYTE) &fileName,
&returnedLength );
if (winStatus != ERROR_SUCCESS) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to query DumpFile file name; error = %d\n", winStatus );
}
#endif // DBG
crashDumpEnabled = FALSE;
goto fileDone;
}
//
// Expand the pathname in case there are buried %'s.
//
ExpandEnvironmentStrings( &fileName[0], &expandedName[0], MAX_PATH );
#if DBG
if (SdDebug & 2) {
DbgPrint( "SAVEDUMP: Expanded pathname is: %ws\n", expandedName );
}
#endif // DBG
//
// Set the priority class of this application down to the Lowest
// priority class to ensure that copying the file does not overload
// everything else that is going on during system initialization.
//
SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_LOWEST );
//
// Create the output file, overwriting it if necessary.
//
i = 0;
do {
fileHandle = CreateFile( expandedName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
(LPSECURITY_ATTRIBUTES) NULL,
overwrite ? CREATE_ALWAYS : CREATE_NEW,
0,
(HANDLE) NULL );
//
// If the create failed, check to see whether or not it was because
// the path was not found. If it is, then it is possible that the
// reason is because the network has not yet started, and the output
// file is being written to a network server. Try this every 15
// seconds for 5 minutes. If it still doesn't work, forget it.
//
if (fileHandle == INVALID_HANDLE_VALUE) {
winStatus = GetLastError();
if (winStatus == ERROR_PATH_NOT_FOUND) {
if (i++ > 20) {
break;
}
Sleep( 15000 );
}
}
} while (winStatus == ERROR_PATH_NOT_FOUND);
if (fileHandle == INVALID_HANDLE_VALUE) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to %s output file; error = %d\n",
overwrite ? "overwrite" : "create",
GetLastError() );
}
#endif // DBG
crashDumpEnabled = FALSE;
goto fileDone;
}
//
// The output file has been created. Attempt to pre-allocate the file
// based on the file size of the dump. If this fails, then the output
// file cannot be fully created, so bail out now.
//
numberOfPages = memory->NumberOfPages + 1;
allocInfo.AllocationSize.QuadPart = numberOfPages * pageSize;
status = NtSetInformationFile( fileHandle,
&ioStatus,
&allocInfo,
sizeof( allocInfo ),
FileAllocationInformation );
if (!NT_SUCCESS( status )) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to pre-allocate copy of dump file; error = %x\n", status );
}
#endif // DBG
crashDumpEnabled = FALSE;
goto fileDone;
}
//
// The output file was successfully pre-allocated, so it is possible to
// actually copy the paging file to it. Simply loop, writing chunks of
// the paging file to the output file, maximizing w/64kb at a time,
// until the entire file has been copied.
//
//
// Attempt to map the entire file. If this fails, back off and map
// a small chunk of the file that can be written to the output file.
// Note that if the allocation size of the file is greater than 4GB,
// then this will not work anyway, so don't even try to map the file.
//
partialMap = FALSE;
sectionOffset.QuadPart = 0;
if (!allocInfo.AllocationSize.HighPart) {
viewSize = pageSize * numberOfPages;
bytes = NULL;
status = NtMapViewOfSection( sectionHandle,
NtCurrentProcess(),
(PVOID *) &bytes,
0,
0,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY );
if (viewSize < pageSize * numberOfPages) {
partialMap = TRUE;
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if (!NT_SUCCESS( status )) {
partialMap = TRUE;
//
// Map only 4MB of the file. If this fails, then something is
// seriously wrong, so bail out.
//
viewSize = 65536 * 64;
bytes = NULL;
status = NtMapViewOfSection( sectionHandle,
NtCurrentProcess(),
(PVOID *) &bytes,
0,
0,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY );
if (!NT_SUCCESS( status )) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to map 4MB of paging file; error = %x\n", status );
}
#endif // DBG
crashDumpEnabled = FALSE;
goto fileDone;
}
}
//
// Begin writing to the output file wherever the paging file was mapped
// in the address space. Continue until there are no more bytes left
// to write.
//
sectionBase = bytes;
while (allocInfo.AllocationSize.QuadPart) {
//
// Write as much of the view as possible, up to 64kb.
//
if (!WriteFile( fileHandle,
bytes,
viewSize > 65536 ? 65536 : viewSize,
&returnedLength,
(LPOVERLAPPED) NULL )) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Error writing file; error = %d\n", GetLastError() );
}
#endif // DBG
crashDumpEnabled = FALSE;
goto fileDone;
}
//
// Account for the number of bytes just written.
//
if (viewSize > 65536) {
viewSize -= 65536;
bytes += 65536;
allocInfo.AllocationSize.QuadPart -= 65536;
} else {
allocInfo.AllocationSize.QuadPart -= viewSize;
bytes += viewSize;
viewSize = 0;
}
//
// If a partial view was mapped to the file and all of the bytes in
// that view have now been written, map the next portion of the file
// if there is any more to write.
//
if (!viewSize && partialMap && allocInfo.AllocationSize.QuadPart) {
//
// Unmap the current view to the section and map the next piece.
//
NtUnmapViewOfSection( NtCurrentProcess(),
sectionBase );
viewSize = 65536 * 64;
sectionOffset.QuadPart += viewSize;
bytes = NULL;
NtMapViewOfSection( sectionHandle,
NtCurrentProcess(),
(PVOID *) &bytes,
0,
0,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY );
sectionBase = bytes;
}
}
//
// The entire file has now been copied. Overwrite the header of the
// file using the in-memory copy, after making it a valid dump.
//
viewBase = (PDUMP_HEADER) &HeaderBuffer[0];
viewBase->ValidDump = 'PMUD';
sectionOffset.LowPart = 0;
SetFilePointer( fileHandle,
0,
&sectionOffset.LowPart,
FILE_BEGIN );
if (!WriteFile( fileHandle,
viewBase,
pageSize,
&returnedLength,
(LPOVERLAPPED) NULL )) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Error writing file; error = %d\n", GetLastError() );
}
#endif // DBG
crashDumpEnabled = FALSE;
goto fileDone;
}
}
fileDone:
if (fileHandle != INVALID_HANDLE_VALUE) {
CloseHandle( fileHandle );
}
//
// If either an alert is to be sent or an event is to be logged, format
// the bugcheck parameter information.
//
if (sendAlert || logEvent) {
sprintf( buffer1,
"0x%08x (0x%08x, 0x%08x, 0x%08x, 0x%08x)",
viewBase->BugCheckCode,
viewBase->BugCheckParameter1,
viewBase->BugCheckParameter2,
viewBase->BugCheckParameter3,
viewBase->BugCheckParameter4 );
RtlInitAnsiString( &ansiString1, buffer1 );
RtlAnsiStringToUnicodeString( &unicodeString1, &ansiString1, TRUE );
sprintf( buffer2,
"Microsoft Windows NT [v%ld.%ld]",
viewBase->MajorVersion,
viewBase->MinorVersion );
RtlInitAnsiString( &ansiString2, buffer2 );
RtlAnsiStringToUnicodeString( &unicodeString2, &ansiString2, TRUE );
}
//
// If an event is to be logged, log it now.
//
if (logEvent) {
HANDLE logHandle;
LPWSTR stringArray[3];
BOOLEAN savedDump;
//
// Register w/the event log service so that events can be logged.
//
winStatus = 0;
i = 0;
do {
logHandle = RegisterEventSource( (LPWSTR) NULL,
L"Save Dump" );
if (!logHandle) {
winStatus = GetLastError();
if (winStatus == RPC_S_SERVER_UNAVAILABLE) {
if (i++ > 20) {
break;
}
#if DBG
if (SdDebug & 4 && ((i & 3) == 0)) {
DbgPrint( "SAVEDUMP: Waiting for event logger...\n" );
}
#endif // DBG
Sleep( 15000 );
}
} else {
winStatus = ERROR_SUCCESS;
}
} while (winStatus == RPC_S_SERVER_UNAVAILABLE);
if (!logHandle) {
#if DBG
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to register event source; error = %d\n", GetLastError() );
}
#endif // DBG
} else {
//
// Set up the parameters based on whether a full crash or summary
// was taken.
//
stringArray[0] = unicodeString1.Buffer;
stringArray[1] = unicodeString2.Buffer;
savedDump = (crashDumpEnabled && !summary);
if (savedDump) {
stringArray[2] = expandedName;
}
//
// Report the appropriate event.
//
winStatus = 0;
winStatus = ReportEvent( logHandle,
EVENTLOG_INFORMATION_TYPE,
0,
savedDump ? EVENT_BUGCHECK_SAVED : EVENT_BUGCHECK,
(PSID) NULL,
(WORD) (savedDump ? 3 : 2),
0,
stringArray,
(LPVOID) NULL );
#if DBG
if (!winStatus) {
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to report event; error = %d\n", GetLastError() );
}
}
#endif // DBG
}
}
//
// If an alert is to be raised, raise it now.
//
if (sendAlert) {
PADMIN_OTHER_INFO adminInfo;
DWORD adminInfoSize;
//
// Set up the administrator information variables for processing the
// buffer.
//
adminInfo = (PADMIN_OTHER_INFO) VariableInfo;
adminInfoSize = sizeof( ADMIN_OTHER_INFO );
//
// Format the bugcheck information into the appropriate message format.
//
RtlCopyMemory( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize),
unicodeString1.Buffer,
unicodeString1.Length );
adminInfoSize += unicodeString1.Length + sizeof( WCHAR );
RtlCopyMemory( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize),
unicodeString2.Buffer,
unicodeString2.Length );
adminInfoSize += unicodeString2.Length + sizeof( WCHAR );
//
// Set up the administrator alert information according to the type of
// dump that was taken.
//
if (crashDumpEnabled && !summary) {
adminInfo->alrtad_errcode = ALERT_BugCheckSaved;
adminInfo->alrtad_numstrings = 3;
wcscpy( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize), expandedName );
adminInfoSize += ((wcslen( expandedName ) + 1) * sizeof( WCHAR ));
} else {
adminInfo->alrtad_errcode = ALERT_BugCheck;
adminInfo->alrtad_numstrings = 2;
}
//
// Get the name of the computer and insert it into the buffer.
//
returnedLength = sizeof( VariableInfo ) - adminInfoSize;
winStatus = GetComputerName( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize),
&returnedLength );
returnedLength = ((returnedLength + 1) * sizeof( WCHAR ));
adminInfoSize += returnedLength;
//
// Raise the alert.
//
i = 0;
do {
winStatus = NetAlertRaiseEx( ALERT_ADMIN_EVENT,
adminInfo,
adminInfoSize,
L"SAVEDUMP" );
if (winStatus) {
if (winStatus == ERROR_FILE_NOT_FOUND) {
if (i++ > 20) {
break;
}
#if DBG
if (SdDebug & 4 && ((i & 3) == 0)) {
DbgPrint( "SAVEDUMP: Waiting for alerter...\n" );
}
#endif // DBG
Sleep( 15000 );
}
}
} while (winStatus == ERROR_FILE_NOT_FOUND);
#if DBG
if (winStatus) {
if (SdDebug) {
DbgPrint( "SAVEDUMP: Unable to raise alert; error = %d\n", winStatus );
}
}
#endif // DBG
}
}