Wielki brat patrzy: EventLogWatcher

Jakiś czas temu zostałem poproszony o pomoc w rozwiązaniu jednego problemu, a z racji iż temat może się wydawać ciekawy postanowiłem to opisać w dzisiejszym poście. Problem skupiał się na przechwytywaniu z dziennika zdarzeń konkretnych eventów, tak by w momencie ich wystąpienia móc wykonać odpowiednie zadania.

Okazuje się, iż problem ten można rozwiązać na kilka sposobów. Najprostsze podejście do sprawy to w harmonogramie zadań skonfigurować obiekt wyzwalany wystąpieniem konkretnego zdarzenia w logach dziennika zdarzeń. Natomiast jako akcję podpiąć np. skrypt PowerShellowy wykonujący interesujący nas kod. Jednakże do problemu można podejść też całkowicie od strony PowerShella, a mianowicie w wolnym tłumaczeniu stworzyć „obserwator dziennika zdarzeń”, czyli EventLogWatcher.

Czym tak naprawdę jest EventLogWatcher? Jest to obiekt klasy .NETa pozwalający na subskrypcję zapisanych zdarzeń oraz późniejszych ich wystąpień. Dodatkowo podczas obsługi tych zdarzeń możliwe jest wykonanie dowolnego kodu skojarzonego z tymi zdarzeniami, jak np. zapis do pliku/bazy danych czy powiadomienie administratora mailem.

Całe rozwiązanie opiera się o pracę dwóch obiektów: EventLogQuery, gdzie konfigurujemy jakie zdarzenie ma być przechwytywane oraz EventLogWatcher czyli nasz główny obiekt do nasłuchiwania.

Zanim jednak przejdziemy do przechwytywania zdarzeń konieczne jest skonfigurowanie kilku parametrów, takich jak nazwa logu czy filtr wyszukiwania odpowiednich zdarzeń w formacie XPath. Dodatkowo definiujemy również powiedzmy rodzaj logu, czyli czy ma być podana nazwa logu czy może jednak ma być podana ścieżka do pliku z logami.

$LogName = "Security"
#Wyłap zdarzenie o ID 4728 (dodanie użytkownika do grupy zabezpieczeń), ale nie starcze niż tydzień
$Query = "*[System[(EventID=4728) and TimeCreated[timediff(@SystemTime) <= 604800000]]]"
$PathType = [System.Diagnostics.Eventing.Reader.PathType]::LogName

EventLogWatcher może przechwytywać również stare zdarzenia, dlatego jeśli nie chcemy pobierać ich od początku z każdym uruchomieniem skryptu, musimy zapisywać np. do pliku czy zmiennej środowiskowej id ostatniego przechwyconego rekordu. Możemy również całkowicie zignorować stare logi, ale o tym później.

$EventBookmark = $null
$CsvLogPath = "c:\log.csv"
if(Test-Path $CsvLogPath)
{
	$CsvLog = Import-Csv $CsvLogPath
	$LastEventRecordID = $CsvLog[$CsvLog.count-1].RecordID
	$EventBookmark = (Get-WinEvent -LogName $LogName -FilterXPath "*[System[(EventRecordID=$LastEventRecordID)]]").Bookmark
}

Mając tak przygotowane dane możemy przystąpić do stworzenia zapytania do dziennika zdarzeń. Następnie używając tego obiektu możemy przystąpić do stworzenia obiektu obserwatora. Jako parametry podajemy właśnie obiekt zapytania, obiekt zawierający znacznik od którego zdarzenia w dzienniku mamy kontynuować odczyt, czyli nasz $EventBookmark oraz przełącznik czy chcemy pobierać stare logi czy tylko nasłuchiwać na wystąpienie nowych. Wartość tego przełącznika wybieramy w zależności od potrzeb, jednakże jeżeli chcemy przechwytywać wszystkie zdarzenia, nawet jeśli skrypt jest chwilowo wyłączony wtedy lepiej jest użyć wartości $true. Pozwoli nam to na uzupełnienie danych od ostatniego uruchomienia skrypu.

$EventLogQuery = New-Object System.Diagnostics.Eventing.Reader.EventLogQuery $LogName, $PathType, $Query
$EventLogWatcher = New-Object System.Diagnostics.Eventing.Reader.EventLogWatcher $EventLogQuery, $EventBookmark, $true

Kolejnym etapem jest stworzenie skryptu, który będzie wykonywany jako akcja na wystąpienie zdarzenia. Mamy tutaj pełną swobodę co zostanie wykonane, np. odczyt niezbędnych danych ze zdarzenia i zapisanie ich do pliku csv, zapis do bazy danych czy wysłanie wiadomości email. Tak naprawdę tutaj ogranicza nas tylko nasza wyobraźnia.

[ScriptBlock]$ObjectEventAction = {
	$EventRecord = $EventArgs.EventRecord
	[XML]$EventRecordXML = $EventRecord.ToXML()

	$Log = New-Object PsObject -Property @{
		TimeCreated = $EventRecord.TimeCreated
		RecordID = $EventRecord.RecordID
		UserName = $EventRecordXML.Event.EventData.Data[6]."#text"
		TargetUser = $EventRecordXML.Event.EventData.Data[0]."#text"
		TargetGroup = $EventRecordXML.Event.EventData.Data[2]."#text"
	}

	$Log |
	Select-Object TimeCreated, RecordID, UserName, TargetUser, TargetGroup |
	Convertto-CSV -Outvariable OutData -NoTypeInformation

	if(Test-Path $CsvLogPath) {	$OutData[1] | Out-File $CsvLogPath -Append }
	else { $OutData | Out-File $CsvLogPath }

	$Log | Out-Host
}

Ostatnią rzeczą jaką musimy dokonać to zarejestrowanie zdarzenia obiektu oraz włączenie nasłuchiwania.

Register-ObjectEvent -InputObject $EventLogWatcher -EventName "EventRecordWritten" -Action $ObjectEventAction
$EventLogWatcher.Enabled = $True

Po tym zabiegu zostaną pobrane wszystkie logi spełniające początkowe zapytanie i dla każdego wystąpienia zostanie wykonany kod bloku skryptu. Dodatkowo w tle będzie pracował nasz obserwator i w momencie wystąpienia kolejnych zdarzeń spełanijących warunki, każdorazowo zostanie wykonany wcześniej zdefiniowany kod.

Źródła:

http://msdn.microsoft.com/en-us/library/system.diagnostics.eventing.reader.eventlogwatcher.aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.eventing.reader.eventlogquery.aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.eventing.reader.pathtype.aspx
http://msdn.microsoft.com/en-us/library/system.diagnostics.eventing.reader.eventlogrecord.aspx
http://pseventlogwatcher.codeplex.com/

Advertisements

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s