استعلام سجلات الأحداث بكفاءة مع PowerShell Get-EventLog

كل مسؤول نظام Windows على الأرجح ملم بسجل الأحداث في Windows. استخدام هذا الأمر في PowerShell يسمح لمسؤولي النظام بتحليل العديد من الأحداث في آن واحد عبر العديد من الكمبيوترات دفعة واحدة. يعفيهم هذا الأمر من النقر حول القارئ للأحداث في محاولة لمعرفة التصفية المناسبة لاستخدامها وتحديد مكان الحدث الحرج بالضبط. ومع ذلك، فإن Get-EventLog لديه عيوبه التي ستراها.

قائمة سجلات الأحداث باستخدام Get-EventLog

أمر Get-EventLog متاح في جميع الإصدارات الحديثة من Windows PowerShell. في استخدامه الأكثر بساطة، يحتاج هذا الأمر إلى سجل حدث للاستعلام عنه ثم يعرض جميع الأحداث في ذلك السجل.

ولكن ماذا لو لم تكن تعرف اسم سجل الحدث في المقام الأول؟ في هذه الحالة، نحتاج إلى معرفة جميع سجلات الأحداث المتاحة على جهاز الكمبيوتر المحلي لدينا. نفعل ذلك باستخدام الأمر Get-EventLog -List.

Get-EventLog -List

يمكنك رؤية أن لدي بعض سجلات الأحداث على نظامي المحلي الآن، ولكن قد تتساءل أين الآخرين؟ هناك عشرات من سجلات الأحداث الأخرى تظهر تحت سجلات التطبيقات والخدمات في قارئ الأحداث. لماذا لا تظهر هنا؟

إذا كنت بحاجة إلى تلك الأحداث، للأسف، فإن Get-EventLog لن يعمل. بدلاً من ذلك، ستحتاج إلى التحقق من Get-WinEvent. يمكن اعتبار أمر Get-EventLog أمرًا تقليديًا في هذه النقطة، لكنه لا يزال أمرًا أستخدمه بشكل متكرر ببساطة لأنه من السهل جدًا الاستخدام.

الاستعلام عن الأحداث باستخدام Get-EventLog

الآن بعد أن نعرف جميع سجلات الأحداث المتاحة، يمكننا الآن قراءة الأحداث ضمن ذلك السجل. ربما أريد أن أرى جميع الأحداث في سجل الحدث التطبيقي. للحصول على تلك الأحداث، أحتاج إلى تحديد المعلمة LogName مع Get-EventLog وسيقوم الأمر بإرجاع جميع الأحداث في ذلك السجل.

Get-EventLog -LogName Application

بشكل افتراضي، سترى فقط ست خصائص في الإخراج:

  • Index
  • Time
  • EntryType
  • Source
  • InstanceId
  • Message

في الواقع، Get-EventLog يعيد 16 منها. السبب في أنك ترى فقط ستة هو قواعد تنسيق PowerShell التي تحدد الإخراج. فيما يلي مثال على الإخراج الفعلي الذي تم العثور عليه عن طريق توجيه Get-EventLog إلى Select-Object وتحديد جميع الخصائص.

Get-EventLog -LogName Application | Select-Object -Property * EventID

التصفية باستخدام Get-EventLog

من المحتمل أننا عند البحث عن الأحداث، لا نحتاج إلى جميع الأحداث. بدلاً من ذلك، نحتاج فقط إلى بعضها. في هذه الحالة، نحتاج إلى تصفية الأحداث المعينة. Get-EventLog لديه بعض الطرق المختلفة للقيام بذلك. يمكن لـ Get-EventLog تصفية استنادًا إلى الطابع الزمني، ونوع الإدخال، ومعرّف الحدث، والرسالة، والمصدر، واسم المستخدم. يتولى ذلك معظم طرق العثور على الأحداث.

لتوضيح عملية التصفية، ربما أقوم بالاستعلام عن الأحداث بشكل دوري، وأريد العثور على أحدث عشرة أحداث. في هذه الحالة، يمكنني استخدام المعامل Newest وتحديد عدد الأحداث التي أود رؤيتها. سيقوم Get-EventLog -LogName Application -Newest 10 بإرجاع آخر عشرة أحداث فقط.

ربما أريد العثور على جميع الأحداث بعد نقطة زمنية معينة. لهذا، لدينا المعامل After. يأخذ المعامل After تاريخ/وقت، لذا إذا كنت أود العثور فقط على الأحداث داخل سجل التطبيق التي حدثت بعد 1/26/19 10:17 صباحًا، يمكنني فعل ذلك باستخدام Get-EventLog -LogName Application -After '1/26/19 10:17'. يمكننا أيضًا أداء نفس العملية ولكن اختيار الأحداث التي حدثت قبل تاريخ معين باستخدام، قد تكون قد تخمنت، المعامل Before.

يحتوي المعامل Get-EventLog على العديد من الطرق المختلفة للتصفية غير المعتمدة على الطابع الزمني. يمكننا أيضًا تصفية الأحداث بناءً على سمات أخرى مثل معرف الحدث (معرف الحالة) والرسالة التي تميل إلى كونها سمات شائعة للبحث عنها. ربما أعرف أنني أبحث عن حدث لديه معرف 916؛ سنمرر 916 إلى المعامل InstanceId.

PS> Get-EventLog -LogName Application -InstanceId 916

يمكننا دمج التصفيات أيضًا. ربما أحصل على الكثير من الأحداث المرتجعة بمعرف 916، لكنني أريد تلك الأحداث التي تحتوي على السلسلة svchost في الرسالة. في هذه الحالة، يمكننا إضافة المعامل Message إلى Get-EventLog وتحديد بطاقة تعريفية مثل svchost.

PS> Get-EventLog -LogName Application -InstanceId 916 -Message '*svchost*'

سكريبت إضافي!

هل تحتاج إلى مثال رائع على استخدام Get-EventLog في سيناريو واقعي للنص؟ إذا كان الأمر كذلك، فأنت محظوظ! فيما يلي حالة استخدام متقدمة لـ \(\text{Get-EventLog}\) يمكنك تنزيلها واستخدامها اليوم!

<#
.ملخص
    يبحث هذا النص البرمجي في كمبيوتر Windows عن جميع سجلات الحدث أو سجلات النص بين وقت بداية ونهاية محدد.
.الوصف
    يستعرض هذا النص البرمجي جميع سجلات الحدث خلال الإطار الزمني المحدد وجميع سجلات النص التي تحتوي على وقت كتابة آخر داخل الإطار الزمني أو تحتوي على سطر يحتوي على إشارة تاريخ/وقت داخل الإطار الزمني. يسجل هذه المعلومات في مجموعة من الملفات وينسخ أي ملف سجل مثير للاهتمام لمزيد من التحليل.
.مثال
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME

    يعثر هذا المثال على جميع إدخالات سجل الحدث بين StartTimestamp و EndTimestamp وأي ملف بامتداد داخل معلمة $FileExtension الذي تم كتابته آخر مرة خلال الإطار الزمني أو يحتوي على سطر يحتوي على إشارة تاريخ/وقت داخل الإطار الزمني.
.المعلمة StartTimestamp
 أقرب تاريخ/وقت ترغب في البحث عن الأحداث ابتداءً.
.المعلمة EndTimestamp
 أحدث تاريخ/وقت ترغب في البحث عن الأحداث ابتداءً.
.المعلمة Computername
 اسم الكمبيوتر البعيد (أو المحلي) الذي ترغب في البحث عليه.
.المعلمة OutputFolderPath
 مسار المجلد الذي سيحتوي على الملفات النصية التي تحتوي على الأحداث.
.المعلمة LogAuditFilPath
 مسار الملف النصي الذي سيوثق ملف السجل ورقم السطر ونوع المطابقة للسجلات التي تمت المطابقة عليها.
.المعلمة EventLogsOnly
 استخدم هذه المعلمة التبديلية إذا كنت ترغب فقط في البحث في سجلات الأحداث.
.المعلمة LogFilesOnly
 استخدم هذه المعلمة إذا كنت ترغب فقط في البحث عن سجلات على نظام الملفات.
.المعلمة ExcludeDirectory
 إذا كنت تبحث في نظام الملفات، فحدد أي مسارات مجلد ترغب في تخطيها.
.المعلمة FileExtension
 حدد واحد أو أكثر من مجموعات مفصولة بفاصلة من الامتدادات التي ترغب في البحث عنها على نظام الملفات. يفترض افتراضيًا أن هذا الأمر ممتد لامتدادات 'log' و 'txt' و 'wer'.
#>
[CmdletBinding(DefaultParameterSetName = 'Neither')]
param (
    [Parameter(Mandatory)]
    [datetime]$StartTimestamp,
    [Parameter(Mandatory)]
    [datetime]$EndTimestamp,
    [Parameter(ValueFromPipeline,
        ValueFromPipelineByPropertyName)]
    [string]$ComputerName = 'localhost',
    [Parameter()]
 [string]$OutputFolderPath = ".\$Computername",
 [Parameter(ParameterSetName = 'LogFiles')]
 [string]$LogAuditFilePath = "$OutputFolderPath\LogActivity.csv",
 [Parameter(ParameterSetName = 'EventLogs')]
 [switch]$EventLogsOnly,
    [Parameter(ParameterSetName = 'LogFiles')]
    [switch]$LogFilesOnly,
    [Parameter(ParameterSetName = 'LogFiles')]
 [string[]]$ExcludeDirectory,
 [Parameter(ParameterSetName = 'LogFiles')]
 [string[]]$FileExtension = @('log', 'txt', 'wer')
)
 
begin {
 if (!$EventLogsOnly.IsPresent) {
 ## إنشاء المجلد المحلي حيث يتم تخزين أي ملفات سجل تطابقت مع المعايير
 $LogsFolderPath = "$OutputFolderPath\logs"
 if (!(Test-Path $LogsFolderPath)) {
 mkdir $LogsFolderPath | Out-Null
 }
 }
 
 function Add-ToLog($FilePath,$LineText,$LineNumber,$MatchType) {
 $Audit = @{
 'FilePath' = $FilePath;
 'LineText' = $LineText
 'LineNumber' = $LineNumber
 'MatchType' = $MatchType
 }
 [pscustomobject]$Audit | Export-Csv -Path $LogAuditFilePath -Append -NoTypeInformation
 }
}
 
process {
 
    ## تشغيل الأمر فقط إذا أراد المستخدم العثور على إدخالات سجل الحدث
    if (!$LogFilesOnly.IsPresent) {
 ## العثور على جميع أسماء سجلات الحدث التي تحتوي على حدث واحد على الأقل
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## العثور على جميع الأحداث في جميع سجلات الحدث التي تقع بين الطوابق الزمنية البداية والنهاية
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## تحويل الخصائص إلى شيء أكثر ودية وإلحاق كل حدث في ملف نص سجل الحدث
 $LogProps = @{ }
 [System.Collections.ArrayList]$MyEvents = @()
 foreach ($Event in $Events) {
 $LogProps.Time = $Event.TimeCreated
 $LogProps.Source = $Event.ProviderName
 $LogProps.EventId = $Event.Id
 if ($Event.Message) {
 $LogProps.Message = $Event.Message.Replace("<code>n", '|').Replace("</code>r", '|')
 }
 $LogProps.EventLog = $Event.LogName
 $MyEvents.Add([pscustomobject]$LogProps) | Out-Null
 }
 $MyEvents | sort Time | Export-Csv -Path "$OutputFolderPath\eventlogs.txt" -Append -NoTypeInformation
 }
 
 ## تشغيل الأمر فقط إذا أراد المستخدم العثور على ملفات سجل
 if (!$EventLogsOnly.IsPresent) {
        ## استعراض جميع حصص المسؤول البعيدة على الكمبيوتر البعيد. أفعل ذلك بدلاً من استعراض جميع الأقراص المادية لأن
        ## قد تكون القرص هناك والحصة قد لا تكون موجودة مما يعني أنني لا أستطيع الوصول إلى القرص على أي حال.
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## التأكد من الوصول إلى جميع حصص المسؤول البعيدة
 foreach ($Share in $Shares) {
 $Share = "\\$ComputerName\$($Share.Name)"
 if (!(Test-Path $Share)) {
 Write-Warning "Unable to access the '$Share' share on '$Computername'"
 } else {
 $AccessibleShares.Add($Share) | Out-Null 
 }
 }
 
 $AllFilesQueryParams = @{
 Path = $AccessibleShares
 Recurse = $true
 Force = $true
 ErrorAction = 'SilentlyContinue'
 File = $true
 }
 ## إضافة أي مجلدات محددة في معلمة $ExcludeDirectory لعدم البحث عن ملفات السجل فيها
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## إنشاء سلسلة regex المجنونة التي أستخدمها للبحث عن عدد مختلف من تنسيقات التاريخ/الوقت.
 ## يتم استخدام هذا في محاولة البحث عن سلاسل تاريخ/وقت في كل ملف نصي تم العثور عليه
 ##TODO: إضافة قدرة للمطابقة على Jan،Feb،Mar،الخ
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## عد فقط الملفات التي تطابق معلمات الاستعلام وتحتوي على محتوى
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## تسجيل الملف إذا كان وقت الكتابة الأخير داخل الإطار الزمني. يعثر هذا على ملفات السجل التي قد لا تسجل تاريخ/وقت
 ## وقت لكن قد تكون متورطة في أي حدث يحاول المستخدم العثور عليه.
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## إذا كان الملف الذي تم العثور عليه يتطابق مع مجموعة الامتدادات التي أحاول العثور عليها وهو في الواقع ملف نصي عادي.
 ## استخدم Get-Content للتحقق مرتين فقط من أنه ملف نصي عادي قبل تحليله.
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## تحقق من محتويات ملف النص من الإشارات إلى التواريخ في الإطار الزمني
 Write-Verbose "Checking log file '$($_.Name)' for date/time match in contents"
 $LineMatches = Select-String -Path $_.FullName -Pattern $DateTimeRegex
 if ($LineMatches) {
 Write-Verbose "Date/time match found in file '$($_.FullName)'"
 ## تسجيل جميع السطور المتطابقة في ملف التدقيق.
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## إنشاء نفس المسار إلى ملف السجل على الكمبيوتر البعيد داخل مجلد سجل الإخراج و
 ## نسخ ملف السجل بحدث داخل الإطار الزمني إلى تلك المسار.
 ## لن يعمل هذا إذا واجهت مسار ملف أعلى من 255 حرفًا.
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##TODO: إزالة إجراء الخطأ عندما يتم تنفيذ دعم المسار الطويل
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

ملخص

يعد أمر Get-EventLog أمرًا رائعًا للاستخدام إذا كنت بحاجة إلى استعلام أحد سجلات الأحداث الشائعة بسرعة. إنه سهل الاستخدام ويوفر بعض القدرة على التصفية الأساسية. ومع ذلك، إذا كنت بحاجة إلى التحقيق في سجل الأحداث بعمق، فإن أمر Get-WinEvent قد يكون أفضل، ولكنه أكثر صعوبة في الاستخدام أحيانًا وقد يتطلب معرفة ببعض بنى الجمل مثل XPath.

Source:
https://adamtheautomator.com/get-eventlog/