Condividi tramite


The Good, the bad, and the ugly: Part 2 - Legacy API

In the previous post I described the current, manifested event provider system that was introduced in Vista.

 

The legacy method of logging Windows Events goes back to Windows 2000. An "Event Source" is what most people refer to when they are describing "what application generated this event?" I'll use "Event Source" when talking about the legacy system and Event Provider when referring to the modern, manifested system for logging Windows Events.

 

In those days, instead of writing a manifest and then registering it with the Eventlog Sevice, you register a machine-local, unique event source name (e.g., "Service Control Manager") for a particular event log ("System") and as part of that registration include a path to a file(s) to render the events (EXE or DLL, in this example: "C:\Windows\Services.exe") - this file is called the "EventMessageFile." An Event Source can also optionally include a ParameterMessageFile and a CategoryMessageFile, both described below.

All of this event source registration is stored in the machine's registry, under:

  • HKLM\System\CurrentControlSet\Services\EventLog

 

There is a folder for each event log name (e.g., "System"), and underneath that a is sub-folder for each event source name (e.g., "Service Control Manager"). Within the event source name sub-folder you'll find the path to the EventMessageFile ("C:\Windows\Services.exe") That message file contains the event descriptions for rendering events.

An event source can also define specific strings, these strings are usually localized which is why they aren't part of the message description. Replacement strings appear as numbers preceded by two percent-signs like this: %%142 or %%827. When rendering the message in the event viewer this Event Viewer these values in the event payload data are replaced with the localized text strings.

Oh, to make things a little more complex, Replacement Strings can be part of the EventMessageFile or in a separate file referenced by the ParameterMessageFile registry value (if it is defined) for the Event Source. This allows flexibility for the developer but for you the person interested in reading events with machines, makes things a little more difficult since you need to pre-gather values ahead of time for reference later.

CategoryMessageFile defines the localized names of the defined Category values. Categories are for grouping like events together - this was implemented in the days before XPath filtering of events was possible so think of it as a form of filtering for the legacy event log. The Category model has been replaced in the modern system by Task and OpCode (aka Operation Codes - a more granular task ) so you can continue this style of grouping of events (Backwards Compatibility!) So you might have a Task for "DataStore Operations" with OpCodes for "Connect", "Query", "Update", "Disconnect" to enable filtering on specific operations within the larger task.

 

Taking just a moment, I just want to point out the model shift between the legacy and modern event logging API:

  • Legacy: Start with an event log, create a source in that eventlog and log all events there.
  • Modern: Start with the event provider, define events, and for each event specify event log the event will be logged.

The modern system gives much more flexibility to developers and since the manifests are centrally stored, you (the event consumer) also have one single place to search for "Where do I look for this particular event?" Win-win.

 

Now back to the task at hand:

Since events logged using Event Source are using a legacy API, they are limited to Event Logs that the legacy API can access, sometimes called "Classic event logs" - this includes the usual suspects: System, Application.  It's not quite clear-cut that "if you look in the System event log, you'll only see legacy events" - since the modern API is backwards compatible and can log events there too.

There also isn't the concept of a specific Event Schema for an Event Source + Event ID combination, so that when a vendor ships new code, it's left up to you to figure out that the event schema has changed (if it has) and what changed in it. (You'll see in the older-style events that there is no System/Version element present.) This is likely where many event processing vendors got the idea "I need to know the Windows Version to know how to parse the events coming from it." It's not entirely without merit but for modern, manifested events, having to know the Windows version isn't a requirement since you can just check the Provider + Event ID + Version to know the event payload schema.

Since the Eventlog Service has no intrinsic information on the schema of an event logged using the legacy Event Source  system, the event payload just logs a list of <Data> elements for an event's payload. Compare that to the manifested event where an event payload property has a Name attribute.

To show the difference between the Manifested and Legacy event payload, here's how the payload data for Windows Defender would be logged:

 

Legacy Modern
<EventData><Data>%%827</Data><Data>4.10.14393.0</Data><Data>1.225.2493.0</Data><Data>1.225.2471.0</Data><Data/><Data/><Data/><Data>NT AUTHORITY</Data><Data>SYSTEM</Data><Data>S-1-5-18</Data><Data>2</Data><Data>%%801</Data><Data>2</Data><Data>%%804</Data><Data>1.1.12902.0</Data><Data>1.1.12902.0</Data></EventData> <EventData><Data Name="Product Name">%%827</Data><Data Name="Product Version">4.10.14393.0</Data><Data Name="Current Signature Version">1.225.2493.0</Data><Data Name="Previous Signature Version">1.225.2471.0</Data><Data Name="Unused" /><Data Name="Unused2" /><Data Name="Unused3" /><Data Name="Domain">NT AUTHORITY</Data><Data Name="User">SYSTEM</Data><Data Name="SID">S-1-5-18</Data><Data Name="Signature Type Index">2</Data><Data Name="Signature Type">%%801</Data><Data Name="Update Type Index">2</Data><Data Name="Update Type">%%804</Data><Data Name="Current Engine Version">1.1.12902.0</Data><Data Name="Previous Engine Version">1.1.12902.0</Data></EventData>

Immediately you can see in the Modern event that some properties are unused and you can safely ignore them.

The modern, manifested event is far easier to comprehend and track if there are changes made in the event schema (which would also show up as a new <Version> number as well.)

 

So, what does this mean for you, who want to read the events and understand their meaning? Put simply, the above means that if you want to understand the data in events logged using the legacy you (unfortunately) need to retrieve the messages, category, and parameter values from the event source referenced binaries if you want to either fully render them, know what each payload property means, or know if an event payload schema has changed.