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.
2908 lines
95 KiB
2908 lines
95 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
evlog.cpp
|
|
|
|
Abstract:
|
|
|
|
!evlog using the debug engine evlog query interface
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
//
|
|
// TODO: Feature to see exact formatted desc string for specific event id
|
|
// TODO: Feature to see correct loaded category name for specific event id
|
|
// TODO: Feature to list cat and desc strings for given msg dll(?)
|
|
//
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <cmnutil.hpp>
|
|
|
|
#include "messages.h"
|
|
|
|
//
|
|
// Global display constants
|
|
//
|
|
|
|
const CHAR *g_pcszEventType[] = {
|
|
"None", // 0
|
|
"Error", // 1
|
|
"Warning", // 2
|
|
"",
|
|
"Information", // 4
|
|
"",
|
|
"",
|
|
"",
|
|
"Success Audit", // 8
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"Failure Audit", // 16
|
|
};
|
|
|
|
const CHAR *g_pcszAppEventCategory[] = {
|
|
"None", // 0
|
|
"Devices", // 1
|
|
"Disk", // 2
|
|
"Printers", // 3
|
|
"Services", // 4
|
|
"Shell", // 5
|
|
"System Event", // 6
|
|
"Network", // 7
|
|
};
|
|
|
|
//
|
|
// TODO: Really we should load the CategoryMessageFile from the registry
|
|
// but that requires lots of calls to RegOpenKeyEx, RegQueryValueEx,
|
|
// LoadLibrary and FormatMessage. So we just create a known static
|
|
// list that works for most cases.
|
|
//
|
|
|
|
const CHAR *g_pcszSecEventCategory[] = {
|
|
"None", // 0
|
|
"System Event", // 1
|
|
"Logon/Logoff", // 2
|
|
"Object Access", // 3
|
|
"Privilege Use", // 4
|
|
"Detailed Tracking", // 5
|
|
"Policy Change", // 6
|
|
"Account Management", // 7
|
|
"Directory Service Access", // 8
|
|
"Account Logon", // 9
|
|
};
|
|
|
|
//
|
|
// Display text for read direction
|
|
//
|
|
|
|
const CHAR g_cszBackwardsRead[] = "Backwards";
|
|
const CHAR g_cszForwardsRead[] = "Forwards";
|
|
const CHAR g_cszUnknownRead[] = "<unknown>";
|
|
|
|
//
|
|
// Global Variables and Constants:
|
|
//
|
|
// g_cdwDefaultMaxRecords:
|
|
// arbitrary to prevent too much output, ctrl+c will interrupt display
|
|
//
|
|
// g_cdwDefaultReadFlags:
|
|
// starts from beginning (FORWARDS) or end (BACKWARDS) of event log
|
|
//
|
|
// g_cwMaxDataDisplayWidth:
|
|
// allows for 32 columns of 8 byte chunks
|
|
//
|
|
// g_cwDefaultDataDisplayWidth
|
|
// same as event log display. Can never be < 1 or > g_cdwMaxDataDisplayWidth
|
|
//
|
|
|
|
const DWORD BACKWARDS_READ = EVENTLOG_BACKWARDS_READ;
|
|
const DWORD FORWARDS_READ = EVENTLOG_FORWARDS_READ;
|
|
const DWORD g_cdwDefaultMaxRecords = 20;
|
|
const DWORD g_cdwDefaultRecordOffset = 0;
|
|
const DWORD g_cdwDefaultReadFlags = BACKWARDS_READ;
|
|
const WORD g_cwMaxDataDisplayWidth = 256;
|
|
const BYTE g_cwDefaultDataDisplayWidth = 8;
|
|
|
|
//
|
|
// Global static vars
|
|
//
|
|
// These are used to persist settings for !evlog option command
|
|
//
|
|
|
|
static DWORD g_dwMaxRecords = g_cdwDefaultMaxRecords;
|
|
static DWORD g_dwRecordOffsetAppEvt = g_cdwDefaultRecordOffset;
|
|
static DWORD g_dwRecordOffsetSecEvt = g_cdwDefaultRecordOffset;
|
|
static DWORD g_dwRecordOffsetSysEvt = g_cdwDefaultRecordOffset;
|
|
static DWORD g_dwReadFlags = g_cdwDefaultReadFlags;
|
|
static WORD g_wDataDisplayWidth = g_cwDefaultDataDisplayWidth;
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
#define SKIP_WSPACE(s) while (*s && (*s == ' ' || *s == '\t')) {++s;}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Generic support/utility functions
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
GetEvLogNewestRecord ( const CHAR *szEventLog , OUT DWORD *pdwNewestRecord)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to retrieve the most recent event record number
|
|
logged to the specified event log.
|
|
|
|
Arguments:
|
|
|
|
szEventLog - Supplies name of event log (Application, System,
|
|
Security)
|
|
pdwNewestRecord - Supplies buffer for record number
|
|
|
|
Return Value:
|
|
|
|
E_POINTER if either argument is NULL
|
|
E_UNEXPECTED if Status is (mistakenly) not set during code execution
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEventLog = NULL;
|
|
DWORD dwRecords = 0;
|
|
DWORD dwOldestRecord = 0xFFFFFFFF;
|
|
HRESULT Status = E_UNEXPECTED;
|
|
|
|
if ((NULL == szEventLog) || (NULL == pdwNewestRecord))
|
|
{
|
|
Status = E_POINTER;
|
|
ExtErr("Internal error: null event log string or null oldest record "
|
|
"pointer\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// Open the event log.
|
|
hEventLog = OpenEventLog(
|
|
NULL, // uses local computer
|
|
szEventLog); // source name
|
|
if (NULL == hEventLog)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the number of records in the event log.
|
|
if (!GetNumberOfEventLogRecords(
|
|
hEventLog, // handle to event log
|
|
&dwRecords)) // buffer for number of records
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to count '%s' event log records, 0x%08X\n",
|
|
szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
if (!GetOldestEventLogRecord(
|
|
hEventLog, // handle to event log
|
|
&dwOldestRecord)) // buffer for number of records
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to get oldest '%s' event log record, 0x%08X\n",
|
|
szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If there are zero events we should have failed above
|
|
// when trying to get the oldest event log record because
|
|
// it does not exist.
|
|
//
|
|
// If there is at least one, the math should work.
|
|
//
|
|
// The logging should result in sequential numbers for
|
|
// the events, however, the first event will not always
|
|
// start at #1
|
|
//
|
|
|
|
*pdwNewestRecord = dwOldestRecord + dwRecords - 1;
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (hEventLog)
|
|
{
|
|
CloseEventLog(hEventLog);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
void
|
|
PrintEvLogTimeGenerated( EVENTLOGRECORD *pevlr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to display two lines with the local date and time
|
|
info from an EVENTLOGRECORD structure.
|
|
|
|
Arguments:
|
|
|
|
pevlr - Supplies the pointer to any EVENTLOGRECORD structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
FILETIME FileTime, LocalFileTime;
|
|
SYSTEMTIME SysTime;
|
|
__int64 lgTemp;
|
|
__int64 SecsTo1970 = 116444736000000000;
|
|
|
|
if (NULL == pevlr)
|
|
goto Exit;
|
|
|
|
lgTemp = Int32x32To64(pevlr->TimeGenerated,10000000) + SecsTo1970;
|
|
|
|
FileTime.dwLowDateTime = (DWORD) lgTemp;
|
|
FileTime.dwHighDateTime = (DWORD)(lgTemp >> 32);
|
|
|
|
// TODO: Could use GetTimeFormat to be more consistent w/Event Log
|
|
// cch = GetTimeFormat(LOCALE_USER_DEFAULT,
|
|
// 0,
|
|
// &stGenerated,
|
|
// NULL,
|
|
// wszBuf,
|
|
// cchBuf);
|
|
FileTimeToLocalFileTime(&FileTime, &LocalFileTime);
|
|
FileTimeToSystemTime(&LocalFileTime, &SysTime);
|
|
|
|
ExtOut("Date:\t\t%02d/%02d/%04d\n",
|
|
SysTime.wMonth,
|
|
SysTime.wDay,
|
|
SysTime.wYear);
|
|
ExtOut("Time:\t\t%02d:%02d:%02d\n",
|
|
SysTime.wHour,
|
|
SysTime.wMinute,
|
|
SysTime.wSecond);
|
|
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
PrintEvLogData( EVENTLOGRECORD *pevlr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to display an event record's data section. If there
|
|
is no data to display, nothing is displayed, not even the "Data:" header.
|
|
|
|
Example:
|
|
========
|
|
Data: (40432 bytes)
|
|
0000: 0d 00 0a 00 0d 00 0a 00 ........
|
|
0008: 41 00 70 00 70 00 6c 00 A.p.p.l.
|
|
|
|
Arguments:
|
|
|
|
pevlr - Supplies the pointer to any EVENTLOGRECORD structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PBYTE pbData = NULL;
|
|
DWORD dwDataLen = pevlr->DataLength;
|
|
DWORD dwCurPos = 0;
|
|
// 0000: 0d 00 0a 00 0d 00 0a 00 ........
|
|
// 4 + 4 bytes for leading offset 0000: (+4 more in case bounds exceeded)
|
|
// 2 bytes for ": " separator
|
|
// 3 * g_cdwMaxDataDisplayWidth bytes for hex display
|
|
// 2 bytes for " " separator
|
|
// g_cdwMaxDataDisplayWidth bytes for trailing ASCII display
|
|
// 1 byte for trailing newline
|
|
// 1 byte for terminating nul '\0'
|
|
// = 1042 bytes required, round up to 1280 to be safe
|
|
const cDataOutputDisplayWidth =
|
|
4+4+4+2+3*g_cwMaxDataDisplayWidth+2+g_cwMaxDataDisplayWidth+1+1;
|
|
CHAR szDataDisplay[cDataOutputDisplayWidth];
|
|
CHAR szTempBuffer[MAX_PATH+1];
|
|
|
|
if (NULL == pevlr)
|
|
goto Exit;
|
|
|
|
ZeroMemory(szDataDisplay, sizeof(szDataDisplay));
|
|
|
|
// Only display Data section if data is present
|
|
if (0 != dwDataLen)
|
|
{
|
|
ExtOut("Data: (%u bytes [=0x%04X])\n", dwDataLen, dwDataLen);
|
|
|
|
if (dwDataLen >= g_wDataDisplayWidth)
|
|
{
|
|
do
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
pbData = (PBYTE)pevlr + pevlr->DataOffset + dwCurPos;
|
|
|
|
//ExtOut("%04x: "
|
|
// "%02x %02x %02x %02x %02x %02x %02x %02x ",
|
|
// dwCurPos,
|
|
// pbData[0], pbData[1], pbData[2], pbData[3],
|
|
// pbData[4], pbData[5], pbData[6], pbData[7]);
|
|
|
|
// Print offset for this line of data
|
|
PrintString(szDataDisplay,
|
|
sizeof(szDataDisplay),
|
|
"%04x: ",
|
|
dwCurPos);
|
|
|
|
// Fill in hex values for next g_wDataDisplayWidth bytes
|
|
for (i = 0; i < g_wDataDisplayWidth; i++)
|
|
{
|
|
PrintString(szTempBuffer,
|
|
sizeof(szTempBuffer),
|
|
"%02x ",
|
|
pbData[i]);
|
|
CatString(szDataDisplay,
|
|
szTempBuffer,
|
|
sizeof(szDataDisplay));
|
|
}
|
|
|
|
// Pad with two extra spaces
|
|
CatString(szDataDisplay, " ", sizeof(szDataDisplay));
|
|
|
|
for (i = 0; i < g_wDataDisplayWidth; i++)
|
|
{
|
|
if (isprint(pbData[i]))
|
|
{
|
|
PrintString(szTempBuffer,
|
|
sizeof(szTempBuffer),
|
|
"%c",
|
|
pbData[i]);
|
|
}
|
|
else
|
|
{
|
|
PrintString(szTempBuffer, sizeof(szTempBuffer), ".");
|
|
}
|
|
|
|
CatString(szDataDisplay,
|
|
szTempBuffer,
|
|
sizeof(szDataDisplay));
|
|
}
|
|
|
|
CatString(szDataDisplay, "\n", sizeof(szDataDisplay));
|
|
|
|
ExtOut(szDataDisplay);
|
|
|
|
if (CheckControlC())
|
|
{
|
|
ExtOut("Terminated w/ctrl-C...\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// Display data 8 bytes at a time like event log
|
|
// unless overridden by !evlog option setting
|
|
dwCurPos += sizeof(BYTE) * g_wDataDisplayWidth;
|
|
|
|
} while (dwCurPos < (dwDataLen - g_wDataDisplayWidth));
|
|
}
|
|
|
|
// Sometimes there will be fewer than 8 bytes on last line
|
|
if (dwCurPos < dwDataLen)
|
|
{
|
|
pbData = (PBYTE)pevlr + pevlr->DataOffset + dwCurPos;
|
|
|
|
ExtOut("%04x: ", dwCurPos);
|
|
|
|
for (unsigned int i = 0; i < (dwDataLen - dwCurPos); i++)
|
|
{
|
|
ExtOut("%02x ", pbData[i]);
|
|
}
|
|
|
|
for (i = 0; i < g_wDataDisplayWidth - (dwDataLen - dwCurPos); i++)
|
|
{
|
|
ExtOut(" ");
|
|
}
|
|
ExtOut(" ");
|
|
|
|
for (i = 0; i < (dwDataLen - dwCurPos); i++)
|
|
{
|
|
if (isprint(pbData[i]))
|
|
ExtOut("%c", pbData[i]);
|
|
else
|
|
ExtOut(".");
|
|
}
|
|
ExtOut("\n");
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
PrintEvLogDescription( EVENTLOGRECORD *pevlr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to display an event record's description insertion
|
|
strings (%1, %2, etc).
|
|
|
|
Currently it does not look up the message string from the event message
|
|
file.
|
|
|
|
Arguments:
|
|
|
|
pevlr - Supplies the pointer to any EVENTLOGRECORD structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD dwOffset = 0;
|
|
CHAR *pString = NULL;
|
|
|
|
if (NULL == pevlr)
|
|
goto Exit;
|
|
|
|
ExtOut("Description: (%u strings)\n", pevlr->NumStrings);
|
|
|
|
for (int i = 0; i < pevlr->NumStrings; i++)
|
|
{
|
|
// TODO: Should break this up into chunks in case it is really long
|
|
// and add Ctrl+C handling
|
|
pString = (CHAR *)(BYTE *)pevlr + pevlr->StringOffset + dwOffset;
|
|
ExtOut("%s\n", pString);
|
|
dwOffset += strlen(pString) + 1;
|
|
}
|
|
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
PrintEvLogEvent( const CHAR *szEventLog, EVENTLOGRECORD *pevlr )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to display an event record. The format used for
|
|
display attempts to duplicate the format you see when you use the "copy
|
|
to clipboard" button while viewing an event in eventvwr.
|
|
|
|
Example:
|
|
========
|
|
-------------- 01 --------------
|
|
Record #: 7923
|
|
|
|
Event Type: Error (1)
|
|
Event Source: Userenv
|
|
Event Category: None (0)
|
|
Event ID: 1030 (0xC0000406)
|
|
Date: 01/06/2002
|
|
Time: 18:13:05
|
|
Description: (0 strings)
|
|
|
|
Arguments:
|
|
|
|
pevlr - Supplies the pointer to any EVENTLOGRECORD structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
const CHAR *cszCategory;
|
|
const CHAR cszDefaultCategory[] = "None";
|
|
|
|
if (NULL == pevlr)
|
|
goto Exit;
|
|
|
|
if (!_stricmp(szEventLog, "System"))
|
|
{
|
|
cszCategory = cszDefaultCategory;
|
|
}
|
|
else if (!_stricmp(szEventLog, "Security"))
|
|
{
|
|
if (pevlr->EventCategory <= 9)
|
|
{
|
|
cszCategory = g_pcszSecEventCategory[pevlr->EventCategory];
|
|
}
|
|
else
|
|
{
|
|
cszCategory = "";
|
|
}
|
|
}
|
|
else // Application
|
|
{
|
|
if (pevlr->EventCategory <= 7)
|
|
{
|
|
cszCategory = g_pcszAppEventCategory[pevlr->EventCategory];
|
|
}
|
|
else
|
|
{
|
|
cszCategory = "";
|
|
}
|
|
}
|
|
|
|
// Output format similar to copy to clipboard format in event viewer
|
|
ExtOut("Record #: %u\n\n", pevlr->RecordNumber);
|
|
ExtOut("Event Type:\t%s (%u)\n",
|
|
(pevlr->EventType <= 16)
|
|
? g_pcszEventType[pevlr->EventType]
|
|
: "None",
|
|
pevlr->EventType);
|
|
ExtOut("Event Source:\t%s\n",
|
|
(CHAR *)(BYTE *)pevlr + sizeof(EVENTLOGRECORD));
|
|
ExtOut("Event Category:\t%s (%u)\n",
|
|
cszCategory,
|
|
pevlr->EventCategory);
|
|
if (pevlr->EventID > 0xFFFF)
|
|
ExtOut("Event ID:\t%u (0x%08X)\n",
|
|
0xFFFF & pevlr->EventID,
|
|
pevlr->EventID);
|
|
else
|
|
ExtOut("Event ID:\t%u\n",
|
|
pevlr->EventID);
|
|
PrintEvLogTimeGenerated(pevlr);
|
|
PrintEvLogDescription(pevlr);
|
|
PrintEvLogData(pevlr);
|
|
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PrintEvLogSummary ( const CHAR *szEventLog )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to display summary information about a specific
|
|
event log.
|
|
|
|
Example:
|
|
========
|
|
--------------------------------
|
|
Application Event Log:
|
|
# Records : 7923
|
|
Oldest Record # : 1
|
|
Newest Record # : 7923
|
|
Event Log Full : false
|
|
--------------------------------
|
|
System Event Log:
|
|
# Records : 5046
|
|
Oldest Record # : 1
|
|
Newest Record # : 5046
|
|
Event Log Full : false
|
|
--------------------------------
|
|
Security Event Log:
|
|
# Records : 24256
|
|
Oldest Record # : 15164
|
|
Newest Record # : 39419
|
|
Event Log Full : false
|
|
--------------------------------
|
|
|
|
Arguments:
|
|
|
|
szEventLog - Supplies name of event log (Application, System,
|
|
Security)
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEventLog = NULL;
|
|
DWORD dwRecords = 0;
|
|
DWORD dwOldestRecord = 0xFFFFFFFF;
|
|
HRESULT Status = E_FAIL;
|
|
|
|
if (NULL == szEventLog)
|
|
{
|
|
ExtErr("Internal error: null event log string\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ExtOut("--------------------------------\n");
|
|
// Open the event log.
|
|
hEventLog = OpenEventLog(
|
|
NULL, // uses local computer
|
|
szEventLog); // source name
|
|
|
|
if (NULL == hEventLog)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
|
|
return Status;
|
|
}
|
|
|
|
// Get the number of records in the event log.
|
|
if (!GetNumberOfEventLogRecords(
|
|
hEventLog, // handle to event log
|
|
&dwRecords)) // buffer for number of records
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to count '%s' event log records, 0x%08X\n",
|
|
szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut("%s Event Log:\n # Records : %u\n", szEventLog, dwRecords);
|
|
|
|
if (!GetOldestEventLogRecord(
|
|
hEventLog, // handle to event log
|
|
&dwOldestRecord)) // buffer for number of records
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to get oldest '%s' event log record, 0x%08X\n",
|
|
szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut(" Oldest Record # : %u\n", dwOldestRecord);
|
|
ExtOut(" Newest Record # : %u\n", dwOldestRecord + dwRecords - 1);
|
|
|
|
DWORD dwBytesNeeded = 0;
|
|
DWORD dwBufSize = 0;
|
|
EVENTLOG_FULL_INFORMATION *pevfi;
|
|
|
|
// Only try this once - we could retry if dwBufSize too small
|
|
dwBufSize = sizeof(EVENTLOG_FULL_INFORMATION);
|
|
pevfi = (EVENTLOG_FULL_INFORMATION *)calloc(1, dwBufSize);
|
|
if (!pevfi)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
|
|
ExtErr("Unable to allocate buffer, 0x%08X\n", Status);
|
|
}
|
|
else
|
|
{
|
|
if ((S_OK == (Status = InitDynamicCalls(&g_Advapi32CallsDesc))) &&
|
|
g_Advapi32Calls.GetEventLogInformation)
|
|
{
|
|
if (!g_Advapi32Calls.GetEventLogInformation(
|
|
hEventLog, // handle to event log
|
|
EVENTLOG_FULL_INFO, // information to retrieve
|
|
pevfi, // buffer for read data
|
|
dwBufSize, // size of buffer in bytes
|
|
&dwBytesNeeded)) // number of bytes needed
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to get full status from '%s', 0x%08X\n",
|
|
szEventLog, Status);
|
|
}
|
|
else
|
|
{
|
|
ExtOut(" Event Log Full : %s\n",
|
|
(pevfi->dwFull) ? "true"
|
|
: "false");
|
|
Status = S_OK;
|
|
}
|
|
}
|
|
|
|
free(pevfi);
|
|
}
|
|
|
|
Exit:
|
|
|
|
CloseEventLog(hEventLog);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
void
|
|
PrintEvLogOptionSettings ( void )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to display the option settings used by the !evlog
|
|
extension for various defaults.
|
|
|
|
Currently all cached option settings are used by the read command only.
|
|
|
|
Example:
|
|
========
|
|
Default EvLog Option Settings:
|
|
--------------------------------
|
|
Max Records Returned: 20
|
|
Search Order: Backwards
|
|
Data Display Width: 8
|
|
--------------------------------
|
|
Bounding Record Numbers:
|
|
Application Event Log: 0
|
|
System Event Log: 0
|
|
Security Event Log: 0
|
|
--------------------------------
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
CHAR szSearchOrder[MAX_PATH];
|
|
|
|
if (FORWARDS_READ == g_dwReadFlags)
|
|
{
|
|
CopyString(szSearchOrder, g_cszForwardsRead, sizeof(szSearchOrder));
|
|
}
|
|
else if (BACKWARDS_READ == g_dwReadFlags)
|
|
{
|
|
CopyString(szSearchOrder, g_cszBackwardsRead, sizeof(szSearchOrder));
|
|
}
|
|
else
|
|
{
|
|
PrintString(szSearchOrder,
|
|
sizeof(szSearchOrder),
|
|
"Unknown (%08X)",
|
|
g_dwReadFlags);
|
|
}
|
|
|
|
ExtOut("Default EvLog Option Settings:\n");
|
|
ExtOut("--------------------------------\n");
|
|
ExtOut("Max Records Returned: %u\n", g_dwMaxRecords);
|
|
ExtOut("Search Order: %s\n", szSearchOrder);
|
|
ExtOut("Data Display Width: %u\n", g_wDataDisplayWidth);
|
|
ExtOut("--------------------------------\n");
|
|
ExtOut("Bounding Record Numbers:\n");
|
|
ExtOut(" Application Event Log: %u\n", g_dwRecordOffsetAppEvt);
|
|
ExtOut(" System Event Log: %u\n", g_dwRecordOffsetSysEvt);
|
|
ExtOut(" Security Event Log: %u\n", g_dwRecordOffsetSecEvt);
|
|
ExtOut("--------------------------------\n");
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Debugger extension(s) options implementation
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
HRESULT
|
|
EvLogAddSource ( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the addsource command to
|
|
the !evlog extension. It is used to add an event source to the registry
|
|
so the events logged by that source display their description correctly
|
|
instead of displaying the error message:
|
|
|
|
Example: (of bad event source)
|
|
========
|
|
Description:
|
|
The description for Event ID ( 2 ) in Source ( DebuggerExtensions )
|
|
cannot be found. The local computer may not have the necessary registry
|
|
information or message DLL files to display messages from a remote
|
|
computer. You may be able to use the /AUXSOURCE= flag to retrieve this
|
|
description; see Help and Support for details. The following information
|
|
is part of the event: Test Message with Event ID 2.
|
|
|
|
Example: (of good event source registered correctly)
|
|
========
|
|
Description:
|
|
Test Message with Event ID 4000For more information, see Help and Support
|
|
Center at http://go.microsoft.com/fwlink/events.asp.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
HKEY hk = NULL;
|
|
HMODULE hModule = NULL;
|
|
DWORD dwTypesSupported = 0;
|
|
DWORD dwDisposition = 0;
|
|
DWORD lResult = ERROR_SUCCESS;
|
|
const DWORD cdwDefaultTypesSupported =
|
|
EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
|
|
EVENTLOG_INFORMATION_TYPE |
|
|
EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
|
|
CHAR szParamValue[MAX_PATH];
|
|
CHAR szSource[MAX_PATH+1];
|
|
CHAR szMessageFile[MAX_PATH+1];
|
|
CHAR szRegPath[MAX_PATH+1];
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
" !evlog addsource [-d] [-s <source>] [-t <types>] [-f <msgfile>]"
|
|
"\n\n"
|
|
"Adds an event source to the registry. By default, only adds "
|
|
"DebuggerExtensions\n"
|
|
"event source to support !evlog report.\n\n"
|
|
"Use !dreg to see the values added.\n\n"
|
|
"Example:\n"
|
|
" !dreg hklm\\system\\currentcontrolset\\services\\eventlog\\"
|
|
"Application\\<source>!*\n\n"
|
|
"Optional parameters:\n"
|
|
"-d : Use defaults\n"
|
|
"<source> : (default: DebuggerExtensions)\n"
|
|
"<types> : All (default: 31), Success, Error (1), Warning (2),\n"
|
|
" Information (4), Audit_Success (8), or Audit_Failure "
|
|
"(16)\n"
|
|
"<msgfile> : (default: local path to ext.dll)\n";
|
|
const CHAR cszDefaultSource[] = "DebuggerExtensions";
|
|
const CHAR cszDefaultExtensionDll[] = "uext.dll";
|
|
|
|
INIT_API();
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
CopyString(szSource, cszDefaultSource, sizeof(szSource));
|
|
dwTypesSupported = cdwDefaultTypesSupported;
|
|
|
|
hModule = GetModuleHandle(cszDefaultExtensionDll);
|
|
GetModuleFileName(hModule, szMessageFile, MAX_PATH);
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse args
|
|
while (*args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
|
|
// Check for optional argument options to appear first
|
|
if (('-' == *args) || ('/' == *args))
|
|
{
|
|
CHAR ch = *(++args); // Get next char + advance arg ptr
|
|
++args; // Skip one more char
|
|
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
|
|
SKIP_WSPACE(args); // Advance to start of param value
|
|
|
|
// Skip looking for value if this is start of another
|
|
// parameter
|
|
if (('-' != *args) && ('/' != *args))
|
|
{
|
|
// Parameter value is delimited by next space in string, or,
|
|
// if quoted, by next quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
szEndOfValue = strchr(args, '"');
|
|
}
|
|
else
|
|
{
|
|
szEndOfValue = strchr(args, ' ');
|
|
}
|
|
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += min(sizeof(szParamValue), strlen(args));
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// skip past (theoretically) paired quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
}
|
|
}
|
|
}
|
|
switch (ch)
|
|
{
|
|
case 'd': // Use defaults
|
|
ExtVerb("Using defaults...\n");
|
|
// do nothing
|
|
break;
|
|
case 's': // Source (string)
|
|
ExtVerb("Setting Source...\n");
|
|
CopyString(szSource, szParamValue, sizeof(szSource));
|
|
break;
|
|
case 't': // Event Type (number or string)
|
|
ExtVerb("Setting Event Type...\n");
|
|
if (!_strnicmp(szParamValue, "All", 3))
|
|
{
|
|
dwTypesSupported = EVENTLOG_ERROR_TYPE |
|
|
EVENTLOG_WARNING_TYPE |
|
|
EVENTLOG_INFORMATION_TYPE |
|
|
EVENTLOG_AUDIT_SUCCESS |
|
|
EVENTLOG_AUDIT_FAILURE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Error", 5))
|
|
{
|
|
dwTypesSupported = EVENTLOG_ERROR_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Warning", 7))
|
|
{
|
|
dwTypesSupported = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Information", 11))
|
|
{
|
|
dwTypesSupported = EVENTLOG_INFORMATION_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Audit_Success", 13))
|
|
{
|
|
dwTypesSupported = EVENTLOG_AUDIT_SUCCESS;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Audit_Failure", 13))
|
|
{
|
|
dwTypesSupported = EVENTLOG_AUDIT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
dwTypesSupported = strtoul(szParamValue, NULL, 10);
|
|
}
|
|
break;
|
|
case 'f': // Message File
|
|
ExtVerb("Setting Message File...\n");
|
|
CopyString(szMessageFile,
|
|
szParamValue,
|
|
sizeof(szMessageFile));
|
|
break;
|
|
default:
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '-%c' specified\n", *args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
|
|
}
|
|
else // Everything to end of line is message string
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '%s' specified\n", args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
// Add source name as subkey under Application in EventLog registry key
|
|
PrintString(szRegPath,
|
|
sizeof(szRegPath),
|
|
"SYSTEM\\CurrentControlSet\\Services\\EventLog\\"
|
|
"Application\\%s",
|
|
szSource);
|
|
lResult = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE, // key
|
|
szRegPath, // subkey to open or create
|
|
0, // reserved, must be zero
|
|
"", // object type class
|
|
REG_OPTION_NON_VOLATILE, // special options (preserves data
|
|
// after system restart)
|
|
KEY_READ | KEY_WRITE, // access mask
|
|
NULL, // security descriptor (NULL = not inherited
|
|
// by child processes)
|
|
&hk, // returned handle to opened or created key
|
|
&dwDisposition); // returns REG_CREATED_NEW_KEY or
|
|
// REG_OPENED_EXISTING_KEY
|
|
if (ERROR_SUCCESS != lResult)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(lResult);
|
|
ExtErr("Could not open or create key, %u\n", lResult);
|
|
goto Exit;
|
|
}
|
|
|
|
if (REG_CREATED_NEW_KEY == dwDisposition)
|
|
{
|
|
ExtOut("Created key:\nHKLM\\%s\n", szRegPath);
|
|
}
|
|
else if (REG_OPENED_EXISTING_KEY == dwDisposition)
|
|
{
|
|
ExtOut("Opened key:\nHKLM\\%s\n", szRegPath);
|
|
}
|
|
else
|
|
{
|
|
ExtWarn("Warning: Unexpected disposition action %u\n"
|
|
"key: HKLM\\%s",
|
|
szRegPath,
|
|
dwDisposition);
|
|
}
|
|
|
|
// Set the value for EventMessageFile
|
|
ExtVerb("Setting EventMessageFile to %s...\n", szMessageFile);
|
|
lResult = RegSetValueEx(
|
|
hk, // subkey handle
|
|
"EventMessageFile", // value name
|
|
0, // must be zero
|
|
REG_EXPAND_SZ, // value type
|
|
(LPBYTE) szMessageFile, // pointer to value data
|
|
strlen(szMessageFile) + 1); // length of value data
|
|
if (ERROR_SUCCESS != lResult)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(lResult);
|
|
ExtErr("Could not set EventMessageFile, %u\n", lResult);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut(" EventMessageFile: %s\n", szMessageFile);
|
|
|
|
// Set the supported event types value in the TypesSupported
|
|
ExtVerb("Setting TypesSupported to %u...\n", dwTypesSupported);
|
|
lResult = RegSetValueEx(
|
|
hk, // subkey handle
|
|
"TypesSupported", // value name
|
|
0, // must be zero
|
|
REG_DWORD, // value type
|
|
(LPBYTE) &dwTypesSupported, // pointer to value data
|
|
sizeof(DWORD)); // length of value data
|
|
if (ERROR_SUCCESS != lResult)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(lResult);
|
|
ExtErr("Could not set TypesSupported, %u\n", lResult);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut(" TypesSupported: %u\n", dwTypesSupported);
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (NULL != hk)
|
|
{
|
|
RegCloseKey(hk);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EvLogBackup ( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the backup command to the
|
|
!evlog extension. It is used to create a backup of an event log to a
|
|
file.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
d!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEventLog = NULL;
|
|
DWORD dwDirLen = 0;
|
|
CHAR szParamValue[MAX_PATH];
|
|
CHAR szEventLog[MAX_PATH+1];
|
|
CHAR szBackupFileName[MAX_PATH+1];
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
" !evlog backup [-d] [-l <eventlog>] [-f <filename>]\n\n"
|
|
"Makes backup of specified event log to a file.\n\n"
|
|
"Optional parameters:\n"
|
|
"-d : Use defaults\n"
|
|
"<eventlog> : Application (default), System, Security\n"
|
|
"<filename> : (default: %%cwd%%\\<eventlog>_backup.evt)\n";
|
|
const CHAR cszDefaultEventLog[] = "Application";
|
|
const CHAR cszDefaultFileNameAppend[] = "_backup.evt";
|
|
|
|
INIT_API();
|
|
|
|
// Initialize defaults
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
|
|
// Create default backup filename: %cwd%\Application_backup.evt
|
|
dwDirLen = GetCurrentDirectory(sizeof(szEventLog)/sizeof(TCHAR),
|
|
szEventLog); // temp use of szEventLog
|
|
if (0 == dwDirLen)
|
|
{
|
|
ExtErr("ERROR: Current directory length too long. Using '.' for "
|
|
"directory\n");
|
|
CopyString(szEventLog, ".", sizeof(szEventLog));
|
|
}
|
|
PrintString(szBackupFileName,
|
|
sizeof(szBackupFileName),
|
|
"%s\\%s%s",
|
|
szEventLog,
|
|
cszDefaultEventLog,
|
|
cszDefaultFileNameAppend);
|
|
ZeroMemory(szEventLog, sizeof(szEventLog));
|
|
CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse args
|
|
while (*args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
|
|
// Check for optional argument options to appear first
|
|
if (('-' == *args) || ('/' == *args))
|
|
{
|
|
CHAR ch = *(++args); // Get next char + advance arg ptr
|
|
++args; // Skip one more char
|
|
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
|
|
SKIP_WSPACE(args); // Advance to start of param value
|
|
|
|
if (('-' != *args) && ('/' != *args))
|
|
{
|
|
// Parameter value is delimited by next space in string, or,
|
|
// if quoted, by next quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
szEndOfValue = strchr(args, '"');
|
|
}
|
|
else
|
|
{
|
|
szEndOfValue = strchr(args, ' ');
|
|
}
|
|
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += min(sizeof(szParamValue), strlen(args));
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// skip past (theoretically) paired quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
}
|
|
}
|
|
}
|
|
switch (ch)
|
|
{
|
|
case 'd': // Use defaults
|
|
ExtVerb("Using defaults...\n");
|
|
// do nothing
|
|
break;
|
|
case 'l': // Source (string)
|
|
ExtVerb("Setting Event Log...\n");
|
|
CopyString(szEventLog, szParamValue, sizeof(szEventLog));
|
|
break;
|
|
case 'f': // Message File
|
|
ExtVerb("Setting Backup File Name...\n");
|
|
CopyString(szBackupFileName,
|
|
szParamValue,
|
|
sizeof(szBackupFileName));
|
|
break;
|
|
default:
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '-%c' specified\n", *args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
|
|
}
|
|
else // Everything to end of line is message string
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '%s' specified\n", args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
// Get a handle to the event log
|
|
ExtVerb("Opening event log '%s'...", szEventLog);
|
|
hEventLog = OpenEventLog(
|
|
NULL, // uses local computer
|
|
szEventLog); // source name
|
|
if (NULL == hEventLog)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
// Backup event log
|
|
ExtOut("Backing up '%s' event log...\n", szEventLog);
|
|
if (!BackupEventLog(
|
|
hEventLog, // handle to event log
|
|
szBackupFileName)) // name of backup file
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to backup event log to '%s', 0x%08X\n",
|
|
szBackupFileName,
|
|
Status);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut("Event log successfully backed up to '%s'\n", szBackupFileName);
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (hEventLog)
|
|
{
|
|
CloseEventLog(hEventLog);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EvLogOption( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the option command to the
|
|
!evlog extension. It is used to modify and display the cached settings.
|
|
Currently all cached option settings are used by the read command only.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
WORD wDataDisplayWidth = g_wDataDisplayWidth;
|
|
DWORD dwRecordOffset = g_cdwDefaultRecordOffset;
|
|
DWORD dwReadFlags = g_cdwDefaultReadFlags;
|
|
enum
|
|
{
|
|
MASK_IGNORE_RECORD_OFFSET=0x0000,
|
|
MASK_RESET_RECORD_OFFSET_DEFAULT=0x0001,
|
|
MASK_SET_MAX_RECORD_OFFSET=0x0002,
|
|
MASK_SET_RECORD_OFFSET=0x0004
|
|
} fMaskRecordOffset = MASK_IGNORE_RECORD_OFFSET;
|
|
CHAR szEventLog[MAX_PATH+1];
|
|
CHAR szParamValue[MAX_PATH];
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
" !evlog option [-d] [-!] [-n <count>] [[-l <eventlog>] -+ | "
|
|
"-r <record>]\n"
|
|
" [-o <order>] [-w <width>]\n\n"
|
|
"Sets and resets default search option parameters for read command."
|
|
"\n\n"
|
|
"A backwards search order implies that by default all searches start "
|
|
"from the\n"
|
|
"most recent record logged to the event log and the search continues "
|
|
"in\n"
|
|
"reverse chronological order as matching records are found.\n\n"
|
|
"Bounding record numbers for each event log allow searches to "
|
|
"terminate after\n"
|
|
"a known record number is encountered. This can be useful when you "
|
|
"want to\n"
|
|
"view all records logged after a certain event only.\n\n"
|
|
"Optional parameters:\n"
|
|
"-d : Display defaults\n"
|
|
"-! : Reset all defaults\n"
|
|
"<count> : Count of max N records to retrieve for any query "
|
|
"(default: 20)\n"
|
|
"<eventlog> : All (default), Application, System, Security\n"
|
|
"-+ : Set bounding record # to current max record #\n"
|
|
"<record> : Use as bounding record # in read queries (default: 0 = "
|
|
"ignore)\n"
|
|
"<order> : Search order Forwards, Backwards (default: Backwards)\n"
|
|
"<width> : Set data display width (in bytes). This is the width "
|
|
"that \"Data:\"\n"
|
|
" sections display. (default: 8, same as event log)\n";
|
|
const CHAR cszDefaultEventLog[] = "All";
|
|
|
|
INIT_API();
|
|
|
|
// Initialize defaults
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse args
|
|
while (*args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
|
|
// Check for optional argument options
|
|
if (('-' == *args) || ('/' == *args))
|
|
{
|
|
CHAR ch = *(++args); // Get next char + advance arg ptr
|
|
++args; // Skip one more char
|
|
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
|
|
SKIP_WSPACE(args); // Advance to start of param value
|
|
|
|
// Parameter value is delimited by next space in string, or,
|
|
// if quoted, by next quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
szEndOfValue = strchr(args, '"');
|
|
}
|
|
else
|
|
{
|
|
szEndOfValue = strchr(args, ' ');
|
|
}
|
|
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += strlen(args);
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
if ('"' == *args) // skip past (theoretically) paired quote
|
|
{
|
|
++args;
|
|
}
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case 'd': // Use defaults
|
|
ExtVerb("Using defaults...\n");
|
|
// do nothing
|
|
break;
|
|
case 'l': // Source (string)
|
|
ExtVerb("Setting Event Log...\n");
|
|
CopyString(szEventLog, szParamValue, sizeof(szEventLog));
|
|
break;
|
|
case 'n': // number of records to retrieve
|
|
ExtVerb("Setting Max Record Count...\n");
|
|
g_dwMaxRecords = strtoul(szParamValue, NULL, 10);
|
|
break;
|
|
case '!': // Use defaults
|
|
ExtVerb("Resetting Defaults...\n");
|
|
fMaskRecordOffset = MASK_RESET_RECORD_OFFSET_DEFAULT;
|
|
g_dwMaxRecords = g_cdwDefaultMaxRecords;
|
|
g_dwReadFlags = g_cdwDefaultReadFlags;
|
|
g_wDataDisplayWidth = g_cwMaxDataDisplayWidth;
|
|
break;
|
|
case '+': // Set to max record number for event source
|
|
ExtVerb(
|
|
"Setting Record Number to Max Record Number...\n");
|
|
fMaskRecordOffset = MASK_SET_MAX_RECORD_OFFSET;
|
|
break;
|
|
case 'r': // record offset for bounds
|
|
ExtVerb("Setting Record Number...\n");
|
|
if (!_strnicmp(szParamValue, "0x", 2))
|
|
{
|
|
dwRecordOffset = strtoul(szParamValue, NULL, 16);
|
|
}
|
|
else
|
|
{
|
|
dwRecordOffset = strtoul(szParamValue, NULL, 10);
|
|
}
|
|
dwReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
|
|
fMaskRecordOffset = MASK_SET_RECORD_OFFSET;
|
|
break;
|
|
case 'o': // Source (string)
|
|
ExtVerb("Setting Search Order...\n");
|
|
if (!_stricmp(szParamValue, "Forwards"))
|
|
{
|
|
g_dwReadFlags ^= BACKWARDS_READ;
|
|
g_dwReadFlags |= FORWARDS_READ;
|
|
}
|
|
else if (!_stricmp(szParamValue, "Backwards"))
|
|
{
|
|
g_dwReadFlags ^= FORWARDS_READ;
|
|
g_dwReadFlags |= BACKWARDS_READ;
|
|
}
|
|
else
|
|
{
|
|
ExtErr("Ignoring invalid search order option '%s'\n",
|
|
szParamValue);
|
|
}
|
|
break;
|
|
case 'w': // record offset for bounds
|
|
ExtVerb("Setting Data Display Width...\n");
|
|
if (!_strnicmp(szParamValue, "0x", 2))
|
|
{
|
|
wDataDisplayWidth = (WORD)strtoul(szParamValue,
|
|
NULL,
|
|
16);
|
|
}
|
|
else
|
|
{
|
|
wDataDisplayWidth = (WORD)strtoul(szParamValue,
|
|
NULL,
|
|
10);
|
|
}
|
|
|
|
if ((0 == wDataDisplayWidth) ||
|
|
(wDataDisplayWidth > g_cwMaxDataDisplayWidth))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("ERROR: Data display width %u exceeds bounds "
|
|
"(1...%u)\n",
|
|
wDataDisplayWidth, g_cwMaxDataDisplayWidth);
|
|
goto Exit;
|
|
}
|
|
g_wDataDisplayWidth = wDataDisplayWidth;
|
|
break;
|
|
default:
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '-%c' specified\n", *args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
|
|
}
|
|
else // Everything to end of line is message string
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '%s' specified\n", args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
// Set any variables not already set here
|
|
if (!_stricmp(szEventLog, "Application"))
|
|
{
|
|
if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetAppEvt = dwRecordOffset;
|
|
}
|
|
else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetAppEvt = g_cdwDefaultRecordOffset;
|
|
}
|
|
else if (MASK_SET_MAX_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
GetEvLogNewestRecord("Application", &g_dwRecordOffsetAppEvt);
|
|
}
|
|
else
|
|
{
|
|
; // error
|
|
}
|
|
|
|
}
|
|
else if (!_stricmp(szEventLog, "System"))
|
|
{
|
|
if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetSysEvt = dwRecordOffset;
|
|
}
|
|
else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetSysEvt = g_cdwDefaultRecordOffset;
|
|
}
|
|
else if (MASK_SET_MAX_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
GetEvLogNewestRecord("System", &g_dwRecordOffsetSysEvt);
|
|
}
|
|
else
|
|
{
|
|
; // error
|
|
}
|
|
}
|
|
else if (!_stricmp(szEventLog, "Security"))
|
|
{
|
|
if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetSecEvt = dwRecordOffset;
|
|
}
|
|
else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetSecEvt = g_cdwDefaultRecordOffset;
|
|
}
|
|
else if (MASK_SET_MAX_RECORD_OFFSET& fMaskRecordOffset)
|
|
{
|
|
GetEvLogNewestRecord("Security", &g_dwRecordOffsetSecEvt);
|
|
}
|
|
else
|
|
{
|
|
; // error
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (MASK_SET_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetAppEvt = dwRecordOffset;
|
|
g_dwRecordOffsetSysEvt = dwRecordOffset;
|
|
g_dwRecordOffsetSecEvt = dwRecordOffset;
|
|
}
|
|
else if (MASK_RESET_RECORD_OFFSET_DEFAULT & fMaskRecordOffset)
|
|
{
|
|
g_dwRecordOffsetAppEvt = g_cdwDefaultRecordOffset;
|
|
g_dwRecordOffsetSysEvt = g_cdwDefaultRecordOffset;
|
|
g_dwRecordOffsetSecEvt = g_cdwDefaultRecordOffset;
|
|
}
|
|
else if (MASK_SET_MAX_RECORD_OFFSET & fMaskRecordOffset)
|
|
{
|
|
GetEvLogNewestRecord("Application", &g_dwRecordOffsetAppEvt);
|
|
GetEvLogNewestRecord("System", &g_dwRecordOffsetSysEvt);
|
|
GetEvLogNewestRecord("Security", &g_dwRecordOffsetSecEvt);
|
|
}
|
|
else
|
|
{
|
|
if (fMaskRecordOffset)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("ERROR: Must specify -!, -+, or -r option\n");
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Display defaults here
|
|
PrintEvLogOptionSettings();
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EvLogClear ( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the clear command to the
|
|
!evlog extension. It is used to clear and, optionally, backup an event
|
|
log to a file.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEventLog = NULL;
|
|
BOOL fIgnoreBackup = FALSE;
|
|
DWORD dwDirLen = 0;
|
|
CHAR szParamValue[MAX_PATH];
|
|
CHAR szEventLog[MAX_PATH+1];
|
|
CHAR szBackupFileName[MAX_PATH+1];
|
|
PCHAR pszBackupFileName = NULL;
|
|
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
" !evlog clear [-!] [-d] [-l <eventlog>] [-f <filename>]\n\n"
|
|
"Clears and creates backup of specified event log.\n\n"
|
|
"Optional parameters:\n"
|
|
"-! : Ignore backup\n"
|
|
"-d : Use defaults\n"
|
|
"<eventlog> : Application (default), System, Security\n"
|
|
"<filename> : (default: %%cwd%%\\<eventlog>_backup.evt)\n";
|
|
const CHAR cszDefaultEventLog[] = "Application";
|
|
const CHAR cszDefaultFileNameAppend[] = "_backup.evt";
|
|
|
|
INIT_API();
|
|
|
|
// Initialize default
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
|
|
// Create default backup filename: %cwd%\Application_backup.evt
|
|
dwDirLen = GetCurrentDirectory(sizeof(szEventLog)/sizeof(TCHAR),
|
|
szEventLog); // temp use of szEventLog
|
|
if (0 == dwDirLen)
|
|
{
|
|
ExtErr("ERROR: Current directory length too long. Using '.' for "
|
|
"directory\n");
|
|
CopyString(szEventLog, ".", sizeof(szEventLog));
|
|
}
|
|
PrintString(szBackupFileName,
|
|
sizeof(szBackupFileName),
|
|
"%s\\%s%s",
|
|
szEventLog,
|
|
cszDefaultEventLog,
|
|
cszDefaultFileNameAppend);
|
|
ZeroMemory(szEventLog, sizeof(szEventLog));
|
|
CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse args
|
|
while (*args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
|
|
// Check for optional argument options to appear first
|
|
if (('-' == *args) || ('/' == *args))
|
|
{
|
|
CHAR ch = *(++args); // Get next char + advance arg ptr
|
|
++args; // Skip one more char
|
|
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
|
|
SKIP_WSPACE(args); // Advance to start of param value
|
|
|
|
if (('-' != *args) && ('/' != *args))
|
|
{
|
|
// Parameter value is delimited by next space in string, or,
|
|
// if quoted, by next quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
szEndOfValue = strchr(args, '"');
|
|
}
|
|
else
|
|
{
|
|
szEndOfValue = strchr(args, ' ');
|
|
}
|
|
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += strlen(args);
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// skip past (theoretically) paired quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
}
|
|
}
|
|
}
|
|
switch (ch)
|
|
{
|
|
case '!': // Use defaults
|
|
ExtVerb("Ignoring default backup procedure...\n");
|
|
fIgnoreBackup = TRUE;
|
|
break;
|
|
case 'd': // Use defaults
|
|
ExtVerb("Using defaults...\n");
|
|
// do nothing
|
|
break;
|
|
case 'l': // Source (string)
|
|
ExtVerb("Setting Event Log...\n");
|
|
CopyString(szEventLog, szParamValue, sizeof(szEventLog));
|
|
break;
|
|
case 'f': // Message File
|
|
ExtVerb("Setting Backup File Name...\n");
|
|
CopyString(szBackupFileName,
|
|
szParamValue,
|
|
sizeof(szBackupFileName));
|
|
break;
|
|
default:
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '-%c' specified\n", *args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
|
|
}
|
|
else // Everything to end of line is message string
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '%s' specified\n", args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
// Get a handle to the event log
|
|
ExtVerb("Opening event log '%s'...", szEventLog);
|
|
hEventLog = OpenEventLog(
|
|
NULL, // uses local computer
|
|
szEventLog); // source name
|
|
if (NULL == hEventLog)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open '%s' event log, 0x%08X\n", szEventLog, Status);
|
|
goto Exit;
|
|
}
|
|
|
|
if (fIgnoreBackup)
|
|
{
|
|
pszBackupFileName = NULL;
|
|
}
|
|
else
|
|
{
|
|
pszBackupFileName = szBackupFileName;
|
|
}
|
|
|
|
// Clear event log
|
|
ExtOut("Clearing '%s' event log...\n", szEventLog);
|
|
if (!ClearEventLog(
|
|
hEventLog, // handle to event log
|
|
pszBackupFileName)) // name of backup file
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to clear event log and backup to '%s', 0x%08X\n",
|
|
szBackupFileName,
|
|
Status);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut("Event log successfully backed up to '%s'\n", szBackupFileName);
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (hEventLog)
|
|
{
|
|
CloseEventLog(hEventLog);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EvLogInfo ( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the info command to the
|
|
!evlog extension. It is used to display summary information about all 3
|
|
standard event logs: Application, System, and Security.
|
|
|
|
A user without rights to see the System or Security event log will
|
|
probably get an error.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
S_OK
|
|
|
|
--*/
|
|
{
|
|
INIT_API();
|
|
|
|
PrintEvLogSummary("Application");
|
|
PrintEvLogSummary("System");
|
|
PrintEvLogSummary("Security");
|
|
|
|
ExtOut("--------------------------------\n");
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EvLogRead ( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the read command to the
|
|
!evlog extension. It is used to display event records from any event
|
|
log.
|
|
|
|
Some search parameters can be set to filter the list of events displayed.
|
|
Also, the !evlog option command can be used to set some default
|
|
parameters.
|
|
|
|
A user without rights to see the System or Security event log will
|
|
probably get an error.
|
|
|
|
There are a few common scenarios where this extension command might be
|
|
used:
|
|
1) To identify when the last event was logged (may have been a few
|
|
minutes ago or days...)
|
|
2) To search for recent occurrences of specific known "interesting"
|
|
events
|
|
3) To monitor events logged as a result of (or side effect of) stepping
|
|
over an instruction
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEventLog = NULL;
|
|
EVENTLOGRECORD *pevlr = NULL;
|
|
BYTE *pbBuffer = NULL;
|
|
LPCSTR cszMessage = NULL;
|
|
DWORD dwEventID = 0; // default (normally event id is non-zero)
|
|
WORD wEventCategory = 0; // default (normally categories start at 1)
|
|
WORD wEventType = 0; // default
|
|
const DWORD cdwDefaultBufSize = 4096; // default
|
|
DWORD dwBufSize = cdwDefaultBufSize;
|
|
DWORD dwBytesRead = 0;
|
|
DWORD dwBytesNeeded = 0;
|
|
DWORD dwReadFlags = g_dwReadFlags | EVENTLOG_SEQUENTIAL_READ;
|
|
DWORD dwRecordOffset = 0; // 0 = ignored for sequential reads
|
|
DWORD dwNumRecords = 0; // Count of records matched/found
|
|
DWORD dwTotalRecords = 0; // Count of records enumerated (for debugging)
|
|
DWORD dwMaxRecords = g_dwMaxRecords;
|
|
DWORD dwBoundingEventRecord = 0;
|
|
DWORD dwLastErr = 0;
|
|
BOOL fSuccess = FALSE;
|
|
BOOL fMatchSource = FALSE;
|
|
BOOL fMatchEventID = FALSE;
|
|
BOOL fMatchEventCategory = FALSE;
|
|
BOOL fMatchEventType = FALSE;
|
|
CHAR szEventLog[MAX_PATH+1];
|
|
CHAR szSource[MAX_PATH+1];
|
|
CHAR szParamValue[MAX_PATH];
|
|
// Note: this could get really fancy, searching on description, date
|
|
// ranges, event id ranges, data, etc. Keep it simple to just display
|
|
// last few events logged.
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
" !evlog read [-d] [-l <eventlog>] [-s <source>] "
|
|
"[-e <id>] [-c <category>]\n"
|
|
" [-t <type>] [-n <count>] [-r <record>]\n\n"
|
|
"Displays last N events logged to the specified event log, in "
|
|
"reverse\n"
|
|
"chronological order by default. If -n option is not specified, a "
|
|
"default max\n"
|
|
"of 20 records is enforced.\n\n"
|
|
"However, if -r is specified, only the specific event record will "
|
|
"be\n"
|
|
"displayed unless the -n option is also specified.\n\n"
|
|
"!evlog option can be used to override some defaults, including the "
|
|
"search\n"
|
|
"order of backwards. See !evlog option -d for default settings.\n\n"
|
|
"Optional parameters:\n"
|
|
"-d : Use defaults\n"
|
|
"<eventlog> : Application (default), System, Security\n"
|
|
"<source> : DebuggerExtensions (default: none)\n"
|
|
"<id> : 0, 1000, 2000, 3000, 4000, etc... (default: 0)\n"
|
|
"<category> : None (default: 0), Devices (1), Disk (2), Printers "
|
|
"(3),\n"
|
|
" Services (4), Shell (5), System_Event (6), Network "
|
|
"(7)\n"
|
|
"<type> : Success (default: 0), Error (1), Warning (2), "
|
|
"Information (4),\n"
|
|
" Audit_Success (8), or Audit_Failure (16)\n"
|
|
"<count> : Count of last N event records to retrieve (default: "
|
|
"1)\n"
|
|
"<record> : Specific record # to retrieve\n";
|
|
const CHAR cszDefaultEventLog[] = "Application";
|
|
const CHAR cszDefaultSource[] = "DebuggerExtensions";
|
|
|
|
INIT_API();
|
|
|
|
// Initialize defaults
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
CopyString(szEventLog, cszDefaultEventLog, sizeof(szEventLog));
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse args
|
|
while (*args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
|
|
// Check for optional argument options
|
|
if (('-' == *args) || ('/' == *args))
|
|
{
|
|
CHAR ch = *(++args); // Get next char + advance arg ptr
|
|
++args; // Skip one more char
|
|
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
|
|
SKIP_WSPACE(args); // Advance to start of param value
|
|
|
|
// Parameter value is delimited by next space in string, or,
|
|
// if quoted, by next quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
szEndOfValue = strchr(args, '"');
|
|
}
|
|
else
|
|
{
|
|
szEndOfValue = strchr(args, ' ');
|
|
}
|
|
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += strlen(args);
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// skip past (theoretically) paired quote
|
|
if ('"' == *args)
|
|
{
|
|
++args;
|
|
}
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case 'd': // Use defaults
|
|
ExtVerb("Using defaults...\n");
|
|
// do nothing
|
|
break;
|
|
case 'l': // Source (string)
|
|
ExtVerb("Setting Event Log...\n");
|
|
CopyString(szEventLog, szParamValue, sizeof(szEventLog));
|
|
break;
|
|
case 's': // Source (string)
|
|
ExtVerb("Setting Source...\n");
|
|
CopyString(szSource, szParamValue, sizeof(szSource));
|
|
fMatchSource = TRUE;
|
|
break;
|
|
case 'e': // Event ID (number or string)
|
|
ExtVerb("Setting Event ID...\n");
|
|
//
|
|
// Some events only display the low WORD, but the high
|
|
// WORD actually contains a status code like 8000 or
|
|
// C000.
|
|
// So allow for hex input as well as decimal...
|
|
//
|
|
if (!_strnicmp(szParamValue, "0x", 2))
|
|
{
|
|
dwEventID = strtoul(szParamValue, NULL, 16);
|
|
}
|
|
else
|
|
{
|
|
dwEventID = strtoul(szParamValue, NULL, 10);
|
|
}
|
|
fMatchEventID = TRUE;
|
|
break;
|
|
case 'c': // Event Category (number or string)
|
|
ExtVerb("Setting Category...\n");
|
|
if (!_strnicmp(szParamValue, "None", 4))
|
|
{
|
|
wEventCategory = 0;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Devices", 7))
|
|
{
|
|
wEventCategory = 1;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Disk", 4))
|
|
{
|
|
wEventCategory = 2;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Printers", 8))
|
|
{
|
|
wEventCategory = 3;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Services", 8))
|
|
{
|
|
wEventCategory = 4;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Shell", 5))
|
|
{
|
|
wEventCategory = 5;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "System_Event", 12))
|
|
{
|
|
wEventCategory = 6;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Network", 7))
|
|
{
|
|
wEventCategory = 7;
|
|
}
|
|
else
|
|
{
|
|
wEventCategory = (WORD)strtoul(szParamValue,
|
|
NULL, 10);
|
|
}
|
|
fMatchEventCategory = TRUE;
|
|
break;
|
|
case 't': // Event Type (number or string)
|
|
ExtVerb("Setting Event Type...\n");
|
|
if (!_strnicmp(szParamValue, "Success", 7))
|
|
{
|
|
wEventType = EVENTLOG_SUCCESS;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Error", 5))
|
|
{
|
|
wEventType = EVENTLOG_ERROR_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Warning", 7))
|
|
{
|
|
wEventType = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Information", 11))
|
|
{
|
|
wEventType = EVENTLOG_INFORMATION_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Audit_Success", 13))
|
|
{
|
|
wEventType = EVENTLOG_AUDIT_SUCCESS;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Audit_Failure", 13))
|
|
{
|
|
wEventType = EVENTLOG_AUDIT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
wEventType = (WORD)strtoul(szParamValue, NULL, 10);
|
|
}
|
|
fMatchEventType = TRUE;
|
|
break;
|
|
case 'n': // number of records to retrieve
|
|
ExtVerb("Setting Max Record Count...\n");
|
|
dwMaxRecords = strtoul(szParamValue, NULL, 10);
|
|
break;
|
|
case 'r': // record offset + switch flags to do seek
|
|
ExtVerb("Setting Record Number...\n");
|
|
dwRecordOffset = strtoul(szParamValue, NULL, 10);
|
|
dwReadFlags ^= EVENTLOG_SEQUENTIAL_READ; // disable
|
|
dwReadFlags |= EVENTLOG_SEEK_READ; // enable
|
|
dwMaxRecords = 1;
|
|
break;
|
|
default:
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '-%c' specified\n", *args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
|
|
}
|
|
else // Everything to end of line is message string
|
|
{
|
|
SKIP_WSPACE(args);
|
|
cszMessage = args;
|
|
args += strlen(args);
|
|
}
|
|
|
|
}
|
|
|
|
// Get a handle to the event log
|
|
ExtVerb("Opening event log '%s'...", szEventLog);
|
|
hEventLog = OpenEventLog(NULL, szEventLog);
|
|
if (!hEventLog)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open event log, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
// If this record number is hit during read, it will
|
|
// not be displayed and reads will halt immediately
|
|
if (!strcmp(szEventLog, "System"))
|
|
{
|
|
dwBoundingEventRecord = g_dwRecordOffsetSysEvt;
|
|
}
|
|
else if (!strcmp(szEventLog, "Security"))
|
|
{
|
|
dwBoundingEventRecord = g_dwRecordOffsetSecEvt;
|
|
}
|
|
else // szEventLog == "Application" or default
|
|
{
|
|
dwBoundingEventRecord = g_dwRecordOffsetAppEvt;
|
|
}
|
|
|
|
ExtVerb("Using Bounding Event Record %u...\n", dwBoundingEventRecord);
|
|
|
|
do
|
|
{
|
|
// Allocate buffer if unallocated
|
|
if (NULL == pbBuffer)
|
|
{
|
|
pbBuffer = (BYTE *)calloc(dwBufSize, sizeof(BYTE));
|
|
if (NULL == pbBuffer)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
|
|
ExtErr("Unable to allocate buffer, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Reposition to beginning of buffer for next read
|
|
pevlr = (EVENTLOGRECORD *) pbBuffer;
|
|
|
|
// Read next N event records that fit into dwBufSize
|
|
fSuccess = ReadEventLog(
|
|
hEventLog, // handle to event log
|
|
dwReadFlags, // how to read log
|
|
dwRecordOffset, // initial record offset
|
|
pevlr, // buffer for read data
|
|
dwBufSize, // bytes to read
|
|
&dwBytesRead, // number of bytes read
|
|
&dwBytesNeeded); // bytes required
|
|
if (!fSuccess)
|
|
{
|
|
dwLastErr = GetLastError();
|
|
if (ERROR_INSUFFICIENT_BUFFER == dwLastErr)
|
|
{
|
|
ExtVerb("Increasing buffer from %u to %u bytes\n",
|
|
dwBufSize, dwBufSize+cdwDefaultBufSize);
|
|
// Retry ReadEventLog with larger buffer next time
|
|
dwBufSize += cdwDefaultBufSize;
|
|
free(pbBuffer);
|
|
pbBuffer = NULL; // allow reallocation above
|
|
|
|
// TODO: Really should put upper limit on buffer size
|
|
continue; // retry ReadEventLog
|
|
}
|
|
else if (ERROR_HANDLE_EOF == dwLastErr)
|
|
{
|
|
Status = S_OK;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
ExtErr("Error reading event log, %u\n", dwLastErr);
|
|
Status = HRESULT_FROM_WIN32(dwLastErr);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Go through all records returned from last successful read
|
|
// Display only the ones that match the criteria
|
|
do
|
|
{
|
|
if (dwBoundingEventRecord == pevlr->RecordNumber)
|
|
{
|
|
ExtWarn("Bounding record #%u reached. Terminating search.\n",
|
|
dwBoundingEventRecord);
|
|
ExtWarn("Use !evlog option -d to view defaults.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
BOOL fDisplayRecord = TRUE;
|
|
dwTotalRecords++;
|
|
|
|
// Ignore this record if it does not match criteria
|
|
if (fMatchSource &&
|
|
_stricmp(szSource,
|
|
(CHAR *)(BYTE *)pevlr + sizeof(EVENTLOGRECORD)))
|
|
{
|
|
fDisplayRecord = FALSE;
|
|
}
|
|
|
|
if (fMatchEventID && (dwEventID != pevlr->EventID))
|
|
{
|
|
fDisplayRecord = FALSE;
|
|
}
|
|
|
|
if (fMatchEventCategory &&
|
|
(wEventCategory != pevlr->EventCategory))
|
|
{
|
|
fDisplayRecord = FALSE;
|
|
}
|
|
|
|
if (fMatchEventType && (wEventType != pevlr->EventType))
|
|
{
|
|
fDisplayRecord = FALSE;
|
|
}
|
|
|
|
if (fDisplayRecord)
|
|
{
|
|
dwNumRecords++;
|
|
|
|
if (dwNumRecords > dwMaxRecords)
|
|
{
|
|
ExtWarn("WARNING: Max record count (%u) exceeded, "
|
|
"increase record count to view more\n",
|
|
dwMaxRecords);
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut("-------------- %02u --------------\n", dwNumRecords);
|
|
PrintEvLogEvent(szEventLog, pevlr);
|
|
}
|
|
|
|
if (dwTotalRecords % 20)
|
|
{
|
|
ExtVerb("dwTotalRecords = %u, "
|
|
"Current Record # = %u, "
|
|
"dwRecordOffset = %u\n",
|
|
dwTotalRecords,
|
|
pevlr->RecordNumber,
|
|
dwRecordOffset);
|
|
}
|
|
|
|
if (CheckControlC())
|
|
{
|
|
ExtOut("Terminated w/ctrl-C...\n");
|
|
goto Exit;
|
|
}
|
|
|
|
if (dwReadFlags & (EVENTLOG_SEEK_READ |
|
|
EVENTLOG_FORWARDS_READ))
|
|
{
|
|
// Start next read at new "forward" seek position
|
|
dwRecordOffset = pevlr->RecordNumber + 1;
|
|
}
|
|
else if (dwReadFlags &
|
|
(EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ))
|
|
{
|
|
// Start next read at new "backwards" seek position
|
|
dwRecordOffset = pevlr->RecordNumber - 1;
|
|
}
|
|
|
|
dwBytesRead -= pevlr->Length;
|
|
pevlr = (EVENTLOGRECORD *) ((BYTE *)pevlr + pevlr->Length);
|
|
|
|
} while (dwBytesRead > 0);
|
|
} while (TRUE);
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if ((0 == dwNumRecords) && (S_OK == Status))
|
|
{
|
|
ExtOut("No matching event records found.\n");
|
|
}
|
|
if (hEventLog)
|
|
{
|
|
CloseEventLog(hEventLog);
|
|
}
|
|
|
|
free(pbBuffer);
|
|
pbBuffer = NULL;
|
|
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EvLogReport ( PDEBUG_CLIENT Client, PCSTR args )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles parsing and execution for the report command to the
|
|
!evlog extension. It is used to log events to the Application event log
|
|
ONLY.
|
|
|
|
To make the events view nicely in the event viewer, there must be a
|
|
registered event source. The !evlog addsource command can be used to
|
|
register the uext.dll as a an event message file for any event source.
|
|
Since this feature is implemented under the guise of a debugger extension,
|
|
the default source name is "DebuggerExtensions".
|
|
|
|
There are a few reasons why this extension command might be handy:
|
|
1) A developer can log a note to recall later
|
|
2) A large lab with many machines running cdb/ntsd/windbg user mode
|
|
debuggers can set a command to log an event when the machine breaks
|
|
into the debugger. The event might then be monitored by a central
|
|
console which might, in turn, send an e-mail notification or page
|
|
someone immediately regarding the break.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEventSource = NULL;
|
|
LPCSTR cszMessage = NULL;
|
|
WORD wEventCategory = 0; // default (normally categories start at 1)
|
|
WORD wEventType = 0; // default
|
|
DWORD dwEventID = 0; // default (normally event id is non-zero)
|
|
CHAR szParamValue[MAX_PATH];
|
|
CHAR szSource[MAX_PATH+1];
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
" !evlog report [-s <source>] "
|
|
"[-e <id>] [-c <category>] [-t <type>] <message>\n\n"
|
|
"Logs an event to the application event log.\n\n"
|
|
"Use !evlog addsource to configure an event source in the registry."
|
|
"Once\n"
|
|
"configured, the following Event IDs will be recognized by the event "
|
|
"viewer:\n\n"
|
|
" 0 Displays raw message in event description field\n"
|
|
" 1000 Prefixes description with \"Information:\"\n"
|
|
" 2000 Prefixes description with \"Success:\"\n"
|
|
" 3000 Prefixes description with \"Warning:\"\n"
|
|
" 4000 Prefixes description with \"Error:\"\n\n"
|
|
"Optional parameters:\n"
|
|
"<source> : (default: DebuggerExtensions)\n"
|
|
"<id> : 0, 1000, 2000, 3000, 4000, etc... (default: 0)\n"
|
|
"<category> : None (default: 0), Devices (1), Disk (2), Printers "
|
|
"(3),\n"
|
|
" Services (4), Shell (5), System_Event (6), Network "
|
|
"(7)\n"
|
|
"<type> : Success (default: 0), Error (1), Warning (2), "
|
|
"Information (4),\n"
|
|
" Audit_Success (8), or Audit_Failure (16)\n"
|
|
"<message> : Text message to add to description\n";
|
|
const CHAR cszDefaultSource[] = "DebuggerExtensions";
|
|
|
|
INIT_API();
|
|
|
|
// Initialize defaults
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
CopyString(szSource, cszDefaultSource, sizeof(szSource));
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse args
|
|
while (*args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
|
|
// Check for optional argument options to appear first
|
|
if (('-' == *args) || ('/' == *args))
|
|
{
|
|
CHAR ch = *(++args); // Get next char + advance arg ptr
|
|
++args; // Skip one more char
|
|
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
|
|
SKIP_WSPACE(args); // Advance to start of param value
|
|
|
|
// Parameter value is delimited by next space in string
|
|
szEndOfValue = strchr(args, ' ');
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += strlen(args);
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case 's': // Source (string)
|
|
ExtVerb("Setting Source...\n");
|
|
CopyString(szSource, szParamValue, sizeof(szSource));
|
|
break;
|
|
case 'e': // Event ID (number or string)
|
|
ExtVerb("Setting Event ID...\n");
|
|
if (!_strnicmp(szParamValue, "0x", 2))
|
|
{
|
|
dwEventID = strtoul(szParamValue, NULL, 16);
|
|
}
|
|
else
|
|
{
|
|
dwEventID = strtoul(szParamValue, NULL, 10);
|
|
}
|
|
break;
|
|
case 'c': // Event Category (number or string)
|
|
ExtVerb("Setting Category...\n");
|
|
if (!_strnicmp(szParamValue, "None", 4))
|
|
{
|
|
wEventCategory = 0;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Devices", 7))
|
|
{
|
|
wEventCategory = 1;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Disk", 4))
|
|
{
|
|
wEventCategory = 2;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Printers", 8))
|
|
{
|
|
wEventCategory = 3;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Services", 8))
|
|
{
|
|
wEventCategory = 4;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Shell", 5))
|
|
{
|
|
wEventCategory = 5;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "System_Event", 12))
|
|
{
|
|
wEventCategory = 6;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Network", 7))
|
|
{
|
|
wEventCategory = 7;
|
|
}
|
|
else
|
|
{
|
|
wEventCategory = (WORD)strtoul(szParamValue,
|
|
NULL, 10);
|
|
}
|
|
break;
|
|
case 't': // Event Type (number or string)
|
|
ExtVerb("Setting Event Type...\n");
|
|
if (!_strnicmp(szParamValue, "Success", 7))
|
|
{
|
|
wEventType = EVENTLOG_SUCCESS;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Error", 5))
|
|
{
|
|
wEventType = EVENTLOG_ERROR_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Warning", 7))
|
|
{
|
|
wEventType = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Information", 11))
|
|
{
|
|
wEventType = EVENTLOG_INFORMATION_TYPE;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Audit_Success", 13))
|
|
{
|
|
wEventType = EVENTLOG_AUDIT_SUCCESS;
|
|
}
|
|
else if (!_strnicmp(szParamValue, "Audit_Failure", 13))
|
|
{
|
|
wEventType = EVENTLOG_AUDIT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
wEventType = (WORD)strtoul(szParamValue, NULL, 10);
|
|
}
|
|
break;
|
|
default:
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid arg '-%c' specified\n", *args);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
break;
|
|
}
|
|
|
|
ZeroMemory(szParamValue, sizeof(szParamValue)); // reset
|
|
}
|
|
else // Everything to end of line is message string
|
|
{
|
|
SKIP_WSPACE(args);
|
|
cszMessage = args;
|
|
args += strlen(args);
|
|
}
|
|
|
|
}
|
|
|
|
// Fix defaults for DebuggerExtensions events when wEventType not set
|
|
if (!strcmp(szSource, cszDefaultSource) && (0 == wEventType))
|
|
{
|
|
if ((EVENT_MSG_GENERIC == dwEventID) ||
|
|
(EVENT_MSG_INFORMATIONAL == dwEventID))
|
|
{
|
|
wEventType = EVENTLOG_INFORMATION_TYPE;
|
|
}
|
|
else if (EVENT_MSG_SUCCESS == dwEventID)
|
|
{
|
|
wEventType = EVENTLOG_SUCCESS;
|
|
}
|
|
else if (EVENT_MSG_WARNING == dwEventID)
|
|
{
|
|
wEventType = EVENTLOG_WARNING_TYPE;
|
|
}
|
|
else if (EVENT_MSG_ERROR == dwEventID)
|
|
{
|
|
wEventType = EVENTLOG_ERROR_TYPE;
|
|
}
|
|
}
|
|
|
|
// Get a handle to the NT application log
|
|
hEventSource = RegisterEventSource(NULL, szSource);
|
|
if (!hEventSource)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open event log, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
if (!ReportEvent(hEventSource, // event log handle
|
|
wEventType, // event type
|
|
wEventCategory, // category
|
|
dwEventID, // event identifier
|
|
NULL, // no user security identifier
|
|
1, // one substitution string
|
|
0, // no data
|
|
&cszMessage, // pointer to string array
|
|
NULL)) // pointer to data
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to report event, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
// Output format similar to copy to clipboard format in event viewer
|
|
ExtOut("Event Type:\t%s (%u)\n",
|
|
(wEventType <= 16)
|
|
? g_pcszEventType[wEventType]
|
|
: "None",
|
|
wEventType);
|
|
ExtOut("Event Source:\t%s\n", szSource);
|
|
ExtOut("Event Category:\t%s (%u)\n",
|
|
(!strcmp(szSource, cszDefaultSource) && wEventCategory <= 7)
|
|
? g_pcszAppEventCategory[wEventCategory]
|
|
: "",
|
|
wEventCategory);
|
|
ExtOut("Event ID:\t%u\n", dwEventID);
|
|
ExtOut("Description:\n%s\n", cszMessage);
|
|
ExtVerb("Event successfully written.\n");
|
|
|
|
Exit:
|
|
if (hEventSource)
|
|
{
|
|
DeregisterEventSource(hEventSource);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Debugger extension(s) implementation
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
DECLARE_API( evlog )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the function exported through the uext extension interface. It
|
|
is used to delegate the real work to the extension command specified as
|
|
an argument.
|
|
|
|
All event log related commands can be combined as sub-commands under this
|
|
one !evlog command.
|
|
|
|
Arguments:
|
|
|
|
Client - Pointer to IDebugClient passed to !evlog extension
|
|
[not used by this command]
|
|
args - Pointer to command line arguments passed to this command from
|
|
!evlog extension
|
|
|
|
Return Value:
|
|
|
|
E_INVALIDARG if invalid argument syntax detected
|
|
ERROR_BUFFER_OVERFLOW if argument length too long
|
|
GetLastError() converted to HRESULT otherwise
|
|
|
|
--*/
|
|
{
|
|
CHAR *szEndOfValue = NULL; // Ptr to last char in value
|
|
size_t cchValue = 0; // Count of chars in value
|
|
CHAR szParamValue[MAX_PATH];
|
|
const CHAR cszUsage[] = "Usage:\n"
|
|
"The following Event Log commands are available:\n\n"
|
|
"!evlog Display this help message\n"
|
|
"!evlog addsource Adds event source entry to registry\n"
|
|
"!evlog backup Makes backup of event log\n"
|
|
"!evlog clear Clears and creates backup of event log\n"
|
|
"!evlog info Displays summary info for event log\n"
|
|
"!evlog option Sets and clears cached option settings used "
|
|
"during read\n"
|
|
"!evlog read Reads event records from event log\n"
|
|
"!evlog report Writes event records to event log\n\n"
|
|
"Try command with -? or no parameters to see help.\n";
|
|
|
|
INIT_API();
|
|
|
|
if (args)
|
|
{
|
|
SKIP_WSPACE(args);
|
|
}
|
|
|
|
if (!args || !args[0] ||
|
|
!strncmp(args, "-h", 2) ||
|
|
!strncmp(args, "-?", 2))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// whitespace already skipped...
|
|
ZeroMemory(szParamValue, sizeof(szParamValue));
|
|
|
|
// Parameter value (command) is delimited by next space in string
|
|
szEndOfValue = strchr(args, ' ');
|
|
if (NULL == szEndOfValue)
|
|
{
|
|
// copy to end of line
|
|
CopyString(szParamValue, args, sizeof(szParamValue));
|
|
args += strlen(args);
|
|
}
|
|
else
|
|
{
|
|
cchValue = szEndOfValue - args;
|
|
if (cchValue < sizeof(szParamValue))
|
|
{
|
|
// copy next N chars
|
|
CopyString(szParamValue, args, cchValue+1);
|
|
args += cchValue;
|
|
}
|
|
else
|
|
{
|
|
Status = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
ExtErr("ERROR: Argument string too long. Aborting.\n");
|
|
goto Exit;
|
|
}
|
|
|
|
++args; // skip space
|
|
}
|
|
|
|
if (!_stricmp(szParamValue, "addsource"))
|
|
{
|
|
Status = EvLogAddSource(Client, args);
|
|
}
|
|
else if (!_stricmp(szParamValue, "backup"))
|
|
{
|
|
Status = EvLogBackup(Client, args);
|
|
}
|
|
else if (!_stricmp(szParamValue, "option"))
|
|
{
|
|
Status = EvLogOption(Client, args);
|
|
}
|
|
else if (!_stricmp(szParamValue, "clear"))
|
|
{
|
|
Status = EvLogClear(Client, args);
|
|
}
|
|
else if (!_stricmp(szParamValue, "info"))
|
|
{
|
|
Status = EvLogInfo(Client, args);
|
|
}
|
|
else if (!_stricmp(szParamValue, "read"))
|
|
{
|
|
Status = EvLogRead(Client, args);
|
|
}
|
|
else if (!_stricmp(szParamValue, "report"))
|
|
{
|
|
Status = EvLogReport(Client, args);
|
|
}
|
|
else
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Invalid command '%s' specified\n", szParamValue);
|
|
ExtErr(cszUsage);
|
|
goto Exit;
|
|
}
|
|
|
|
// do not set Status to S_OK here, it is returned above
|
|
|
|
Exit:
|
|
EXIT_API();
|
|
return Status;
|
|
}
|