Message read by OnEntryWritten are coming with parameters not set - c#

I'm using the OnEntryWritten event in order to get events from the event log when they are fired.
The problem I started to see today is that some messages come with parameters unset.
For example:
The Windows Filtering Platform has permitted a bind to a local port. Application Information: Process ID:9852 Application Name:\device\harddiskvolume7\program files (x86)\google\chrome\application\chrome.exe Network Information: Source Address::: Source Port:51714 Protocol:17 Filter Information: Filter Run-Time ID:0 Layer Name:%%14608 Layer Run-Time ID:38
You can see the %%14608 parameter. This comes with a value if I see the same log in the Event Viewer.
I'm running a windows service as LocalSystem, so I don't know if this is a permission issue or this technology is not useful at all.
I have tried the rest offered on C# and they also don't meet my requirements.
UPDATE: this is the code I'm using to read the events.
1) First I subscribe to the corresponding Event log:
private void Subscribe()
{
_eventLog.EnableRaisingEvents = true;
var callbackFunction = new EntryWrittenEventHandler(OnEntryWritten);
_eventLog.EntryWritten += callbackFunction;
// Save a reference for callbackFunction
_eventHandler = callbackFunction;
}
2) Then on the callback method, I read data from the message:
public void OnEntryWritten(Object source, EntryWrittenEventArgs entryArgs)
{
// When overwrite policy is enabled, this will trigger for all elements when it starts writing new ones
try
{
var entry = entryArgs.Entry;
var timeWritten = entry.TimeWritten;
// This comes with %% values depending on the log
string message = entry.Message;
}
catch(Exception ex)
{
...
}
}
3) The event log variable is simply initialized as:
var eventLog = EventLog.GetEventLogs().FirstOrDefault(el => el.Log.Equals(logName, StringComparison.OrdinalIgnoreCase));
I need some help on this, honestly I don't know what else to try.
UPDATE
I'm adding some images here so everybody can understand the situation a little bit better. To be honest, it looks like there's no solution but to implement a dictionary and replace manually the required values, which appear to be always constants.
This is what I see on the Event Viewer for a given Event ID:
This is what I see on my program when I read that entry:
You can clearly see that the following values:
"Machine key." (Key type)
"Read persisted key from file." (Operation)
Are coming unmapped in the ReplacementStrings and the Message properties as: %%2499 and %%2458
This is the message value I get on the program:
"Key file operation.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-1-5-18\r\n\tAccount Name:\t\tMyAccount$\r\n\tAccount Domain:\t\tWORKGROUP\r\n\tLogon ID:\t\t0x3e7\r\n\r\nProcess Information:\r\n\tProcess ID:\t\t6644\r\n\tProcess Creation Time:\t2019-04-03T12:17:24.587994400Z\r\n\r\nCryptographic Parameters:\r\n\tProvider Name:\tMicrosoft Software Key Storage Provider\r\n\tAlgorithm Name:\tUNKNOWN\r\n\tKey Name:\t816339d2-c476-4f1e-bc40-954f0aa0f851\r\n\tKey Type:\t%%2499\r\n\r\nKey File Operation Information:\r\n\tFile Path:\tC:\ProgramData\Microsoft\Crypto\Keys\6d55a45fd69327293e9ed3e7930f4565_5663a8bb-2d1d-4c0d-90c1-624beddabe9c\r\n\tOperation:\t%%2458\r\n\tReturn Code:\t0x0"
What can be done here? There also nothing in entry.Data that might help me out to obtain both values.

No, I believe you're mistaken, sorry that this answer is too late. I found a similar event that was raised by chrome and evaluated if there's anything missed by the event handler. There wasn't anything missed. The message I got in my console output exactly matched what I saw in my Event Viewer.
A better solution would be to
Use entry.Data to get the complete data instead of entry.Message
entry.Data will return a byte[] which you can convert to a string. Here's the link to all properties that an entry will have.

Related

API to resolve %% variables

I am working with the C# Event Log API in Windows (essentially everything in System.Diagnostics.Eventing.Reader).
I have an EventMetadata object and pull its Description property to retrieve the message template for an event.
Generally, these templates look similar to the following:
Network interface reports message %1
These variables are easily replaceable with actual data whenever I receive an event.
(EventLogRecord.Properties match up to the placeholders)
Here is where my problem comes in. The EventLogRecord.Properties sometimes contain different kinds of placeholders. These always begin in %% and I cannot find a way of resolving them.
As an example:
// This method is triggered when a new event comes in
async public static void ListenerEvent(object s, EventRecordWrittenEventArgs args) {
var evt = (EventLogRecord)args.EventRecord;
// This method retrieves the template from a ProviderMetadata object
// And replaces all %n with {n}
// So that we can string.Format on it
var tmp = TemplateCache.TemplateFor(evt);
// Need this since the indices start with 1, not 0
var props = new List<object> {string.Empty};
props.AddRange(evt.Properties.Select(prop => prop.Value));
// Now the message should be human-readable
var msg = string.Format(tmp, props);
}
Using the above example template, the Properties might be ["%%16411"] and now I end up with the following message
Network interface reports message %%16411
I figure my question now is, how do I replace this %%16411?
I have looked into ProviderMetadata and the rest of its properties but none seem to match up.
Any help figuring out how to resolve these placeholders (or even what they are/where they come from) is appreciated.
An event that shows this behaviour is 5152, as found here: http://community.spiceworks.com/windows_event/show/452-microsoft-windows-security-auditing-5152
Thank you.

Event Creation is not working

I have source "Source401" used for log "Log401". I need to use this source for "Log402" log and delete the log "Log401". (If we can rename “Log401” as “Log402” that is also fine. But all this need to be done programmatically)
With the code below, I am getting the following exception. What is the best way to achieve it?
Source Source401 already exists on the local computer.
Note: When I delete the old log, it is working fine. But the events are not getting created.
UPDATE
From MSDN
The operating system stores event logs as files. When you use EventLogInstaller or CreateEventSource to create a new event log, the associated file is stored in the %SystemRoot%\System32\Config directory on the specified computer. The file name is set by appending the first 8 characters of the Log property with the ".evt" file name extension.
The source must be unique on the local computer; a new source name cannot match an existing source name or an existing event log name. Each source can write to only one event log at a time; however, your application can use multiple sources to write to multiple event logs.
CODE
string source = "Source401";
string logName = "Log402";
string oldLogName = "Log401";
string eventName = "Sample Event";
string machineName = ".";
if (!EventLog.Exists(logName, machineName))
{
////Delete old log
//if (EventLog.Exists(oldLogName, machineName))
//{
// EventLog.Delete(oldLogName, machineName);
//}
//Create Source for the Log
EventLog.CreateEventSource(source, logName, machineName);
//Create Event
EventLog eventLog = new EventLog(logName, machineName, source);
eventLog.WriteEntry(eventName);
try
{
eventLog.WriteEntry(eventName, EventLogEntryType.Warning, 234, (short)3);
}
catch (Exception exception)
{
int x = 0;
}
The exception is telling you exactly what the problem is. The event source named "Source401" already exists. You're deleting the old event log, "Log401", but you're not deleting the event source.
As the documentation says:
The operating system stores event logs as files. When you use EventLogInstaller or CreateEventSource to create a new event log, the associated file is stored in the %SystemRoot%\System32\Config directory on the specified computer. The file name is set by appending the first 8 characters of the Log property with the ".evt" file name extension.
The source must be unique on the local computer; a new source name cannot match an existing source name or an existing event log name. Each source can write to only one event log at a time;
Also, this little nugget:
If a source has already been mapped to a log and you remap it to a new log, you must restart the computer for the changes to take effect.
In addition, you might want to consider this, also from the documentation:
Create the new event source during the installation of your application. This allows time for the operating system to refresh its list of registered event sources and their configuration. If the operating system has not refreshed its list of event sources, and you attempt to write an event with the new source, the write operation will fail
Finally, the CreateEventSource method you're calling is marked obsolete, and has been since .NET 2.0. There's usually a good reason for methods to be marked obsolete. You should be calling CreateEventSource(EventSourceCreationData).
I think you need to re-think the way you're using event logs. Your application shouldn't be creating and deleting logs that way. It's not how they're intended to be used.

Visual Studio Custom Language Service

I am attempting to implement a Language Service in a VSPackage using the MPF, and it's not working quite as I understand it should.
I have several implementations already, such as ParseSource parsing the input file with a ParseRequest. However, when it finds an error, it adds it with AuthoringSink.AddError. The documentation for this implies it adds it to the Error List for me; it doesn't.
I also have a simple MySource class, a subclass of Source. I return this new class with an overridden LanguageService.CreateSource method. The documentation for OnCommand says it's fired 'when a command is entered'. However, it's not.
There's obviously some intermediate step which I haven't done correctly. I've already rambled enough, so I'll be glad to give any additional details by request.
Any clarification is much appreciated.
For the AuthoringSink error list question, I use this behavior in my Language Service. In ParseSource, the ParseRequest class has an AuthoringSink. You can also create a new ErrorListProvider if you want to work outside of the parser's behavior. Here is some example code:
error_list = new ErrorListProvider(this.Site);
error_list.ProviderName = "MyLanguageService Errors";
error_list.ProviderGuid = new Guid(this.errorlistGUIDstring.);
}
ErrorTask task = new ErrorTask();
task.Document = filename;
task.CanDelete = true;
task.Category = TaskCategory.CodeSense;
task.Column = column;
task.Line = line;
task.Text = message;
task.ErrorCategory = TaskErrorCategory.Error;
task.Navigate += NavigateToParseError;
error_list.Tasks.Add(task);
I hope this was helpful.
OnCommand should be firing every time there is a command, in your MySource class you can do something like this (pulled from working code):
public override void OnCommand(IVsTextView textView, VsCommands2K command, char ch)
{
if (textView == null || this.LanguageService == null
|| !this.LanguageService.Preferences.EnableCodeSense)
return;
if (command == Microsoft.VisualStudio.VSConstants.VSStd2KCmdID.TYPECHAR)
{
if (char.IsLetterOrDigit(ch))
{
//do something cool
}
}
base.OnCommand(textView, command, ch);
}
If that doesn't work double check that CodeSense = true in your ProvideLanguageService attribute when you setup your LanguageService package. A whole lot of what is cool to do in the LanguageService requires these attributes to be correctly turned on. Some even give cool behaviors for free!
Another thing to be careful of is that some behaviors like colorizer don't function correctly in the hive in my experience. I don't think these were ones that gave me trouble, but I implemented these a couple of years ago so I'm mostly just looking back at old code.
AuthoringSink.AddError only adds errors to the error list if ParseRequest.Reason is ParseReason.Check. When your ParseSource function attempts to add errors while parsing for any other ParseReason, nothing will happen.
It's possible that your language service is never calling ParseSource with this ParseReason. As far as I know, the only way to get a ParseReason of Check (outside of manually calling BeginParse or ParseSource yourself) is to proffer your service with an idle timer.

Reading the Windows Event Log with C# (Source != ProviderName != SourceName)

I am using C# to read the Windows Event Log and I want to select/filter entries from it. The problem is that the information displayed in the Event Viewer is not always matching the data I get from c#.
for example:
EventViewer "Source": "User Profile Service"
Using the EventLogEntry class: Property "Source": "Microsoft-Windows-User Profiles Service"
Using the EventLogReader class: Property "ProviderName": "Microsoft-Windows-User Profiles Service"
Using WMI: "SourceName": "Microsoft-Windows-User Profiles Service"
I need to be able to read the exact information displayed in the EventViewer, where can I get this information from?
Reading the EventLog message...
When reading the EventLog message using the EventLogEntry class I occasionally get the following string:
The description for Event ID "xxx" in Source "xxx" cannot be found
Again, this does not match the message displayed in the EventViewer... I have tried using the EventLogReader.FormatDescription() method and it gives me the right (the same as the EventViewer) message, BUT for some entries it simply returns null, while the EventLogEntry.Message contains the proper text.
What is the correct way to retrieve the message of the event to get the same message as the one displayed in the EventViewer?
var eventLog = new EventLog("logName", "machine", "source");
foreach(var entry in eventLog.Entries)
{
}
That is a fairly basic swag at interacting with the log. If you need deeper filtering that source, you can write a LINQ query on the Entries. As shown here.
As for the error, one common reason is not having the proper access to the events and/or registry on the box in question. Since you can see data in question in EventViewer, I am suspecting a permissions error is a good possibility.
It appears that the "Source" string shown in the "Source" column in the Event Viewer is abbreviated. Also it seems that when you try to create an EventLog in C# only the logtype matters e.g. "Application", "System" etc. Once you create an EventLog it will contain all the entries for that logtype regardless of what you specified a source.
In order to get an event based on "Source" you want to iterate over the entries and filter only the entries for that "Source". Just keep in mind that the actual source name is not the same as what you see in the Event Viewer. For example for Source "Winlogon" the actual source name would be: "Microsoft-Windows-Winlogon" and so on.

Checking Event Log writing Permissions without writing an entry

I need to check if a user has write permissions for the event log. My solution right now is to write a test message in the log and delete it afterwards (so that the log does not get messed up, as the check for permissions is called often (every 3-5 Mins.) by some 'Healthcheck'-service:
const string log = "MyApplicationLog";
const string source = "PermissionCheck";
EventLog evLog;
try
{
if (!EventLog.SourceExists(source))
{
EventLog.CreateEventSource(source, log);
}
evLog = new EventLog();
evLog.Source = source;
evLog.WriteEntry("PermissionCheck Test Message");
return true;
}
finally
{
//remove the check messages:
if (EventLog.Exists(log))
{
EventLog.Delete(log);
}
}
Is there any possibility to check the permissions without actually writing a log entry?
Thank you in advance,
ElKunzo
Yes, AFAIK, using CAS. Decorate the required member/s with the EventLogPermission attribute, from there you can control whether you must have access, only desired and so forth.
This may well entail a little further adventure in CAS itself, however, if you're unfamiliar.
MSDN Link.

Categories

Resources