Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find out EventProperty name?

My C# application subscribes to Windows Event Log messages:

var subscriptionQuery = new EventLogQuery(Settings.Default.LogPath, PathType.LogName, Settings.Default.LogQuery);
_watcher = new EventLogWatcher(subscriptionQuery);
_watcher.EventRecordWritten += EventLogEventRead;

When message occurs EventLogEventRead handler reseives a System.Diagnostics.Eventing.Reader.EventLogRecord object, containing event data. This information includes a collection of EventProperty objects. The problem is: EventProperty only defines a value, not the name of the property. However, when I open the same event in Windows Event Log Viewer, it shows properties with names. Now the question is: How do I get event propery names?

like image 738
m0n5ter Avatar asked Jul 18 '15 05:07

m0n5ter


2 Answers

I finally figured it out! 🎉 You are intended to use EventLogPropertySelector to specify XPath expressions to select the properties you want. Then, instead of calling EventRecord.Properties, call EventLogRecord.GetPropertyValues(EventLogPropertySelector). The returned values are correlated by index with the XPath expressions that you specified when you created the EventLogPropertySelector.

For a C++ example which shows you what the XPath expressions should look like, see https://docs.microsoft.com/windows/desktop/WES/rendering-events:

...

LPWSTR ppValues[] = {L"Event/System/Provider/@Name", L"Event/System/Channel"};
DWORD count = sizeof(ppValues)/sizeof(LPWSTR);

// Identify the components of the event that you want to render. In this case,
// render the provider's name and channel from the system section of the event.
// To get user data from the event, you can specify an expression such as
// L"Event/EventData/Data[@Name=\"<data name goes here>\"]".
hContext = EvtCreateRenderContext(count, (LPCWSTR*)ppValues, EvtRenderContextValues);

...

Here’s an example in C# which demonstrates pulling only the needed values for each event type:

var query = new EventLogQuery(
    "Security",
    PathType.LogName,
    "*[System[EventID=4624 or EventID=4634]]");

using (var loginEventPropertySelector = new EventLogPropertySelector(new[]
{
    // (The XPath expression evaluates to null if no Data element exists with the specified name.)
    "Event/EventData/Data[@Name='TargetUserSid']",
    "Event/EventData/Data[@Name='TargetLogonId']",
    "Event/EventData/Data[@Name='LogonType']",
    "Event/EventData/Data[@Name='ElevatedToken']",
    "Event/EventData/Data[@Name='WorkstationName']",
    "Event/EventData/Data[@Name='ProcessName']",
    "Event/EventData/Data[@Name='IpAddress']",
    "Event/EventData/Data[@Name='IpPort']"
}))
using (var logoffEventPropertySelector = new EventLogPropertySelector(new[]
{
    "Event/EventData/Data[@Name='TargetUserSid']",
    "Event/EventData/Data[@Name='TargetLogonId']"
}))
using (var reader = new EventLogReader(query))
{
    // In C# 8: while (reader.ReadEvent() is { } ev)
    while (reader.ReadEvent() is var ev && ev != null)
    {
        using (ev)
        {
            switch (ev.Id)
            {
                case 4624:
                    var loginPropertyValues = ((EventLogRecord)ev).GetPropertyValues(loginEventPropertySelector);
                    var targetUserSid = (SecurityIdentifier)loginPropertyValues[0];
                    // ...
                    break;

                case 4634:
                    var logoffPropertyValues = ((EventLogRecord)ev).GetPropertyValues(logoffEventPropertySelector);
                    var targetUserSid = (SecurityIdentifier)logoffPropertyValues[0];
                    // ...
                    break;
            }
        }
    }
}

The thing that made this nearly undiscoverable (besides the lack of .NET documentation) was that EventLogReader.ReadEvent returns EventRecord which you then have to cast to EventLogRecord. This is what clued me in when looking at the .NET Framework source and seeing that EventRecord.Properties ultimately p/invokes EvtRender. https://docs.microsoft.com/windows/desktop/api/winevt/nf-winevt-evtrender#remarks:

There is a one-to-one relationship between the array of XPath expressions that you specified when you called the EvtCreateRenderContext function and the array the values returned in the buffer.

like image 80
jnm2 Avatar answered Sep 19 '22 05:09

jnm2


There are two ways you could go about this (that I know of), but both involve parsing xml.

EventRecord has the function ToXml which includes an UserData section, containing all the values from that block.

The other option is a bit more complicated: Create an ProviderMetadata, find the EventMetadata that describes the current EventRecord, and parse the Template member. This Template member explains how to interpret the values (even including types), but it is in XML format.

var meta = new ProviderMetadata(record.ProviderName).Events.Where(evt => evt.Id == eventRecord.Id).FirstOrDefault();

like image 40
Mark Jansen Avatar answered Sep 19 '22 05:09

Mark Jansen