├── .editorconfig ├── .github ├── FUNDING.yml ├── copilot-instructions.md └── dependabot.yml ├── .gitignore ├── Build └── Manage-Module.ps1 ├── CHANGELOG.MD ├── Docs ├── Get-Events.md ├── Get-EventsFilter.md ├── Get-EventsInformation.md ├── Get-EventsSettings.md ├── Readme.md ├── Set-EventsSettings.md └── Write-Event.md ├── Examples ├── Example.ComparePerformance.ps1 ├── Example.QueryEvents01.ps1 ├── Example.QueryEvents02.ps1 ├── Example.QueryEvents03.ps1 ├── Example.QueryEvents04.ps1 ├── Example.QueryEventsByRecordID.ps1 ├── Example.QueryEventsLocally.ps1 ├── Example.QueryListLog.ps1 ├── Example.QueryNamedEvents01.ps1 ├── Example.QueryNamedEvents02.ps1 ├── Example.QueryNamedEvents03.ps1 ├── Example.QueryNamedEventsADUserLogonFailed.ps1 ├── Example.QueryNamedEventsSMB01.ps1 ├── Example.UseBinary.ps1 ├── Example.WriteEvent.ps1 ├── RunMe - Events Information 2.ps1 ├── RunMe - Events Information.ps1 ├── RunMe-EventsPowerShell.ps1 ├── RunMe-LevelsAndKeywords.ps1 ├── RunMe-SetEventLogSize.ps1 ├── RunMe-Test File Scan Functionality.ps1 ├── RunMe-Test Named Filters.ps1 ├── RunMe-Test RecordID.ps1 ├── RunMe-Test XML Building.ps1 ├── RunMe-Test15.ps1 ├── RunMe-Test16.ps1 ├── RunMe-Test17.ps1 ├── RunMe-Test19.ps1 ├── RunMe-Tests1.ps1 ├── RunMe-Tests10.ps1 ├── RunMe-Tests11.ps1 ├── RunMe-Tests12.ps1 ├── RunMe-Tests13.ps1 ├── RunMe-Tests14.ps1 ├── RunMe-Tests2.ps1 ├── RunMe-Tests3.ps1 ├── RunMe-Tests4.ps1 ├── RunMe-Tests5.ps1 ├── RunMe-Tests8.ps1 ├── RunMe-Tests9.ps1 └── RunMe-Text XML Output.ps1 ├── License ├── PSEventViewer.AzurePipelines.yml ├── PSEventViewer.Tests.ps1 ├── PSEventViewer.psd1 ├── PSEventViewer.psm1 ├── Private ├── Add-ToHashTable.ps1 ├── ScriptBlock.ps1 └── ScriptBlockEventsInformation.ps1 ├── Public ├── Get-Events.ps1 ├── Get-EventsFilter.ps1 ├── Get-EventsInformation.ps1 ├── Get-EventsSettings.ps1 └── Set-EventsSettings.ps1 ├── README.md ├── Sources ├── EventViewerX.Examples │ ├── EventViewerX.Examples.csproj │ ├── Examples.EventWatchingBasic.cs │ ├── Examples.FindBasic.cs │ ├── Examples.QueryBasic.cs │ ├── Examples.QueryEventSettings.cs │ ├── Examples.QueryParallelSpeed.cs │ ├── Examples.QueryParallelWithCount.cs │ ├── Examples.WriteEvents.cs │ └── Program.cs ├── EventViewerX.Tests │ ├── EventViewerX.Tests.csproj │ └── TestSearchingEvents.cs ├── EventViewerX.sln ├── EventViewerX │ ├── BinaryWrappers.cs │ ├── Definitions │ │ ├── Events.cs │ │ ├── GroupPolicy.cs │ │ ├── GroupPolicyLinks.cs │ │ ├── Keywords.cs │ │ ├── Level.cs │ │ └── ParallelOption.cs │ ├── EventLogDetails.cs │ ├── EventObject.cs │ ├── EventObjectSlim.cs │ ├── EventViewer.csproj.DotSettings │ ├── EventViewerX.csproj │ ├── EventViewerX.csproj.DotSettings │ ├── EventsHelper.cs │ ├── Helpers │ │ └── ActiveDirectory │ │ │ └── GroupPolicyHelpers.cs │ ├── Logger │ │ └── InternalLogger.cs │ ├── MyEventSource.cs │ ├── Rules │ │ ├── ActiveDirectory │ │ │ ├── Computers │ │ │ │ ├── ADComputerChangeDetailed.cs │ │ │ │ ├── ADComputerCreateChange.cs │ │ │ │ └── ADComputerDeleted.cs │ │ │ ├── GroupPolicies │ │ │ │ ├── ADGroupPolicyChanges.cs │ │ │ │ ├── ADGroupPolicyChangesDetailed.cs │ │ │ │ ├── ADGroupPolicyEdits.cs │ │ │ │ ├── ADGroupPolicyLinks.cs │ │ │ │ └── GroupPolicies.cs │ │ │ ├── Groups │ │ │ │ ├── ADGroupChange.cs │ │ │ │ ├── ADGroupChangeDetailed.cs │ │ │ │ ├── ADGroupCreateDelete.cs │ │ │ │ ├── ADGroupEnumeration.cs │ │ │ │ └── ADGroupMembershipChange.cs │ │ │ ├── LDAP │ │ │ │ ├── ADLdapBindingDetails.cs │ │ │ │ └── ADLdapBindingSummary.cs │ │ │ ├── OrganizationalUnit │ │ │ │ └── ADOrganizationalUnitChangeDetailed.cs │ │ │ ├── Others │ │ │ │ └── ADOtherChangeDetailed.cs │ │ │ ├── Replication │ │ │ │ └── Replication.cs │ │ │ ├── SMB │ │ │ │ └── SMBServerAudit.cs │ │ │ └── Users │ │ │ │ ├── ADUserChangeDetailed.cs │ │ │ │ ├── ADUserCreateChange.cs │ │ │ │ ├── ADUserLockouts.cs │ │ │ │ ├── ADUserLogon.cs │ │ │ │ ├── ADUserLogonFailed.cs │ │ │ │ ├── ADUserLogonKerberos.cs │ │ │ │ ├── ADUserStatus.cs │ │ │ │ └── ADUserUnlocked.cs │ │ ├── Logging │ │ │ ├── LogsClearedOther.cs │ │ │ ├── LogsClearedSecurity.cs │ │ │ └── LogsFullSecurity.cs │ │ ├── NPS │ │ │ └── NetworkAccessAuthenticationPolicy.cs │ │ └── Windows │ │ │ ├── ClientGroupPolicies.cs │ │ │ ├── OSCrash.cs │ │ │ ├── OSStartupShutdownCrash.cs │ │ │ └── OSTimeChange.cs │ ├── SearchEvents.GetProviders.cs │ ├── SearchEvents.GetPublisher.cs │ ├── SearchEvents.LogDetails.cs │ ├── SearchEvents.NamedEventsDetails.cs │ ├── SearchEvents.QueryLog.cs │ ├── SearchEvents.QueryNamedEvents.cs │ ├── SearchEvents.WriteEvent.cs │ ├── SearchEvents.WriteEventEx.cs │ ├── Settings.cs │ ├── TimeHelper.cs │ └── WatchEvents.cs └── PSEventViewer │ ├── CmdletFindWinEvent.cs │ ├── CmdletStartEventWatching.cs │ ├── CmdletWriteWinEvent.cs │ ├── Communication │ ├── AsyncPSCmdlet.cs │ └── InternalLoggerPowerShell.cs │ ├── PSEventViewer.csproj │ └── PSEventViewer.csproj.DotSettings └── Tests ├── Get-EventFilters.Tests.ps1 ├── Get-Events.Tests.ps1 └── Logs ├── Active Directory Web Services.evtx └── NamedFilterExamples.evtx /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: PrzemyslawKlys 4 | custom: https://paypal.me/PrzemyslawKlys -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Copilot Instructions 2 | 3 | ## Rules for PowerShell 4 | - always use K&R/OTBS style. 5 | - always add documentation for functions inside the function block. 6 | 7 | # Rules for C# 8 | - always prefer braces even for single line statements. 9 | - prefer file-scoped namespaces. -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | 3 | #### 2.3.0 - 2024.11.25 *EXPERIMENTAL* 4 | - Fix for SMBv1 5 | 6 | **Full Changelog**: https://github.com/EvotecIT/PSEventViewer/compare/v2.2.0...v2.3.0 7 | 8 | #### 2.2.0 - 2024.11.25 *EXPERIMENTAL* 9 | * Add smbv1 by @PrzemyslawKlys in https://github.com/EvotecIT/PSEventViewer/pull/21 10 | 11 | **Full Changelog**: https://github.com/EvotecIT/PSEventViewer/compare/v2.1.0...v2.2.0 12 | 13 | #### 2.1.0 - 2024.08.30 *EXPERIMENTAL* 14 | - Fixes missing alias for `Write-WinEvent` for one of properties 15 | 16 | #### 2.0.0 - 2024.07.21 *EXPERIMENTAL* 17 | - Converted `Write-Event` to `Write-WinEvent` as binary cmdlet now 18 | - Added `Find-WinEvent` binary cmdlet which is a replacement for `Get-Events`. It's complete rewrite of `Get-Events`, it's much faster and provides unique features known from `PSWinReporting` module as offered by `Find-Events` function 19 | 20 | #### 1.0.22 - 2022.05.26 21 | - Fixed `Set-EventsSettings` not setting values as required 22 | 23 | #### 1.0.21 - 2022.05.25 24 | - Added alias to parameter in `Set-EventsSettings` 25 | 26 | #### 1.0.20 - 2022.05.25 27 | - Improved `Get-EventsSettings` to work using native ways rather than playing with registry settings, also more options 28 | - Improved `Set-EventsSettings` to work using native ways rather than playing with registry settings, also more options 29 | 30 | #### 1.0.19 - 2022.05.18 31 | - Performance improvements 32 | - `ObjectAffected` property not reporting when `TargetDomainName` is empty - [#16](https://github.com/EvotecIT/PSEventViewer/issues/16) 33 | #### 1.0.18 - 2022.04.02 34 | - Improved documentation 35 | - Signed module 36 | 37 | #### 1.0.17 - 2020.05.31 38 | - Fix for `Get-Events` (use of Path and NamedDataFilter) - provided by [danubie #13](https://github.com/EvotecIT/PSEventViewer/pull/13) - solves [#12](https://github.com/EvotecIT/PSEventViewer/issues/12) 39 | 40 | #### 1.0.16 - 2020.05.31 41 | - Fix for `Get-Events` for NamedDataFilter - provided by [danubie #11](https://github.com/EvotecIT/PSEventViewer/pull/11) - solves [#10](https://github.com/EvotecIT/PSEventViewer/issues/10) 42 | 43 | #### 1.0.15 - 2020.05.17 44 | - Fix for `Get-EventsFilter` - provided by [danubie #9](https://github.com/EvotecIT/PSEventViewer/pull/9) - solves [#7](https://github.com/EvotecIT/PSEventViewer/issues/7) and [#8](https://github.com/EvotecIT/PSEventViewer/issues/8) 45 | 46 | #### 1.0.14 - 2020.04.11 47 | - Updates to PSD1 48 | 49 | #### 1.0.13 - 2020.02.16 50 | - Added Get-EventsSettings/Set-EventsSettings (Work in progress) 51 | 52 | #### 1.0.12 - 2020.01.01 53 | - Added some new aliases 54 | 55 | #### 1.0.11 - 2019.12.30 56 | - Added Write-Event 57 | 58 | #### 1.0.10 - 2019.12.17 59 | - Path is back. Not sure why it was gone. Need improvements. 60 | 61 | #### 1.0.9 - 2019.11.12 62 | - Removed dependency on PSSharedGoods on the published module 63 | - PSSharedGoods is still dependency, but the building process makes it possible to compile it and push to PSGallery/Releases without that dependency. 64 | 65 | #### 1.0.7 - 2019.09.12 66 | - Small changes to Get-EventsInformation 67 | 68 | #### 0.62 - 2019.01.11 69 | - Fix for Member Name with a comma inside 70 | 71 | #### 0.61 - 2019.01.02 72 | - Multiple new parameters, some new functionality 73 | 74 | #### 0.51 75 | - Added -RecordID parameter (currently it only works with LogName + RecordID, you can't use any other parameters with RecordID as it will take LogName + RecordID anyways and crash if it's not there) 76 | 77 | #### 0.50 78 | - A version that worked fine :-) -------------------------------------------------------------------------------- /Docs/Get-EventsSettings.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSEventViewer-help.xml 3 | Module Name: PSEventViewer 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-EventsSettings 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Get-EventsSettings [[-LogName] ] [[-ComputerName] ] [[-MaximumSize] ] 17 | [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | {{ Fill in the Description }} 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | ```powershell 27 | PS C:\> {{ Add example code here }} 28 | ``` 29 | 30 | {{ Add example description here }} 31 | 32 | ## PARAMETERS 33 | 34 | ### -ComputerName 35 | {{ Fill ComputerName Description }} 36 | 37 | ```yaml 38 | Type: String 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: False 43 | Position: 1 44 | Default value: None 45 | Accept pipeline input: False 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -LogName 50 | {{ Fill LogName Description }} 51 | 52 | ```yaml 53 | Type: String 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: False 58 | Position: 0 59 | Default value: None 60 | Accept pipeline input: False 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -MaximumSize 65 | {{ Fill MaximumSize Description }} 66 | 67 | ```yaml 68 | Type: Int32 69 | Parameter Sets: (All) 70 | Aliases: 71 | 72 | Required: False 73 | Position: 2 74 | Default value: None 75 | Accept pipeline input: False 76 | Accept wildcard characters: False 77 | ``` 78 | 79 | ### CommonParameters 80 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 81 | 82 | ## INPUTS 83 | 84 | ### None 85 | 86 | ## OUTPUTS 87 | 88 | ### System.Object 89 | ## NOTES 90 | 91 | ## RELATED LINKS 92 | -------------------------------------------------------------------------------- /Docs/Readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: PSEventViewer 3 | Module Guid: 5df72a79-cdf6-4add-b38d-bcacf26fb7bc 4 | Download Help Link: {{ Update Download Link }} 5 | Help Version: {{ Please enter version of help manually (X.X.X.X) format }} 6 | Locale: en-US 7 | --- 8 | 9 | # PSEventViewer Module 10 | ## Description 11 | {{ Fill in the Description }} 12 | 13 | ## PSEventViewer Cmdlets 14 | ### [Get-Events](Get-Events.md) 15 | {{ Fill in the Description }} 16 | 17 | ### [Get-EventsFilter](Get-EventsFilter.md) 18 | {{ Fill in the Description }} 19 | 20 | ### [Get-EventsInformation](Get-EventsInformation.md) 21 | {{ Fill in the Description }} 22 | 23 | ### [Get-EventsSettings](Get-EventsSettings.md) 24 | {{ Fill in the Description }} 25 | 26 | ### [Set-EventsSettings](Set-EventsSettings.md) 27 | {{ Fill in the Description }} 28 | 29 | ### [Write-Event](Write-Event.md) 30 | {{ Fill in the Description }} 31 | 32 | -------------------------------------------------------------------------------- /Docs/Set-EventsSettings.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSEventViewer-help.xml 3 | Module Name: PSEventViewer 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Set-EventsSettings 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Set-EventsSettings [[-LogName] ] [[-ComputerName] ] [[-MaximumSizeMB] ] 17 | [[-EventAction] ] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | {{ Fill in the Description }} 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | ```powershell 27 | PS C:\> {{ Add example code here }} 28 | ``` 29 | 30 | {{ Add example description here }} 31 | 32 | ## PARAMETERS 33 | 34 | ### -ComputerName 35 | {{ Fill ComputerName Description }} 36 | 37 | ```yaml 38 | Type: String 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: False 43 | Position: 1 44 | Default value: None 45 | Accept pipeline input: False 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -EventAction 50 | {{ Fill EventAction Description }} 51 | 52 | ```yaml 53 | Type: String 54 | Parameter Sets: (All) 55 | Aliases: 56 | Accepted values: OverwriteEventsAsNeededOldestFirst, ArchiveTheLogWhenFullDoNotOverwrite, DoNotOverwriteEventsClearLogManually, None 57 | 58 | Required: False 59 | Position: 3 60 | Default value: None 61 | Accept pipeline input: False 62 | Accept wildcard characters: False 63 | ``` 64 | 65 | ### -LogName 66 | {{ Fill LogName Description }} 67 | 68 | ```yaml 69 | Type: String 70 | Parameter Sets: (All) 71 | Aliases: 72 | 73 | Required: False 74 | Position: 0 75 | Default value: None 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### -MaximumSizeMB 81 | {{ Fill MaximumSizeMB Description }} 82 | 83 | ```yaml 84 | Type: Int32 85 | Parameter Sets: (All) 86 | Aliases: 87 | 88 | Required: False 89 | Position: 2 90 | Default value: None 91 | Accept pipeline input: False 92 | Accept wildcard characters: False 93 | ``` 94 | 95 | ### CommonParameters 96 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 97 | 98 | ## INPUTS 99 | 100 | ### None 101 | 102 | ## OUTPUTS 103 | 104 | ### System.Object 105 | ## NOTES 106 | 107 | ## RELATED LINKS 108 | -------------------------------------------------------------------------------- /Docs/Write-Event.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSEventViewer-help.xml 3 | Module Name: PSEventViewer 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Write-Event 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Write-Event [[-Computer] ] [-LogName] [-Source] [[-Category] ] 17 | [[-EntryType] ] [-ID] [-Message] [[-AdditionalFields] ] 18 | [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | {{ Fill in the Description }} 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | ```powershell 28 | PS C:\> {{ Add example code here }} 29 | ``` 30 | 31 | {{ Add example description here }} 32 | 33 | ## PARAMETERS 34 | 35 | ### -AdditionalFields 36 | {{ Fill AdditionalFields Description }} 37 | 38 | ```yaml 39 | Type: Array 40 | Parameter Sets: (All) 41 | Aliases: 42 | 43 | Required: False 44 | Position: 7 45 | Default value: None 46 | Accept pipeline input: False 47 | Accept wildcard characters: False 48 | ``` 49 | 50 | ### -Category 51 | {{ Fill Category Description }} 52 | 53 | ```yaml 54 | Type: Int32 55 | Parameter Sets: (All) 56 | Aliases: 57 | 58 | Required: False 59 | Position: 3 60 | Default value: None 61 | Accept pipeline input: False 62 | Accept wildcard characters: False 63 | ``` 64 | 65 | ### -Computer 66 | {{ Fill Computer Description }} 67 | 68 | ```yaml 69 | Type: String[] 70 | Parameter Sets: (All) 71 | Aliases: 72 | 73 | Required: False 74 | Position: 0 75 | Default value: None 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### -EntryType 81 | {{ Fill EntryType Description }} 82 | 83 | ```yaml 84 | Type: EventLogEntryType 85 | Parameter Sets: (All) 86 | Aliases: Level 87 | Accepted values: Error, Warning, Information, SuccessAudit, FailureAudit 88 | 89 | Required: False 90 | Position: 4 91 | Default value: None 92 | Accept pipeline input: False 93 | Accept wildcard characters: False 94 | ``` 95 | 96 | ### -ID 97 | {{ Fill ID Description }} 98 | 99 | ```yaml 100 | Type: Int32 101 | Parameter Sets: (All) 102 | Aliases: EventID 103 | 104 | Required: True 105 | Position: 5 106 | Default value: None 107 | Accept pipeline input: False 108 | Accept wildcard characters: False 109 | ``` 110 | 111 | ### -LogName 112 | {{ Fill LogName Description }} 113 | 114 | ```yaml 115 | Type: String 116 | Parameter Sets: (All) 117 | Aliases: EventLog 118 | 119 | Required: True 120 | Position: 1 121 | Default value: None 122 | Accept pipeline input: False 123 | Accept wildcard characters: False 124 | ``` 125 | 126 | ### -Message 127 | {{ Fill Message Description }} 128 | 129 | ```yaml 130 | Type: String 131 | Parameter Sets: (All) 132 | Aliases: 133 | 134 | Required: True 135 | Position: 6 136 | Default value: None 137 | Accept pipeline input: False 138 | Accept wildcard characters: False 139 | ``` 140 | 141 | ### -Source 142 | {{ Fill Source Description }} 143 | 144 | ```yaml 145 | Type: String 146 | Parameter Sets: (All) 147 | Aliases: Provider, ProviderName 148 | 149 | Required: True 150 | Position: 2 151 | Default value: None 152 | Accept pipeline input: False 153 | Accept wildcard characters: False 154 | ``` 155 | 156 | ### CommonParameters 157 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 158 | 159 | ## INPUTS 160 | 161 | ### None 162 | 163 | ## OUTPUTS 164 | 165 | ### System.Object 166 | ## NOTES 167 | 168 | ## RELATED LINKS 169 | -------------------------------------------------------------------------------- /Examples/Example.ComparePerformance.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module .\PSEventViewer.psd1 -Force 3 | 4 | Measure-Command { 5 | Find-WinEvent -LogName 'Security' -EventId 5136, 5137, 5168 -Verbose -MachineName 'AD1' -ParallelOption Disabled 6 | } 7 | Measure-Command { 8 | Get-WinEvent -FilterHashtable @{ 9 | LogName = 'Security' 10 | ID = 5136, 5137, 5168 11 | } -ComputerName AD1 12 | } 13 | 14 | Measure-Command { 15 | Find-WinEvent -LogName 'Security' -EventId 5136, 5137, 5168 -Verbose -MachineName 'AD1', 'AD2', 'AD0' -ParallelOption Parallel 16 | } 17 | Measure-Command { 18 | foreach ($Machine in 'AD1', 'AD2', 'AD0') { 19 | Get-WinEvent -FilterHashtable @{ 20 | LogName = 'Security' 21 | ID = 5136, 5137, 5168 22 | } -ComputerName $Machine 23 | } 24 | } -------------------------------------------------------------------------------- /Examples/Example.QueryEvents01.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | # those events will be split in 22 chunks to allow parallel processing and prevent errors 5 | $EventId = @( 6 | 1..30 7 | 5136, 5137, 5168 8 | ) 9 | 10 | $Output = Find-WinEvent -LogName 'Security' -EventId $EventId -Verbose -MachineName 'AD1', 'AD2', 'AD0' -ParallelOption Parallel 11 | $Output | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryEvents02.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | $Output = Find-WinEvent -LogName 'Security' -EventId 4627 -Verbose -MachineName 'AD1', 'AD2', 'AD0' -ParallelOption Parallel | Select-Object -First 2 5 | $Output[0] | Format-List 6 | $Output[0].MessageData 7 | 8 | 9 | Find-WinEvent -Machine AD0 -LogName Security 5136, 5137, 5139, 5141, 4741, 4742, 4740, 4727, 4730, 4731, 4734, 4744, 4748, 4749, 4753, 4754, 4758, 4759, 4763 -StartTime (Get-Date).AddDays(-1) -EndTime (Get-Date) -Verbose | Format-Table 10 | 11 | Find-WinEvent -Machine AD0, AD1, AD2 -LogName Security -Id 4625 -Verbose #-StartTime (Get-Date).AddDays(-1) -EndTime (Get-Date) -Verbose | Format-Table 12 | 13 | Find-WinEvent -Machine AD0, AD1, AD2 -LogName Security -Id 4624 -Verbose #-StartTime (Get-Date).AddDays(-1) -EndTime (Get-Date) -Verbose | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryEvents03.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force -Verbose 3 | 4 | Find-WinEvent -LogName 'System' -Verbose -ParallelOption Parallel -MachineName 'EVOMONSTER', 'AD1' -MaxEvents 1 | Format-Table 5 | 6 | Find-WinEvent -LogName 'System' -Verbose -ParallelOption Parallel -MachineName 'EVOMONSTER', 'AD1' -MaxEvents 1 -Expand | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryEvents04.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | #$Events = Find-WinEvent -LogName 'Application' -Verbose -ParallelOption Parallel -EventID 1035,1042 -MaxEvents 2 5 | #$Events | Format-Table 6 | 7 | $Events = Find-WinEvent -LogName 'Application' -Verbose -ParallelOption Parallel -EventID 1035,258 -MaxEvents 10 8 | $Events | Format-Table 9 | <# 10 | $Events = Find-WinEvent -LogName 'Application' -Verbose -ParallelOption Parallel -ProviderName "MsiInstaller" -MaxEvents 2 11 | $Events | Format-Table 12 | #> 13 | $Events.Data | Format-Table 14 | -------------------------------------------------------------------------------- /Examples/Example.QueryEventsByRecordID.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module .\PSEventViewer.psd1 -Force 3 | 4 | Find-WinEvent -LogName 'Security' -Verbose -MachineName 'AD1', 'AD2', 'AD0' -EventRecordID 16833138,16833124,16833136 | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryEventsLocally.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | Find-WinEvent -LogName 'Security' -EventId 4905 -Verbose -ParallelOption Disable | Format-Table 5 | 6 | Find-WinEvent -LogName 'Security' -EventId 4905 -Verbose -ParallelOption Parallel | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryListLog.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | Find-WinEvent -Verbose -ListLog "Application", "System", "Security*" -MachineName AD3, AD1, EVOMONSTER, AD2, AD0, AD15, AD30 | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryNamedEvents01.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force -Verbose 3 | 4 | Find-WinEvent -Machine AD0 -Type ADUserLogonKerberos -TimePeriod Last1Hours -Verbose # | Format-Table 5 | 6 | $Data = Find-WinEvent -MachineName AD0 -EventRecordId '28907707' -LogName Security 7 | 8 | #Find-WinEvent -Type ADComputerChangeDetailed, ADComputerCreateChange, ADUserLockouts, ADGroupCreateDelete , ADLdapBindingDetails -MachineName AD1, AD2, AD0 -Verbose | Format-Table 9 | 10 | # Measure-Command { 11 | # Find-WinEvent -Type ADComputerChangeDetailed, ADComputerCreateChange, ADUserLockouts, ADGroupCreateDelete -MachineName AD1, AD2, AD0 -Verbose -StartTime (Get-Date).AddDays(-1) 12 | 13 | # } 14 | 15 | Measure-Command { $T = Find-WinEvent -Machine AD0 -LogName Security -EventId 5136, 5137, 5139, 5141, 4741, 4742, 4740, 4727, 4730, 4731, 4734, 4744, 4748, 4749, 4753, 4754, 4758, 4759, 4763 -DateFrom (Get-Date).AddDays(-1) -DateTo (Get-Date) -Verbose } -------------------------------------------------------------------------------- /Examples/Example.QueryNamedEvents02.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | $findWinEventSplat = @{ 5 | Type = 'ADUserLogonFailed' # 'ADComputerChangeDetailed', 'ADUserChangeDetailed', 'ADGroupChange', 'ADGroupCreateDelete', 'ADGroupMembershipChange' 6 | MachineName = 'AD1', 'AD2', 'AD0', 'ADCS' 7 | Verbose = $true 8 | } 9 | 10 | Find-WinEvent @findWinEventSplat -TimePeriod CurrentDay | Format-List 11 | ##Test | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryNamedEvents03.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | $findWinEventSplat = @{ 5 | Type = 'OSTimeChange' 6 | MachineName = 'AD1', 'AD2', 'AD0' 7 | Verbose = $true 8 | } 9 | 10 | Find-WinEvent @findWinEventSplat -TimePeriod Last3Days | Format-Table -------------------------------------------------------------------------------- /Examples/Example.QueryNamedEventsADUserLogonFailed.ps1: -------------------------------------------------------------------------------- 1 |  2 | Clear-Host 3 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 4 | 5 | $findWinEventSplat = @{ 6 | Type = 'ADUserLogonFailed' 7 | MachineName = 'DC1', 'DC2' 8 | Verbose = $true 9 | } 10 | 11 | Find-WinEvent @findWinEventSplat -TimePeriod Last7Days | Format-Table * -------------------------------------------------------------------------------- /Examples/Example.QueryNamedEventsSMB01.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | $findWinEventSplat = @{ 5 | Type = 'ADSMBServerAuditV1' 6 | MachineName = 'AD1', 'AD2' 7 | Verbose = $true 8 | } 9 | 10 | Find-WinEvent @findWinEventSplat -TimePeriod Last3Days | Format-Table * -------------------------------------------------------------------------------- /Examples/Example.UseBinary.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | 5 | $List = [PSEventViewer.SearchEvents]::GetProviders() 6 | 7 | return 8 | foreach ($L in $LIst) { 9 | if ($L.Events.Count -gt 0) { 10 | $L.Events | Format-Table 11 | break 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /Examples/Example.WriteEvent.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module $PSScriptRoot\..\PSEventViewer.psd1 -Force 3 | 4 | $writeWinEventSplat = @{ 5 | LogName = 'Application' 6 | EventId = 5136 7 | Message = 'This is a test message' 8 | Verbose = $true 9 | ProviderName = 'PSEventViewer1' 10 | Category = 1 11 | AdditionalFields = 'Field1', 'Field2', 'Field3' 12 | EventLogEntryType = 'Error' 13 | } 14 | 15 | Write-WinEvent @writeWinEventSplat -------------------------------------------------------------------------------- /Examples/RunMe - Events Information 2.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PSEventViewer.psd1 -Force 2 | 3 | $Size = Get-EventsInformation -FilePath $EventLogsDirectory.FullName -LogName 'Security' -Machine AD1 #-RunAgainstDC 4 | $Size #| Format-Table -AutoSize Source, EventNewest, EventOldest,FileSize, FileSizeCurrentGB, FileSizeMaximumGB, IsEnabled, IsLogFull, LastAccessTime, LastWriteTime, LogFilePath, LOgName 5 | $Size.SecurityDescriptorDiscretionaryAcl -------------------------------------------------------------------------------- /Examples/RunMe - Events Information.ps1: -------------------------------------------------------------------------------- 1 | Import-Module ..\PSEventViewer.psd1 -Force 2 | 3 | $Computers = 'EVO1', 'AD1' 4 | $LogName = 'Security' 5 | 6 | $EventLogsDirectory = Get-ChildItem -Path 'C:\MyEvents' 7 | 8 | $Size = Get-EventsInformation -FilePath $EventLogsDirectory.FullName -Computer $Computers -LogName 'Security', 'System' -Verbose 9 | $Size | Format-Table -a Source, EventNewest, EventOldest, FileSize, FileSizeCurrentGB, FileSizeMaximumGB, IsEnabled, IsLogFull, LastAccessTime, LastWriteTime, LogFilePath, LOgName -------------------------------------------------------------------------------- /Examples/RunMe-EventsPowerShell.ps1: -------------------------------------------------------------------------------- 1 | # https://devblogs.microsoft.com/powershell/powershell-the-blue-team/ 2 | 3 | Restore-PowerShellScript -Type WindowsPowerShell -Path $PSScriptRoot\Scripts -ComputerName AD1, AD2 -Verbose 4 | 5 | #$Events1 = Get-Events -LogName 'Microsoft-Windows-PowerShell/Operational' -ID 4103, 4104 -Verbose -Path "$Env:USERPROFILE\Desktop\PowerShellCore.evtx" 6 | #$Events1 = Get-Events -LogName 'Microsoft-Windows-PowerShell/Operational' -ID 4104, 4105, 4106 -Verbose -Path "$Env:USERPROFILE\Desktop\PowerShellWindows.evtx" 7 | #$Events1 = Get-Events -LogName 'Microsoft-Windows-PowerShell/Operational' -ID 4104 -Verbose -Path "$Env:USERPROFILE\Desktop\PowerShellWindows.evtx" 8 | #$Events = Get-Events -ID 4103, 4104, 4105, 4106 -Verbose -Path "$Env:USERPROFILE\Desktop\PowerShell-26082020.evtx" 9 | #Restore-PowerShellScript -Events $Events -Path $PSScriptRoot\ScriptsMalware -AddMarkdown #-Format 10 | #Restore-PowerShellScript -Events $Events1 -Path $PSScriptRoot\Scripts -AddMarkdown -Format -------------------------------------------------------------------------------- /Examples/RunMe-LevelsAndKeywords.ps1: -------------------------------------------------------------------------------- 1 | Import-Module PSEventViewer -Force 2 | 3 | Get-Events -MaxEvents 5 -LogName Security -Keywords AuditSuccess | Format-List * -------------------------------------------------------------------------------- /Examples/RunMe-SetEventLogSize.ps1: -------------------------------------------------------------------------------- 1 | Import-Module ..\PSEventViewer.psd1 -Force 2 | 3 | # proper registry key 4 | Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application' | Format-List 5 | # probably via gpo, but don't use 6 | Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\EventLog\\Application' | Format-List 7 | # optional, but usually not existing 8 | Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Channels' | Format-Table -AutoSize 9 | 10 | Get-EventsSettings -LogName 'Application' | Format-Table 11 | 12 | Set-EventsSettings -LogName 'Application' -MaximumSizeMB 15 -Mode Circular -WhatIf 13 | 14 | Get-EventsSettings -LogName 'Application' | Format-Table -------------------------------------------------------------------------------- /Examples/RunMe-Test File Scan Functionality.ps1: -------------------------------------------------------------------------------- 1 | Import-Module ..\PSEventViewer.psd1 -Force 2 | 3 | $FilePath = 'C:\Test\Archive-System-2019-11-18-16-45-35-050.evtx' 4 | $FilePath = 'C:\Test\Active Directory Web Services.evtx' 5 | $Events = Get-Events -Path $FilePath -Oldest -MaxEvents 1 -Verbose 6 | $Events 7 | 8 | $EventsNewest = Get-Events -Path $FilePath -MaxEvents 1 -Verbose 9 | $EventsNewest 10 | 11 | #Get-WinEvent -Path $FilePath -Oldest -MaxEvents 1 12 | #Get-WinEvent -Path $FilePath -MaxEvents 1 -------------------------------------------------------------------------------- /Examples/RunMe-Test Named Filters.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module ..\PSEventViewer.psd1 -Force 3 | 4 | [string] $User = 'Administrator' 5 | #Get-Events -LogName 'ForwardedEvents' -NamedDataFilter @{'SubjectUserName' = $User; 'TargetUserName' = $User } -Verbose -ExcludeID 5136 6 | 7 | 8 | 9 | Get-Events -LogName 'ForwardedEvents' -NamedDataExcludeFilter @{'SubjectUserName' = $User; 'TargetUserName' = $User } -Verbose -ExcludeID 5136 10 | #Get-Events -LogName 'ForwardedEvents' -------------------------------------------------------------------------------- /Examples/RunMe-Test RecordID.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | #Import-Module PSEventViewer -Force 3 | #Get-Events -LogName 'ForwardedEvents' -ID 1105 -RecordID 3512231 -Verbose 4 | #Get-Events -LogName 'Security' -ID 5379 -Verbose 5 | 6 | 7 | #Get-Events -LogName 'Security' -RecordID 5287804 -Machine AD1.AD.EVOTEC.XYZ | fl Message, MemberName, MemberNameWithoutCN 8 | #Get-Events -LogName 'Security' -RecordID 5844279 -Machine AD1.AD.EVOTEC.XYZ | fl Message, MemberName, MemberNameWithoutCN 9 | 10 | 11 | $MemberName = @( 12 | 'CN=Weird\, Name\, with ,OU=Users-Offboarded,OU=Production,DC=ad,DC=evotec,DC=xyz' 13 | 'CN=Weird Name\, with $\,.,OU=Users-Offboarded,OU=Production,DC=ad,DC=evotec,DC=xyz' 14 | 'CN=Weird Name,OU=Users-Offboarded,OU=Production,DC=ad,DC=evotec,DC=xyz' 15 | 'CN=Weird Name,DC=ad,DC=evotec,DC=xyz' 16 | 'CN=Weird Name,DC=ad,DC=evotec,DC=xyz' 17 | 'CN=Mailbox Database 1527735546,CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=Evotec,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=ad,DC=evotec,DC=xyz' 18 | 'CN=Test My\, User,OU=Users-Offboarded,OU=Production,DC=ad,DC=evotec,DC=xyz' 19 | ) 20 | 21 | Write-Color '- Members' -Color Yellow 22 | foreach ($Member in $MemberName) { 23 | 24 | $Member 25 | } 26 | 27 | Write-Color '- MyVersion' -Color Green 28 | foreach ($Member in $MemberName) { 29 | 30 | $Member -replace '^CN=|,(OU|DC|CN).*$' 31 | } 32 | Write-Color '- NewVersion' -Color Red 33 | foreach ($Member in $MemberName) { 34 | 35 | $Member -replace 'CN=|\\,|,(OU|DC|CN).*$' 36 | } 37 | 38 | Write-Color '- Final version?' -Color Blue 39 | foreach ($Member in $MemberName) { 40 | 41 | $Member -replace 'CN=|\\|,(OU|DC|CN).*$' 42 | } -------------------------------------------------------------------------------- /Examples/RunMe-Test XML Building.ps1: -------------------------------------------------------------------------------- 1 | Import-Module PSEventViewer -Force 2 | 3 | $StartTime = (Get-Date ).AddDays(-100) 4 | 5 | function Test { 6 | [CmdletBinding()] 7 | param() 8 | 9 | #$StartTime.ToUniversalTime() 10 | 11 | $xml = Get-EventsFilter -LogName 'ForwardedEvents' -RecordID '3512231', '3512232' -ProviderName 'Microsoft-Windows-Eventlog' 12 | 13 | # . 14 | 15 | $xml 16 | Get-WinEvent -FilterXml $xml -Verbose:$true 17 | 18 | } 19 | 20 | #Test -Verbose 21 | 22 | Write-Color "Output 1" -Color Red 23 | 24 | Get-EventsFilter -StartTime '1/1/2015 01:30:00 PM' -EndTime '1/1/2015 02:00:00 PM' -LogName 'ForwardedEvents' 25 | 26 | 27 | Write-Color "Output 2" -Color Red 28 | 29 | $User = 'Administrator' 30 | Get-EventsFilter -ID 4663 -NamedDataFilter @{'SubjectUserName' = $User; 'TargetUserName' = $User } -LogName 'ForwardedEvents' 31 | -------------------------------------------------------------------------------- /Examples/RunMe-Test15.ps1: -------------------------------------------------------------------------------- 1 | Import-Module PSEventViewer -Force 2 | 3 | #$WrongCredentials = (Get-Credential) 4 | $Credentials = (Get-Credential) 5 | 6 | $AllEvents = Get-Events -LogName 'Application' -ID 16384 -Machine 'AD1' -MaxEvents 3 -Verbose -ErrorVariable Test #-Credential $Credentials 7 | 8 | $AllEvents | Format-Table -a 9 | 10 | $Test | Format-Table -a 11 | 12 | 13 | #Get-WinEvent -LogName Application -ComputerName AD1 -Credential $Credentials -FilterHas 14 | 15 | $FilterHashTable = @{ 16 | LogName = 'Application' 17 | ID = 16384 18 | } 19 | $Event1 = Get-WinEvent -FilterHashtable $FilterHashTable -ComputerName AD1 -MaxEvents 3 -Credential $Credentials 20 | $Event1 | Format-Table -a 21 | 22 | 23 | 24 | return 25 | 26 | $Data = @( 27 | if ($AllEvents[2].NoNameB1 -match "\n") { 28 | $AllEvents[2].NoNameB1 -split '\n' 29 | } 30 | ) 31 | $AllEvents[2] | Format-List * -------------------------------------------------------------------------------- /Examples/RunMe-Test16.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PSEventViewer.psd1 -Force 2 | 3 | $Date = (Get-Date).AddDays(-3) 4 | $Date1 = Get-Date 5 | 6 | $User = 'Administrator' 7 | 8 | $Events = Get-Events -Machine AD1 -DateFrom $Date -DateTo $Date1 -ID 4738 -LogName 'Security' -Verbose -NamedDataFilter @{'SubjectUserName' = $User; 'TargetUserName' = $User } 9 | $Events -------------------------------------------------------------------------------- /Examples/RunMe-Test17.ps1: -------------------------------------------------------------------------------- 1 | Import-Module PSEventViewer -Force 2 | 3 | $AllEvents = Get-Events -LogName 'Application' -ID 1001 -MaxEvents 1 -Verbose -DisableParallel 4 | $Message = $AllEvents[0].Message 5 | 6 | $M = [ordered] @{} 7 | foreach ($SubMessage in $Message.Split([Environment]::NewLine)) { 8 | if ($SubMessage -like '*:*') { 9 | $T = $SubMessage.Split(':') 10 | $M[($T[0])] = $T[1] 11 | } else { 12 | if ($SubMessage.Trim() -ne '') { 13 | 14 | } 15 | } 16 | } 17 | 18 | 19 | 20 | <# 21 | Fault bucket 2058674454742653114, type 5 22 | Event Name: RADAR_PRE_LEAK_64 23 | Response: Not available 24 | Cab Id: 0 25 | 26 | Problem signature: 27 | P1: Code.exe 28 | P2: 1.36.0.0 29 | P3: 10.0.18362.2.0.0 30 | P4: 31 | P5: 32 | P6: 33 | P7: 34 | P8: 35 | P9: 36 | P10: 37 | 38 | Attached files: 39 | \\?\C:\Users\PRZEMY~1.KLY\AppData\Local\Temp\RDR7D2A.tmp\empty.txt 40 | \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER7D2B.tmp.WERInternalMetadata.xml 41 | \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER7D3C.tmp.xml 42 | \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER7D98.tmp.csv 43 | \\?\C:\ProgramData\Microsoft\Windows\WER\Temp\WER7DD7.tmp.txt 44 | 45 | These files may be available here: 46 | 47 | 48 | Analysis symbol: 49 | Rechecking for solution: 0 50 | Report Id: a0709c03-7dbf-42e3-977b-9b53a404444a 51 | Report Status: 268435456 52 | Hashed bucket: 74775b34cbd39705dc91e1825f1b58ba 53 | Cab Guid: 0 54 | 55 | #> -------------------------------------------------------------------------------- /Examples/RunMe-Test19.ps1: -------------------------------------------------------------------------------- 1 |  2 | Import-Module .\PSEventViewer.psd1 -Force 3 | 4 | $OK = Get-Events -LogName 'Security' -EventID 4722, 4725, 4740, 4724 -Computer ad1 -DisableParallel 5 | $OK[1] | Format-List * -------------------------------------------------------------------------------- /Examples/RunMe-Tests1.ps1: -------------------------------------------------------------------------------- 1 | Import-Module PSEventViewer -Force 2 | Clear-Host 3 | Write-Color 'Start processing events - Tests for expected output' -Color Red 4 | # #, 4722, 4723, 4724, 4725, 4726, 4738, 4740, 4767 5 | $TestServers = 'AD1.ad.evotec.xyz' 6 | $ID = 104 7 | $TestEvents1 = Get-Events -Machine $TestServers -Id $ID -LogName 'System' -MaxEvents 1 #-DisableParallel #-Verbose 8 | $ID = 16384 9 | $TestEvents2 = Get-Events -Machine $TestServers -Id $ID -LogName 'Application' -MaxEvents 1 #-DisableParallel #-Verbose 10 | $ID = 4634 11 | $TestEvents3 = Get-Events -Machine $TestServers -Id $ID -LogName 'Security' -MaxEvents 1 #-DisableParallel #-Verbose 12 | $ID = 4688 13 | $TestEvents4 = Get-Events -Machine $TestServers -Id $ID -LogName 'Security' -MaxEvents 1 #-DisableParallel #-Verbose 14 | $ID = 105 15 | $TestEvents5 = Get-Events -Machine $TestServers -Id $ID -LogName 'Application' -MaxEvents 1 #-DisableParallel #-Verbose 16 | $ID = 7036 17 | $TestEvents6 = Get-Events -Machine $TestServers -Id $ID -LogName 'System' -MaxEvents 1 #-DisableParallel #-Verbose 18 | $ID = 32 19 | $TestEvents7 = Get-Events -Machine $TestServers -Id $ID -LogName 'System' -MaxEvents 1 #-DisableParallel #-Verbose 20 | $ID = 1014 21 | $TestEvents8 = Get-Events -Machine $TestServers -Id $ID -LogName 'System' -MaxEvents 1 #-DisableParallel #-Verbose 22 | $ID = 8198 23 | $TestEvents9 = Get-Events -Machine $TestServers -Id $ID -LogName 'Application' -MaxEvents 1 #-DisableParallel #-Verbose 24 | $ID = 10154 25 | $TestEvents10 = Get-Events -Machine $TestServers -Id $ID -LogName 'System' -MaxEvents 1 #-DisableParallel #-Verbose 26 | 27 | Write-Color 'Jump 1' -Color Green 28 | $TestEvents1 | fl Channel, BackupPath, SubjectDomainName, SubjectUserName, MessageSubject, Message 29 | Write-Color 'Jump 2' -Color Yellow 30 | $TestEvents2 | fl NoNameA0, NoNameA1, MessageSubject, Message 31 | Write-Color 'Jump 3' -Color Green 32 | $TestEvents3 | fl TargetUserName, TargetDomainName, TargetUserSid, MessageSubject, Message 33 | Write-Color 'Jump 4' -Color Green 34 | $TestEvents4 | fl Computer, SubjectUserSid, NewProcessName, ParentProcessName, MessageSubject 35 | Write-Color 'Jump 5' -Color Green 36 | $TestEvents5 | fl NoNameA0, NoNameA1, NoNameA2, MessageSubject 37 | Write-Color 'Jump 6' -Color Green 38 | $TestEvents6 | fl param1, param2, MessageSubject 39 | Write-Color 'Jump 7' -Color Green 40 | $TestEvents7 | fl NoNameB1, NoNameB2, MessageSubject 41 | Write-Color 'Jump 8' -Color Green 42 | $TestEvents8 | fl QueryName, AddressLength, MessageSubject 43 | Write-Color 'Jump 9' -Color Green 44 | $TestEvents9 | fl NoNameA0, NoNameA1, MessageSubject 45 | Write-Color 'Jump 10' -Color Green 46 | $TestEvents10 | fl spn1, spn2, MessageSubject 47 | Write-Color 'End processing events - Tests for expected output' -Color Red -------------------------------------------------------------------------------- /Examples/RunMe-Tests10.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module PSEventViewer -Force 3 | Import-Module PSSharedGoods -Force 4 | 5 | #Get-Events -LogName 'Security' -ID 4624 -MaxEvents 10 -Verbose | ` 6 | # Format-Table Date, Action, Who, ObjectAffected, Computer, IpAddress, IpPort, MachineName,ProviderName, LogonProcessName -AutoSize 7 | 8 | $Output = Get-Events -LogName 'Security' -ID 4624 -MaxEvents 10 -Verbose -Machine 'AD1',AD3,AD2 #-ErrorAction SilentlyContinue -ErrorVariable MyErrors 9 | $Output | ft RecordID, ID, GatheredFrom, GatheredLogName 10 | #$Output[0] | Format-List * #Date, Action, Who, ObjectAffected, Computer, IpAddress, IpPort, MachineName,ProviderName, LogonProcessName 11 | #Get-Events -LogName 'Security' -ID 4624 -MaxEvents 10 -Verbose -Machine 'AD1','AD2' 12 | # | Format-Table Date, Action, Who, ObjectAffected, Computer, IpAddress, IpPort, MachineName,ProviderName, LogonProcessName 13 | 14 | 15 | #$MyErrors.Count 16 | #$MyErrors #.Exception.Message -------------------------------------------------------------------------------- /Examples/RunMe-Tests11.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module PSEventViewer -Force 3 | 4 | Get-Events -LogName 'System' -ID 1001,1018 -ProviderName 'Microsoft-Windows-WER-SystemErrorReporting' -Verbose 5 | Get-Events -LogName 'System' -ID 42,41,109 -ProviderName 'Microsoft-Windows-Kernel-Power' -Verbose 6 | Get-Events -LogName 'System' -ID 1,12,13 -ProviderName 'Microsoft-Windows-Kernel-General' -Verbose 7 | Get-Events -LogName 'System' -ID 6005,6006,6008,6013 -ProviderName 'EventLog' -Verbose 8 | #Get-Events -LogName 'Security' -ID 1001,1018 -ProviderName 'Microsoft-Windows-WER-SystemErrorReporting' -Verbose 9 | #Get-Events -LogName 'Security' -ID 1001,1018 -ProviderName 'Microsoft-Windows-WER-SystemErrorReporting' -Verbose -------------------------------------------------------------------------------- /Examples/RunMe-Tests12.ps1: -------------------------------------------------------------------------------- 1 | #Get-WinEvent -FilterHashTable @{ LogName = 'ForwardedEvents'} -MaxEvents 5 -Verbose 2 | 3 | $XML = @' 4 | 5 | '@ 6 | 7 | Get-WinEvent -FilterXml $XML -MaxEvents 10 8 | 9 | <# 10 | function Test { 11 | [CmdletBinding()] 12 | param( 13 | 14 | ) 15 | $e = Get-WinEvent -LogName 'ForwardedEvents' -MaxEvents 1 -Verbose 16 | $e 17 | 18 | } 19 | 20 | #> 21 | 22 | #. 23 | -------------------------------------------------------------------------------- /Examples/RunMe-Tests13.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module PSSharedGoods -Force 3 | Import-Module ..\PSEventViewer.psd1 -Force 4 | Write-Color "Start 1" -Color Red 5 | $Output1 = Get-Events -LogName 'Setup' -ID 2 -ComputerName 'Evo1' -MaxEvents 1 -Verbose #| Format-Table * 6 | Write-Color 'Start 2' -Color Red 7 | $Output2 = Get-Events -LogName 'Setup' -ComputerName 'Evo1' -MaxEvents 1 -Verbose #| Format-Table * 8 | Write-Color 'Start 3' -Color Blue 9 | $Output3 = Get-Events -LogName 'Security' -ComputerName 'AD1.AD.EVOTEC.XYZ' -ID 4720, 4738, 5136, 5137, 5141, 4722, 4725, 4767, 4723, 4724, 4726, 4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762, 4785, 4786, 4787, 4788, 5136, 5137, 5141, 5136, 5137, 5141, 5136, 5137, 5141 -Verbose 10 | Write-Color 'Start 4' -Color Blue 11 | $List = @() 12 | #$List += @{ Server = 'AD1'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'Computer' } 13 | #$List += @{ Server = 'AD2'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'Computer' } 14 | #$List += @{ Server = 'C:\MyEvents\Archive-Security-2018-08-21-23-49-19-424.evtx'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'File' } 15 | #$List += @{ Server = 'C:\MyEvents\Archive-Security-2018-09-15-09-27-52-679.evtx'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'File' } 16 | #$List += @{ Server = 'Evo1'; LogName = 'Setup'; EventID = 2; Type = 'Computer'; } 17 | $List += @{ Server = 'AD1.ad.evotec.xyz'; LogName = 'Security'; EventID = 4720, 4738, 5136, 5137, 5141, 4722, 4725, 4767, 4723, 4724, 4726, 4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762, 4785, 4786, 4787, 4788, 5136, 5137, 5141, 5136, 5137, 5141, 5136, 5137, 5141; Type = 'Computer' } 18 | #$List += @{ Server = 'Evo1'; LogName = 'Setup'; Type = 'Computer'; } 19 | $Output4 = Get-Events -ExtendedInput $List -Verbose 20 | 21 | Write-Color "End 1" -Color Red 22 | $Output1 | Format-Table -AutoSize 23 | 24 | Write-Color "End 2" -Color Red 25 | $Output2 | Format-Table -AutoSize 26 | 27 | Write-Color 'END 3' -Color Blue 28 | $Output3 | Format-Table -AutoSize 29 | 30 | Write-Color "End 4" -Color Red 31 | $Output4 | Format-Table -AutoSize -------------------------------------------------------------------------------- /Examples/RunMe-Tests14.ps1: -------------------------------------------------------------------------------- 1 | Import-Module ..\PSEventViewer.psd1 -Force 2 | $List = @( 3 | @{ Server = 'AD1'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'Computer' } 4 | @{ Server = 'AD2'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'Computer' } 5 | @{ Server = 'C:\MyEvents\Archive-Security-2018-08-21-23-49-19-424.evtx'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'File' } 6 | @{ Server = 'C:\MyEvents\Archive-Security-2018-09-15-09-27-52-679.evtx'; LogName = 'Security'; EventID = '5136', '5137'; Type = 'File' } 7 | @{ Server = 'Evo1'; LogName = 'Setup'; EventID = 2; Type = 'Computer'; } 8 | @{ Server = 'AD1.ad.evotec.xyz'; LogName = 'Security'; EventID = 4720, 4738, 5136, 5137, 5141, 4722, 4725, 4767, 4723, 4724, 4726, 4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762, 4785, 4786, 4787, 4788, 5136, 5137, 5141, 5136, 5137, 5141, 5136, 5137, 5141; Type = 'Computer' } 9 | @{ Server = 'Evo1'; LogName = 'Security'; Type = 'Computer'; MaxEvents = 15; Keywords = 'AuditSuccess' } 10 | @{ Server = 'Evo1'; LogName = 'Security'; Type = 'Computer'; MaxEvents = 15; Level = 'Informational'; Keywords = 'AuditFailure' } 11 | ) 12 | $Output4 = Get-Events -ExtendedInput $List -Verbose 13 | $Output4 | Format-Table Computer, Date, LevelDisplayName -------------------------------------------------------------------------------- /Examples/RunMe-Tests2.ps1: -------------------------------------------------------------------------------- 1 | # Example old way 2 | #Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 1102 } -ComputerName 'AD1' | Format-List * 3 | 4 | # Example new way 5 | Import-Module PSEventViewer -Force 6 | Get-Events -LogName 'Security' -Id 1102 -ComputerName 'AD1' -Verbose -MaxEvents 5 | Format-List * 7 | Get-Events -LogName 'Setup' -Id 9 -ComputerName 'AD1' -MaxEvents 1 | Format-List * 8 | Get-Events -LogName 'Setup' -Id 2 -ComputerName 'AD1' -MaxEvents 10 | Format-List * -------------------------------------------------------------------------------- /Examples/RunMe-Tests3.ps1: -------------------------------------------------------------------------------- 1 | 2 | # Find out the structure of event 3 | Clear-Host 4 | Get-WinEvent -FilterHashtable @{ LogName = 'Setup'; Id = 2 } -ComputerName 'AD1' -MaxEvents 1 | Format-List * 5 | Get-WinEvent -FilterHashtable @{ LogName = 'Setup'; Id = 2 } -ComputerName 'AD1' -MaxEvents 10 | Format-List Message, TimeCreated, MachineName 6 | Get-WinEvent -FilterHashtable @{ LogName = 'Setup'; Id = 2 } -ComputerName 'AD1' -MaxEvents 10 | Where-Object { $_.Message -like '*KB4103723*' } | Format-List Message, TimeCreated, MachineName 7 | 8 | Clear-Host 9 | Import-Module PSEventViewer -Force 10 | Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 1 | Format-List * 11 | Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 10 | Format-List MachineName, IntendedPackageState, PackageIdentifier, Date, ErrorCode 12 | Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 10 | Where-Object { $_.PackageIdentifier -eq 'KB4103723' } | Format-List MachineName, IntendedPackageState, PackageIdentifier, Date, ErrorCode -------------------------------------------------------------------------------- /Examples/RunMe-Tests4.ps1: -------------------------------------------------------------------------------- 1 | # Following won't work due to more events then 23 2 | Import-Module PSEventViewer -Force 3 | $IDRequiringSplitOver23 = 1102, 5136, 5137, 5141, 4364, 4647, 4672, 4727, 4730, 4731, 4734, 4759, 4760, 4754, 4758, 4728, 4729, 4732, 4733, 4756, 4757, 4761, 4762, 4725, 4722, 4725, 4767, 4723, 4724, 4726, 4740, 4720, 4738 4 | 5 | ### Standard aproach 6 | #Get-WinEvent -FilterHashtable @{ LogName = 'ForwardedEvents'; ID = $IDRequiringSplitOver23 } -Verbose 7 | 8 | ### New approach 9 | Get-Events -Id $IDRequiringSplitOver23 -LogName 'Security' -Verbose -------------------------------------------------------------------------------- /Examples/RunMe-Tests5.ps1: -------------------------------------------------------------------------------- 1 | 2 | Clear-Host 3 | Import-Module PSEventViewer -Force 4 | Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 1 -Verbose | Format-List * 5 | Get-Events -LogName 'Setup' -ID 2 -ComputerName 'AD1' -MaxEvents 1 -Verbose | Format-List * -------------------------------------------------------------------------------- /Examples/RunMe-Tests8.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module PSEventViewer -Force 3 | Get-Events -LogName 'Security' -ID 5379 -RecordID 19626 -Verbose | Format-Table TimeCreated, ProviderName, Id, Message # takes 380 miliseconds 4 | Get-Events -LogName 'Security' -ID 5379 -Verbose | Where { $_.RecordID -eq 19626 } | Format-Table TimeCreated, ProviderName, Id, Message # takes 6 minutes+ -------------------------------------------------------------------------------- /Examples/RunMe-Tests9.ps1: -------------------------------------------------------------------------------- 1 | $Array = @(4728, 4729, 4732, 4733, 4756, 4757, 4761, 4762) 2 | 3 | Clear-Host 4 | Import-Module PSEventViewer -Force 5 | Import-Module PSSharedGoods -Force 6 | 7 | #Get-Events -LogName 'Security' -ID 4624 -MaxEvents 10 -Verbose | ` 8 | # Format-Table Date, Action, Who, ObjectAffected, Computer, IpAddress, IpPort, MachineName,ProviderName, LogonProcessName -AutoSize 9 | 10 | $Output = Get-Events -LogName 'Security' -ID $Array -MaxEvents 10 -Verbose -Machine 'AD1', AD3, AD2 #-ErrorAction SilentlyContinue -ErrorVariable MyErrors 11 | $Output | Format-Table RecordID, ID, GatheredFrom, GatheredLogName 12 | #$Output[0] | Format-List * #Date, Action, Who, ObjectAffected, Computer, IpAddress, IpPort, MachineName,ProviderName, LogonProcessName 13 | #Get-Events -LogName 'Security' -ID 4624 -MaxEvents 10 -Verbose -Machine 'AD1','AD2' 14 | # | Format-Table Date, Action, Who, ObjectAffected, Computer, IpAddress, IpPort, MachineName,ProviderName, LogonProcessName 15 | 16 | 17 | #$MyErrors.Count 18 | #$MyErrors #.Exception.Message -------------------------------------------------------------------------------- /Examples/RunMe-Text XML Output.ps1: -------------------------------------------------------------------------------- 1 | $User = 'Administrator' 2 | $XML = Get-EventsFilter -NamedDataFilter @{'SubjectUserName' = $User; 'TargetUserName' = $User } -LogName 'ForwardedEvents' 3 | 4 | <# Output of command above 5 | 6 | 7 | 10 | 11 | 12 | #> 13 | #$XML 14 | Get-WinEvent -FilterXml $XML -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Evotec 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /PSEventViewer.AzurePipelines.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: Build_PS_Win2016 3 | pool: 4 | vmImage: vs2017-win2016 5 | steps: 6 | - powershell: | 7 | Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck 8 | .\PSEventViewer.Tests.ps1 -Verbose 9 | displayName: 'Run Pester Tests' -------------------------------------------------------------------------------- /PSEventViewer.Tests.ps1: -------------------------------------------------------------------------------- 1 | $PSVersionTable.PSVersion 2 | 3 | $ModuleName = (Get-ChildItem $PSScriptRoot\*.psd1).BaseName 4 | #$ModuleVersion = (Get-Content -Raw $PSScriptRoot\*.psd1) | Invoke-Expression | ForEach-Object ModuleVersion 5 | 6 | #$Dest = "Builds\$ModuleName-{0}-{1}.zip" -f $ModuleVersion, (Get-Date).ToString("yyyyMMddHHmmss") 7 | #Compress-Archive -Path . -DestinationPath .\$dest 8 | 9 | $Pester = (Get-Module -ListAvailable pester) 10 | if ($null -eq $Pester -or ($Pester[0].Version.Major -le 4 -and $Pester[0].Version.Minor -lt 4)) { 11 | Write-Warning "$ModuleName - Downloading Pester from PSGallery" 12 | Install-Module -Name Pester -Repository PSGallery -Force -SkipPublisherCheck -Scope CurrentUser 13 | } 14 | if ($null -eq (Get-Module -ListAvailable PSSharedGoods)) { 15 | Write-Warning "$ModuleName - Downloading PSSharedGoods from PSGallery" 16 | Install-Module -Name PSSharedGoods -Repository PSGallery -Force -Scope CurrentUser 17 | } 18 | 19 | import-module .\PSEventViewer.psd1 -Force 20 | 21 | $result = Invoke-Pester -Path $PSScriptRoot\Tests 22 | 23 | if ($result.FailedCount -gt 0) { 24 | throw "$($result.FailedCount) tests failed." 25 | } -------------------------------------------------------------------------------- /PSEventViewer.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | AliasesToExport = @('Write-Event') 3 | Author = 'Przemyslaw Klys' 4 | CmdletsToExport = @('Find-WinEvent', 'Start-EventWatching', 'Write-WinEvent') 5 | CompanyName = 'Evotec' 6 | CompatiblePSEditions = @('Desktop', 'Core') 7 | Copyright = '(c) 2011 - 2024 Przemyslaw Klys @ Evotec. All rights reserved.' 8 | Description = 'Simple module allowing parsing of event logs. Has its own quirks...' 9 | FunctionsToExport = @('Get-Events', 'Get-EventsFilter', 'Get-EventsInformation', 'Get-EventsSettings', 'Set-EventsSettings') 10 | GUID = '5df72a79-cdf6-4add-b38d-bcacf26fb7bc' 11 | ModuleVersion = '2.4.4' 12 | PowerShellVersion = '5.1' 13 | PrivateData = @{ 14 | PSData = @{ 15 | ExternalModuleDependencies = @('Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Diagnostics') 16 | IconUri = 'https://evotec.xyz/wp-content/uploads/2018/10/PSEventViewer.png' 17 | ProjectUri = 'https://github.com/EvotecIT/PSEventViewer' 18 | Tags = @('Events', 'Viewer', 'Windows', 'XML', 'XPATH', 'EVTX') 19 | } 20 | } 21 | RequiredModules = @(@{ 22 | Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe' 23 | ModuleName = 'PSSharedGoods' 24 | ModuleVersion = '0.0.302' 25 | }, 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Diagnostics') 26 | RootModule = 'PSEventViewer.psm1' 27 | } -------------------------------------------------------------------------------- /Private/Add-ToHashTable.ps1: -------------------------------------------------------------------------------- 1 | function Add-ToHashTable($Hashtable, $Key, $Value) { 2 | <# 3 | .SYNOPSIS 4 | Adds a key-value pair to a hashtable. 5 | 6 | .DESCRIPTION 7 | This function adds a key-value pair to a given hashtable. If the value is not null or empty, it is added to the hashtable. 8 | 9 | .PARAMETER Hashtable 10 | The hashtable to which the key-value pair will be added. 11 | 12 | .PARAMETER Key 13 | The key of the key-value pair to be added. 14 | 15 | .PARAMETER Value 16 | The value of the key-value pair to be added. 17 | 18 | .EXAMPLE 19 | $myHashtable = @{} 20 | Add-ToHashTable -Hashtable $myHashtable -Key "Name" -Value "John" 21 | # Adds the key-value pair "Name"-"John" to $myHashtable. 22 | 23 | .EXAMPLE 24 | $myHashtable = @{} 25 | Add-ToHashTable -Hashtable $myHashtable -Key "Age" -Value 25 26 | # Adds the key-value pair "Age"-25 to $myHashtable. 27 | #> 28 | if ($null -ne $Value -and $Value -ne '') { 29 | $Hashtable.Add($Key, $Value) 30 | } 31 | } -------------------------------------------------------------------------------- /Public/Get-EventsSettings.ps1: -------------------------------------------------------------------------------- 1 | function Get-EventsSettings { 2 | <# 3 | .SYNOPSIS 4 | Get-EventsSettings retrieves information about a specified event log. 5 | 6 | .DESCRIPTION 7 | Get-EventsSettings retrieves detailed information about a specified event log, including log properties and settings. 8 | 9 | .EXAMPLE 10 | Get-EventsSettings -LogName 'Application' 11 | Retrieves information about the 'Application' event log. 12 | 13 | .EXAMPLE 14 | Get-EventsSettings -LogName 'Security' -ComputerName 'Server01' 15 | Retrieves information about the 'Security' event log on the remote computer 'Server01'. 16 | 17 | #> 18 | [cmdletBinding()] 19 | param( 20 | [parameter(Mandatory)][string] $LogName, 21 | [string] $ComputerName 22 | ) 23 | try { 24 | if ($ComputerName) { 25 | $Log = Get-WinEvent -ListLog $LogName -ErrorAction Stop 26 | } else { 27 | $Log = Get-WinEvent -ListLog $LogName -ComputerName $ComputerName -ErrorAction Stop 28 | } 29 | } catch { 30 | if ($ErrorActionPreference -eq 'Stop') { 31 | throw 32 | } else { 33 | Write-Warning -Message "Get-EventsSettings - Error occured during reading of event log $LogName - $($_.Exception.Message)" 34 | return 35 | } 36 | } 37 | if ($Log.LogMode -eq 'AutoBackup') { 38 | $EventAction = 'ArchiveTheLogWhenFullDoNotOverwrite' 39 | } elseif ($Log.LogMode -eq 'Circular') { 40 | $EventAction = 'OverwriteEventsAsNeededOldestFirst' 41 | } elseif ($Log.LogMode -eq 'Retain') { 42 | $EventAction = 'DoNotOverwriteEventsClearLogManually' 43 | } else { 44 | $EventAction = 'Unknown' 45 | } 46 | [PSCustomObject] @{ 47 | EventAction = $EventAction 48 | LogName = $Log.LogName # #: Application 49 | LogType = $Log.LogType # #: Administrative 50 | LogMode = $Log.LogMode # #: Circular 51 | FileSize = $Log.FileSize # #: 69632 52 | FileSizeMB = Convert-Size -Value $Log.FileSize -From Bytes -To MB -Precision 2 53 | MaximumSizeInBytes = $Log.MaximumSizeInBytes # #: 2105344 54 | MaximumSizeinMB = Convert-Size -Value $Log.MaximumSizeInBytes -From Bytes -To MB -Precision 2 55 | IsLogFull = $Log.IsLogFull # #: False 56 | LogFilePath = $Log.LogFilePath # #: % SystemRoot % \System32\Winevt\Logs\Application.evtx 57 | LastAccessTime = $Log.LastAccessTime # #: 25.05.2022 12:32:29 58 | LastWriteTime = $Log.LastWriteTime # #: 25.05.2022 12:22:33 59 | OldestRecordNumber = $Log.OldestRecordNumber # #: 1 60 | RecordCount = $Log.RecordCount # #: 11 61 | LogIsolation = $Log.LogIsolation # #: Application 62 | IsEnabled = $Log.IsEnabled # #: True 63 | IsClassicLog = $Log.IsClassicLog # #: True 64 | SecurityDescriptor = $Log.SecurityDescriptor # #: O:BAG:SYD:(A; ; 0x2; ; ; S - 1 - 15 - 2 - 1)(A; ; 0x2; ; ; S - 1 - 15 - 3 - 1024 - 3153509613 - 960666767 - 3724611135 - 2725662640 - 12138253 - 543910227 - 1950414635 - 4190290187)(A; ; 0xf0007; ; ; SY)(A; ; 0x7; ; ; BA)(A; ; 0x7; ; ; SO)(A; ; 0x3; ; ; IU)(A; ; 0x3; ; ; SU)(A; ; 0x3; ; ; S - 1 - 5 - 3)(A; ; 0x3; ; ; S - 1 - 5 - 33)(A; ; 0x1; ; ; S - 1 - 5 - 32$Log. - 57 65 | OwningProviderName = $Log.OwningProviderName # #: 66 | ProviderNames = $Log.ProviderNames # #: { .NET Runtime, .NET Runtime Optimization Service, Application, Application Error… } 67 | ProviderLevel = $Log.ProviderLevel # #: 68 | ProviderKeywords = $Log.ProviderKeywords # #: 69 | ProviderBufferSize = $Log.ProviderBufferSize # #: 64 70 | ProviderMinimumNumberOfBuffers = $Log.ProviderMinimumNumberOfBuffers # #: 0 71 | ProviderMaximumNumberOfBuffers = $Log.ProviderMaximumNumberOfBuffers # #: 64 72 | ProviderLatency = $Log.ProviderLatency # #: 1000 73 | ProviderControlGuid = $Log.ProviderControlGuid # #: 74 | } 75 | } -------------------------------------------------------------------------------- /Public/Set-EventsSettings.ps1: -------------------------------------------------------------------------------- 1 | function Set-EventsSettings { 2 | <# 3 | .SYNOPSIS 4 | Set-EventsSettings updates the settings of a specified event log. 5 | 6 | .DESCRIPTION 7 | Set-EventsSettings allows you to modify various settings of a specified event log, such as log mode, maximum size, and event action. 8 | 9 | .EXAMPLE 10 | Set-EventsSettings -LogName 'Application' -EventAction 'ArchiveTheLogWhenFullDoNotOverwrite' 11 | Updates the 'Application' event log to archive the log when full without overwriting. 12 | 13 | .EXAMPLE 14 | Set-EventsSettings -LogName 'Security' -MaximumSizeMB 100 -Mode Circular 15 | Sets the 'Security' event log to have a maximum size of 100 MB and log mode as Circular. 16 | #> 17 | [cmdletBinding(SupportsShouldProcess)] 18 | param( 19 | [Parameter(Mandatory)][string] $LogName, 20 | [string] $ComputerName, 21 | [int] $MaximumSizeMB, 22 | [int] $MaximumSizeInBytes, 23 | [ValidateSet('OverwriteEventsAsNeededOldestFirst', 'ArchiveTheLogWhenFullDoNotOverwrite', 'DoNotOverwriteEventsClearLogManually')][string] $EventAction, 24 | [alias('LogMode')][System.Diagnostics.Eventing.Reader.EventLogMode] $Mode 25 | ) 26 | 27 | $TranslateEventAction = @{ 28 | 'OverwriteEventsAsNeededOldestFirst' = [System.Diagnostics.Eventing.Reader.EventLogMode]::Circular 29 | 'ArchiveTheLogWhenFullDoNotOverwrite' = [System.Diagnostics.Eventing.Reader.EventLogMode]::AutoBackup 30 | 'DoNotOverwriteEventsClearLogManually' = [System.Diagnostics.Eventing.Reader.EventLogMode]::Retain 31 | } 32 | 33 | try { 34 | if ($ComputerName) { 35 | $Log = Get-WinEvent -ListLog $LogName -ErrorAction Stop 36 | } else { 37 | $Log = Get-WinEvent -ListLog $LogName -ComputerName $ComputerName -ErrorAction Stop 38 | } 39 | } catch { 40 | if ($ErrorActionPreference -eq 'Stop') { 41 | throw 42 | } else { 43 | Write-Warning -Message "Set-EventsSettings - Error occured during reading $LogName log - $($_.Exception.Message)" 44 | return 45 | } 46 | } 47 | if ($PSBoundParameters.ContainsKey('EventAction')) { 48 | $Log.LogMode = $TranslateEventAction[$EventAction] 49 | } 50 | if ($PSBoundParameters.ContainsKey('Mode')) { 51 | $Log.LogMode = $Mode 52 | } 53 | if ($PSBoundParameters.ContainsKey('MaximumSizeMB')) { 54 | $MaxSize = $MaximumSizeMB * 1MB 55 | $Log.MaximumSizeInBytes = $MaxSize 56 | } 57 | if ($PSBoundParameters.ContainsKey('MaximumSizeInBytes')) { 58 | $Log.MaximumSizeInBytes = $MaximumSizeInBytes 59 | } 60 | if ($PSCmdlet.ShouldProcess($LogName, "Saving event log settings")) { 61 | try { 62 | $Log.SaveChanges() 63 | } catch { 64 | if ($ErrorActionPreference -eq 'Stop') { 65 | throw 66 | } else { 67 | Write-Warning -Message "Set-EventsSettings - Error occured during saving of changes for $LogName log - $($_.Exception.Message)" 68 | return 69 | } 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/EventViewerX.Examples.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | latest 9 | true 10 | windows 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.EventWatchingBasic.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Examples { 2 | internal partial class Examples { 3 | 4 | public static void EventWatchingBasic() { 5 | WatchEvents c1 = new WatchEvents { 6 | Warning = true, 7 | Verbose = true 8 | }; 9 | c1.Watch("AD1", "Security", new List() { 4627, 4624 }); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.FindBasic.cs: -------------------------------------------------------------------------------- 1 | using EventViewerX.Rules.ActiveDirectory; 2 | using EventViewerX.Rules.Windows; 3 | 4 | namespace EventViewerX.Examples { 5 | internal partial class Examples { 6 | public static void FindEventsTargetedBasic() { 7 | 8 | foreach (var foundObject in SearchEvents.FindEventsByNamedEvents([NamedEvents.OSCrash, NamedEvents.OSStartupShutdownCrash])) { 9 | 10 | Console.WriteLine("Event ID: {0}", foundObject.EventID + ", " + foundObject.Type + " " + foundObject.GatheredFrom); 11 | Console.WriteLine("Type: " + foundObject.Type + ", " + foundObject.EventID + " " + foundObject.EventID + " " + foundObject.GatheredFrom); 12 | 13 | if (foundObject is OSCrash osCrash) { 14 | //Display the properties of the ADComputerChangeDetailed object 15 | Console.WriteLine("[*] Computer: " + osCrash.Computer); 16 | Console.WriteLine("[*] Who: " + osCrash.Who); 17 | Console.WriteLine("[*] When: " + osCrash.When); 18 | } else if (foundObject is OSStartupShutdownCrash osStartupShutdownCrash) { 19 | //Display the properties of the ADComputerChangeDetailed object 20 | Console.WriteLine("[*] Computer: " + osStartupShutdownCrash.Computer); 21 | Console.WriteLine("[*] Who: " + osStartupShutdownCrash.Action); 22 | Console.WriteLine("[*] When: " + osStartupShutdownCrash.When); 23 | } 24 | } 25 | } 26 | 27 | public static void FindEventsTargetedPerType() { 28 | List MachineName = new List { "AD1", "AD2", "AD0" }; 29 | 30 | // Initialize the logger 31 | var internalLogger = new InternalLogger(true); 32 | 33 | SearchEvents eventSearching = new SearchEvents(internalLogger); 34 | eventSearching.Verbose = true; 35 | 36 | List Type = new List { NamedEvents.ADLdapBindingDetails, NamedEvents.ADLdapBindingSummary }; 37 | foreach (var foundObject in SearchEvents.FindEventsByNamedEvents(Type, MachineName)) { 38 | // Check if the foundObject is of type ADComputerChangeDetailed 39 | 40 | // Console.WriteLine("Event ID: {0}", foundObject.EventID + ", " + foundObject.Type + " " + foundObject.GatheredFrom); 41 | Console.WriteLine("Type: " + foundObject.Type + ", " + foundObject.EventID + " " + foundObject.EventID + " " + foundObject.GatheredFrom); 42 | 43 | if (foundObject is ADComputerChangeDetailed adComputerChange) { 44 | // Display the properties of the ADComputerChangeDetailed object 45 | Console.WriteLine("[*] Computer: " + adComputerChange.Computer); 46 | Console.WriteLine("[*] Action: " + adComputerChange.Action); 47 | Console.WriteLine("[*] Operation Type: " + adComputerChange.OperationType); 48 | Console.WriteLine("[*] Who: " + adComputerChange.Who); 49 | Console.WriteLine("[*] When: " + adComputerChange.When); 50 | Console.WriteLine("[*] Object DN: " + adComputerChange.ComputerObject); 51 | Console.WriteLine("[*] Field Changed: " + adComputerChange.FieldChanged); 52 | Console.WriteLine("[*] Field Value: " + adComputerChange.FieldValue); 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.QueryBasic.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Examples { 2 | internal partial class Examples { 3 | 4 | public static void QueryBasic() { 5 | SearchEvents.QueryLog("Application", [1008, 4098, 1001], "AD1"); 6 | SearchEvents.QueryLog("Application", [1001], "AD1"); 7 | SearchEvents.QueryLog("Security", new List() { 4627, 4624 }, "AD1"); 8 | } 9 | 10 | public static void QueryBasicForwardedEvents() { 11 | var list = SearchEvents.QueryLog("ForwardedEvents", [4722, 4738]); 12 | foreach (var test in list) { 13 | Console.WriteLine(test.Id + " " + test.QueriedMachine + " " + test.MachineName + " " + test.ContainerLog + " " + test.LogName); 14 | } 15 | Console.WriteLine("Count: " + list.Count()); 16 | } 17 | 18 | public static void QueryBasicWithOutput() { 19 | foreach (var eventObject in SearchEvents.QueryLog("Security", [4932, 4933], "AD1")) { 20 | Console.WriteLine("Event ID: {0}", eventObject.Id); 21 | Console.WriteLine("Data count: " + eventObject.Data.Count); 22 | foreach (var data in eventObject.Data) { 23 | Console.WriteLine("[-] Data: {0} - {1}", data.Key, data.Value); 24 | } 25 | } 26 | } 27 | 28 | public static void QueryBasicParallelForEach() { 29 | foreach (var eventObject in SearchEvents.QueryLogsParallelForEach("Security", [4932, 4933], ["AD1"])) { 30 | Console.WriteLine("Event ID: {0}", eventObject.Id); 31 | Console.WriteLine("Data count: " + eventObject.Data.Count); 32 | foreach (var data in eventObject.Data) { 33 | Console.WriteLine("[-] Data: {0} - {1}", data.Key, data.Value); 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.QueryEventSettings.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Examples { 2 | internal partial class Examples { 3 | 4 | public static void QueryBasicEventLogList() { 5 | 6 | SearchEvents eventLogSettings = new SearchEvents(); 7 | eventLogSettings.Verbose = true; 8 | eventLogSettings.Warning = true; 9 | eventLogSettings.Error = true; 10 | 11 | foreach (var test in SearchEvents.DisplayEventLogs()) { 12 | Console.WriteLine(test); 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.QueryParallelSpeed.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace EventViewerX.Examples { 4 | internal partial class Examples { 5 | 6 | public static void QueryParallelSpeed() { 7 | var eventSearching = new SearchEvents { 8 | Verbose = true, 9 | Warning = true, 10 | Error = true, 11 | Debug = true, 12 | NumberOfThreads = 1, 13 | }; 14 | 15 | var machineNames = new List { "AD1", "AD2", "AD3" }; // Add your machine names here 16 | var eventIds = new List { 4932, 4933 }; // Add your event IDs here 17 | 18 | Parallel.ForEach(machineNames, machine => { 19 | foreach (var eventObject in SearchEvents.QueryLog("Security", eventIds, machine)) { 20 | 21 | } 22 | }); 23 | } 24 | 25 | public static void QueryParallelCompare() { 26 | var eventSearching = new SearchEvents { 27 | Verbose = true, 28 | Warning = true, 29 | Error = true, 30 | Debug = true 31 | }; 32 | 33 | var machineNames = new List { "AD1", "AD2", "AD3" }; // Add your machine names here 34 | var eventIds = new List { 4932, 4933 }; // Add your event IDs here 35 | 36 | var stopwatch = Stopwatch.StartNew(); 37 | int eventCount1 = 0; 38 | Parallel.ForEach(machineNames, machine => { 39 | foreach (var eventObject in SearchEvents.QueryLog("Security", eventIds, machine)) { 40 | eventCount1++; 41 | } 42 | }); 43 | stopwatch.Stop(); 44 | Console.WriteLine($"Parallel.ForEach method took {stopwatch.ElapsedMilliseconds} ms and returned {eventCount1} events."); 45 | 46 | stopwatch.Restart(); 47 | int eventCount2 = 0; 48 | foreach (var eventObject in SearchEvents.QueryLogsParallel("Security", eventIds, machineNames)) { 49 | eventCount2++; 50 | } 51 | stopwatch.Stop(); 52 | Console.WriteLine($"QueryLogsParallel method took {stopwatch.ElapsedMilliseconds} ms and returned {eventCount2} events."); 53 | 54 | stopwatch.Restart(); 55 | int eventCount3 = 0; 56 | foreach (var eventObject in SearchEvents.QueryLogsParallelForEach("Security", eventIds, machineNames)) { 57 | eventCount3++; 58 | } 59 | stopwatch.Stop(); 60 | Console.WriteLine($"QueryBasicParallelForEach method took {stopwatch.ElapsedMilliseconds} ms and returned {eventCount3} events."); 61 | 62 | if (eventCount1 == eventCount2 && eventCount2 == eventCount3) { 63 | Console.WriteLine("All methods returned the same number of events."); 64 | } else { 65 | Console.WriteLine("The methods returned a different number of events."); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.QueryParallelWithCount.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Examples { 2 | internal partial class Examples { 3 | 4 | public static void QueryParallelWithCount() { 5 | var eventSearching = new SearchEvents { 6 | Verbose = true, 7 | Warning = true, 8 | Error = true, 9 | Debug = true, 10 | NumberOfThreads = 1, 11 | }; 12 | 13 | var machineNames = new List { "AD1", "AD2", "AD3" }; // Add your machine names here 14 | var eventIds = new List { 4932, 4933 }; // Add your event IDs here 15 | 16 | // Initialize a dictionary to keep track of the number of events per server 17 | var eventCounts = new Dictionary(); 18 | 19 | foreach (var eventObject in SearchEvents.QueryLogsParallel("Security", eventIds, machineNames)) { 20 | // If the server is not yet in the dictionary, add it with a count of 1 21 | if (!eventCounts.ContainsKey(eventObject.ComputerName)) { 22 | eventCounts[eventObject.ComputerName] = 1; 23 | } 24 | // If the server is already in the dictionary, increment its count 25 | else { 26 | eventCounts[eventObject.ComputerName]++; 27 | } 28 | 29 | // Print an update every 2000 events 30 | if (eventCounts[eventObject.ComputerName] % 2000 == 0) { 31 | Console.WriteLine("Server: {0}, Event Count: {1}", eventObject.ComputerName, eventCounts[eventObject.ComputerName]); 32 | } 33 | } 34 | 35 | // Print the final number of events per server 36 | foreach (var pair in eventCounts) { 37 | Console.WriteLine("Server: {0}, Final Event Count: {1}", pair.Key, pair.Value); 38 | } 39 | 40 | 41 | Console.WriteLine("Press any key to continue..."); 42 | Console.ReadLine(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Examples.WriteEvents.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace EventViewerX.Examples { 4 | internal partial class Examples { 5 | 6 | public static void WriteBasic() { 7 | 8 | SearchEvents search = new SearchEvents(); 9 | search.Verbose = true; 10 | search.Warning = true; 11 | search.Error = true; 12 | 13 | SearchEvents.WriteEvent("MySource", "Application", "This is a test message", EventLogEntryType.Information, 101, "AD1", "Replacement string 1", "Replacement string 2"); 14 | } 15 | 16 | public static void WriteToDifferentLog() { 17 | SearchEvents.CreateLogSource("MyApplication", "MyCustomLog"); 18 | SearchEvents.WriteEvent("MySource", "MyApplication", "This is a test message", EventLogEntryType.Information, 101, null, "Replacement string 1", "Replacement string 2"); 19 | } 20 | 21 | public static void WriteAdvanced() { 22 | SearchEvents search = new SearchEvents(); 23 | search.Verbose = true; 24 | search.Warning = true; 25 | search.Error = true; 26 | 27 | // SearchEvents.WriteEvent("System", "RetailDemo", "is is a test message"); 28 | // SearchEvents.WriteEvent("System", "WinLogon", "is is a test message"); 29 | SearchEvents.WriteEventEx( 30 | "log", "serviceName", "Authentication started.", 31 | 2, 0, 1, 16, 4, 1, 0x4000200000010000); 32 | 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Sources/EventViewerX.Examples/Program.cs: -------------------------------------------------------------------------------- 1 | using EventViewerX.Examples; 2 | 3 | //Examples.EventWatchingBasic(); 4 | //Examples.QueryBasic(); 5 | //Examples.QueryBasicWithOutput(); 6 | //Examples.QueryParallelSpeed(); 7 | //Examples.QueryParallelWithCount(); 8 | 9 | //Examples.QueryParallelCompare(); 10 | //Examples.QueryBasicParallelForEach(); 11 | //Examples.QueryBasicForwardedEvents(); 12 | 13 | //Examples.FindEventsTargetedBasic(); 14 | //Examples.FindEventsTargetedPerType(); 15 | 16 | //Examples.QueryWithTasks(); 17 | 18 | //Examples.FindEventsTargetedPerType(); 19 | 20 | //Examples.QueryBasicEventLogList(); 21 | //Examples.WriteBasic(); 22 | Examples.WriteAdvanced(); 23 | 24 | //Examples.GetProviders(); -------------------------------------------------------------------------------- /Sources/EventViewerX.Tests/EventViewerX.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | latest 11 | true 12 | windows 13 | 14 | 15 | 16 | 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Sources/EventViewerX.Tests/TestSearchingEvents.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace EventViewerX.Tests { 4 | public class TestSearchingEvents { 5 | [Fact] 6 | public void QuerySecurityEvents4932and4933() { 7 | var fields = new List() { "DestinationDRA", "SourceDRA", "NamingContext", "Options", "SessionID", "EndUSN", "StatusCode", "StartUSN" }; 8 | foreach (var eventObject in SearchEvents.QueryLog("Security", [4932, 4933], "AD1")) { 9 | Assert.True(eventObject.Id == 4932 || eventObject.Id == 4933); 10 | Assert.True(eventObject.Data.Count == 6 || eventObject.Data.Count == 7); 11 | 12 | foreach (var data in eventObject.Data) { 13 | Assert.Contains(data.Key, fields); 14 | } 15 | } 16 | } 17 | [Fact] 18 | public void QuerySetupEventID2() { 19 | var fields = new List() { "PackageIdentifier" }; 20 | foreach (var eventObject in SearchEvents.QueryLog("Setup", [2])) { 21 | Assert.True(eventObject.Id == 2); 22 | Assert.True(eventObject.Data.Count == 5); 23 | 24 | foreach (var field in fields) { 25 | Assert.Contains(field, eventObject.Data.Keys); 26 | } 27 | 28 | } 29 | } 30 | [Fact] 31 | public void QuerySetupEventID1() { 32 | var fields = new List() { "PackageIdentifier", "InitialPackageState" }; 33 | foreach (var eventObject in SearchEvents.QueryLog("Setup", [1])) { 34 | Assert.True(eventObject.Id == 1); 35 | Assert.True(eventObject.Data.Count == 6); 36 | 37 | foreach (var field in fields) { 38 | Assert.Contains(field, eventObject.Data.Keys); 39 | } 40 | 41 | } 42 | } 43 | [Fact] 44 | public void QuerySystemEventID566() { 45 | var fields = new List() { "BootId", "Reason", "MonitorReason" }; 46 | foreach (var eventObject in SearchEvents.QueryLog("System", [566])) { 47 | Assert.True(eventObject.Id == 566); 48 | Assert.True(eventObject.Data.Count == 13); 49 | 50 | foreach (var field in fields) { 51 | Assert.Contains(field, eventObject.Data.Keys); 52 | } 53 | 54 | } 55 | } 56 | 57 | [Fact] 58 | public void QueryApplicationEvent10005() { 59 | var fields = new List() { "RmSessionId", "nApplications", "RebootReasons", "Applications" }; 60 | foreach (var eventObject in SearchEvents.QueryLog("Application", [10005])) { 61 | Assert.True(eventObject.Id == 10005); 62 | 63 | foreach (var field in fields) { 64 | Assert.Contains(field, eventObject.Data.Keys); 65 | } 66 | 67 | } 68 | } 69 | [Fact] 70 | public void QueryApplicationEvent100() { 71 | var fields = new List() { "NoNameA0" }; 72 | foreach (var eventObject in SearchEvents.QueryLog("Application", [100])) { 73 | Assert.True(eventObject.Id == 100); 74 | 75 | foreach (var field in fields) { 76 | Assert.Contains(field, eventObject.Data.Keys); 77 | } 78 | 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Sources/EventViewerX.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34525.116 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventViewerX", "EventViewerX\EventViewerX.csproj", "{11312B6F-B3D4-499B-B5CB-A076C17959D6}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventViewerX.Examples", "EventViewerX.Examples\EventViewerX.Examples.csproj", "{E47BF385-95EE-46B5-8711-A84615B503C4}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {11312B6F-B3D4-499B-B5CB-A076C17959D6} = {11312B6F-B3D4-499B-B5CB-A076C17959D6} 11 | EndProjectSection 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSEventViewer", "PSEventViewer\PSEventViewer.csproj", "{232A863B-DD04-4A96-B071-0CA976B79EFF}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventViewerX.Tests", "EventViewerX.Tests\EventViewerX.Tests.csproj", "{138A9517-CA02-4C1A-AC02-DA895E646476}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA4BCF9A-8C83-485D-A881-C36B1DFA67F8}" 18 | ProjectSection(SolutionItems) = preProject 19 | test.cpp = test.cpp 20 | EndProjectSection 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {11312B6F-B3D4-499B-B5CB-A076C17959D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {11312B6F-B3D4-499B-B5CB-A076C17959D6}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {11312B6F-B3D4-499B-B5CB-A076C17959D6}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {11312B6F-B3D4-499B-B5CB-A076C17959D6}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {E47BF385-95EE-46B5-8711-A84615B503C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {E47BF385-95EE-46B5-8711-A84615B503C4}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {E47BF385-95EE-46B5-8711-A84615B503C4}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {E47BF385-95EE-46B5-8711-A84615B503C4}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {232A863B-DD04-4A96-B071-0CA976B79EFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {232A863B-DD04-4A96-B071-0CA976B79EFF}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {232A863B-DD04-4A96-B071-0CA976B79EFF}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {232A863B-DD04-4A96-B071-0CA976B79EFF}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {138A9517-CA02-4C1A-AC02-DA895E646476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {138A9517-CA02-4C1A-AC02-DA895E646476}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {138A9517-CA02-4C1A-AC02-DA895E646476}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {138A9517-CA02-4C1A-AC02-DA895E646476}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {636CEB4B-F5EF-4657-ADE2-D35A7A66F59D} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /Sources/EventViewerX/BinaryWrappers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace EventViewerX { 7 | public class BinaryWrappers { 8 | 9 | public static void ImportManifest(string manifestPath) { 10 | 11 | //string manifestPath = @"C:\path\to\your\manifest.man"; 12 | 13 | ProcessStartInfo startInfo = new ProcessStartInfo { 14 | FileName = "wevtutil.exe", 15 | Arguments = $"im {manifestPath}", 16 | UseShellExecute = false, 17 | RedirectStandardOutput = true, 18 | CreateNoWindow = true 19 | }; 20 | 21 | Process process = new Process { StartInfo = startInfo }; 22 | process.Start(); 23 | 24 | string output = process.StandardOutput.ReadToEnd(); 25 | process.WaitForExit(); 26 | 27 | if (process.ExitCode == 0) { 28 | Console.WriteLine("Manifest imported successfully."); 29 | } else { 30 | Console.WriteLine($"Failed to import manifest. Exit code: {process.ExitCode}"); 31 | Console.WriteLine($"Output: {output}"); 32 | } 33 | } 34 | 35 | 36 | public static void ConvertXMLtoMAN(string xmlPath, string manPath, string mcPath = null) { 37 | //string xmlPath = @"C:\path\to\your\manifest.xml"; 38 | //string manPath = @"C:\path\to\output\manifest.man"; 39 | 40 | if (mcPath == null) { 41 | string windowsKitsPath = @"C:\Program Files (x86)\Windows Kits"; 42 | string mcExeName = "mc.exe"; 43 | 44 | // Find the mc.exe path 45 | mcPath = Directory.EnumerateDirectories(windowsKitsPath) 46 | .SelectMany(kitPath => Directory.EnumerateDirectories(Path.Combine(kitPath, "bin"))) 47 | .SelectMany(binPath => Directory.EnumerateFiles(binPath, mcExeName, SearchOption.AllDirectories)) 48 | .FirstOrDefault(); 49 | } 50 | if (mcPath == null || !File.Exists(mcPath)) { 51 | Console.WriteLine("`mc.exe` is not found in the specified paths."); 52 | return; 53 | } 54 | 55 | ProcessStartInfo startInfo = new ProcessStartInfo { 56 | FileName = mcPath, 57 | Arguments = $"-um {xmlPath}", 58 | UseShellExecute = false, 59 | RedirectStandardOutput = true, 60 | CreateNoWindow = true 61 | }; 62 | 63 | try { 64 | Process process = Process.Start(startInfo); 65 | if (process == null) { 66 | Console.WriteLine("`mc.exe` is not available."); 67 | return; 68 | } 69 | 70 | string output = process.StandardOutput.ReadToEnd(); 71 | process.WaitForExit(); 72 | 73 | if (process.ExitCode == 0) { 74 | Console.WriteLine("Manifest compiled successfully."); 75 | } else { 76 | Console.WriteLine($"Failed to compile manifest. Exit code: {process.ExitCode}"); 77 | Console.WriteLine($"Output: {output}"); 78 | } 79 | } catch (System.ComponentModel.Win32Exception) { 80 | Console.WriteLine("`mc.exe` is not available."); 81 | } 82 | } 83 | 84 | 85 | public static void GetProvidersResults() { 86 | // Get the list of providers 87 | string[] providers = GetProviders(); 88 | 89 | // For each provider, get its metadata and print its name, GUID, and log names 90 | foreach (string provider in providers) { 91 | string metadata = GetProviderMetadata(provider); 92 | string name = GetMetadataProperty(metadata, "name"); 93 | string guid = GetMetadataProperty(metadata, "guid"); 94 | string[] logNames = GetMetadataProperty(metadata, "channelReferences").Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); 95 | Console.WriteLine($"Provider Name: {name}, Provider GUID: {guid}, Log Names: {string.Join(", ", logNames)}"); 96 | } 97 | } 98 | 99 | private static string[] GetProviders() { 100 | return RunCommand("wevtutil.exe", "ep").Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); 101 | } 102 | 103 | private static string GetProviderMetadata(string provider) { 104 | return RunCommand("wevtutil.exe", $"gp \"{provider}\""); 105 | } 106 | 107 | private static string GetMetadataProperty(string metadata, string propertyName) { 108 | string marker = propertyName + ":"; 109 | int startIndex = metadata.IndexOf(marker); 110 | if (startIndex == -1) { 111 | return null; 112 | } 113 | 114 | startIndex += marker.Length; 115 | int endIndex = metadata.IndexOf('\n', startIndex); 116 | if (endIndex == -1) { 117 | endIndex = metadata.Length; 118 | } 119 | 120 | return metadata.Substring(startIndex, endIndex - startIndex).Trim(); 121 | } 122 | 123 | private static string RunCommand(string fileName, string arguments) { 124 | ProcessStartInfo startInfo = new ProcessStartInfo { 125 | FileName = fileName, 126 | Arguments = arguments, 127 | UseShellExecute = false, 128 | RedirectStandardOutput = true, 129 | CreateNoWindow = true 130 | }; 131 | 132 | using (Process process = Process.Start(startInfo)) { 133 | string output = process.StandardOutput.ReadToEnd(); 134 | process.WaitForExit(); 135 | return output; 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Definitions/Events.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | public enum ImpersonationLevel { 4 | Identification = 1832, 5 | Impersonation = 1833, 6 | Delegation = 1840, 7 | DeniedByProcessTrustLabelACE = 1841, 8 | Yes = 1842, 9 | No = 1843, 10 | System = 1844, 11 | NotAvailable = 1845, 12 | Default = 1846, 13 | DisallowMmConfig = 1847, 14 | Off = 1848, 15 | Auto = 1849 16 | } 17 | 18 | public enum VirtualAccount { 19 | Yes = 1843, 20 | No = 1844 21 | } 22 | 23 | public enum ElevatedToken { 24 | Yes = 1842, 25 | No = 1843 26 | } 27 | 28 | public enum LogonType { 29 | Interactive = 2, 30 | Network = 3, 31 | Batch = 4, 32 | Service = 5, 33 | Unlock = 7, 34 | NetworkCleartext = 8, 35 | NewCredentials = 9, 36 | RemoteInteractive = 10, 37 | CachedInteractive = 11 38 | } 39 | 40 | public enum TicketOptions { 41 | // Add appropriate values here 42 | } 43 | 44 | public enum Status { 45 | // Add appropriate values here 46 | } 47 | 48 | public enum TicketEncryptionType { 49 | DES_CBC_CRC = 0x1, 50 | DES_CBC_MD5 = 0x3, 51 | AES128_CTS_HMAC_SHA1_96 = 0x11, 52 | AES256_CTS_HMAC_SHA1_96 = 0x12, 53 | RC4_HMAC = 0x17, 54 | RC4_HMAC_EXP = 0x18, 55 | AuditFailure = unchecked((int)0xFFFFFFFF) 56 | } 57 | 58 | public enum PreAuthType { 59 | // Add appropriate values here 60 | } 61 | 62 | public enum FailureReason { 63 | None = 0, 64 | // Common failure reasons (prefixed with %%) 65 | UnknownUserNameOrBadPassword = 2304, 66 | AccountRestrictions = 2305, 67 | AccountLockedOut = 2307, 68 | AccountExpired = 2306, 69 | LogonTypeNotGranted = 2309, 70 | PasswordExpired = 2308 71 | } 72 | 73 | public enum StatusCode : uint { 74 | None = 0x0, 75 | // NTSTATUS codes for logon failures 76 | StatusLogonFailure = 0xC000006D, // Unknown username or bad password 77 | StatusAccountRestriction = 0xC000006E, // User account restrictions 78 | StatusInvalidLogonHours = 0xC000006F, // Invalid logon hours 79 | StatusInvalidWorkstation = 0xC0000070, // Invalid workstation 80 | StatusPasswordExpired = 0xC0000071, // Password expired 81 | StatusAccountDisabled = 0xC0000072, // Account disabled 82 | StatusAccountLockedOut = 0xC0000234, // Account locked out 83 | StatusAccountExpired = 0xC0000193, // Account expired 84 | StatusLogonTypeNotGranted = 0xC000015B, // Logon type not granted 85 | StatusNoTrust = 0xC000005E // No trust SAM account 86 | } 87 | 88 | public enum SubStatusCode : uint { 89 | None = 0x0, 90 | // Add any specific substatus codes as they are discovered 91 | StatusInvalidLogonHours = 0xC000006F, 92 | StatusInvalidWorkstation = 0xC0000070, 93 | StatusPasswordExpired = 0xC0000071, 94 | StatusAccountDisabled = 0xC0000072 95 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Definitions/GroupPolicy.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | /// 4 | /// Represents a single Group Policy with its name, ID, and domain. 5 | /// 6 | public class GroupPolicy { 7 | public string GpoName { get; set; } 8 | public string GpoId { get; set; } 9 | public string GpoDomain { get; set; } 10 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Definitions/GroupPolicyLinks.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | /// 4 | /// Represents a single Group Policy link with its display name, GUID, and distinguished name. 5 | /// 6 | public class GroupPolicyLinks { 7 | public string DisplayName { get; set; } = string.Empty; 8 | public string Guid { get; set; } = string.Empty; 9 | public string DistinguishedName { get; set; } = string.Empty; 10 | public bool IsEnabled { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Definitions/Keywords.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | public enum Keywords : long { 3 | AuditFailure = (long)4503599627370496, 4 | AuditSuccess = (long)9007199254740992, 5 | CorrelationHint2 = (long)18014398509481984, 6 | EventLogClassic = (long)36028797018963968, 7 | Sqm = (long)2251799813685248, 8 | WdiDiagnostic = (long)1125899906842624, 9 | WdiContext = (long)562949953421312, 10 | ResponseTime = (long)281474976710656, 11 | None = (long)0 12 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Definitions/Level.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | public enum Level { 4 | Verbose = 5, 5 | Informational = 4, 6 | Warning = 3, 7 | Error = 2, 8 | Critical = 1, 9 | LogAlways = 0 10 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Definitions/ParallelOption.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | public enum ParallelOption { 4 | Disabled, 5 | Parallel 6 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/EventLogDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Eventing.Reader; 2 | 3 | namespace EventViewerX; 4 | 5 | public class EventLogDetails { 6 | public string MachineName { get; set; } 7 | public string LogName { get; set; } 8 | public string LogType { get; set; } 9 | public EventLogIsolation LogIsolation { get; set; } 10 | public bool IsEnabled { get; set; } 11 | public bool? IsLogFull { get; set; } 12 | public long MaximumSizeInBytes { get; set; } 13 | public string LogFilePath { get; set; } 14 | public string LogMode { get; set; } 15 | public string OwningProviderName { get; set; } 16 | public List ProviderNames { get; set; } 17 | public string ProviderLevel { get; set; } 18 | public string ProviderKeywords { get; set; } 19 | public int ProviderBufferSize { get; set; } 20 | public int ProviderMinimumNumberOfBuffers { get; set; } 21 | public int ProviderMaximumNumberOfBuffers { get; set; } 22 | public int ProviderLatency { get; set; } 23 | public string ProviderControlGuid { get; set; } 24 | public DateTime? CreationTime { get; set; } 25 | public DateTime? LastAccessTime { get; set; } 26 | public DateTime? LastWriteTime { get; set; } 27 | public long? FileSize { get; set; } 28 | public long? FileSizeMaximum; 29 | public double? FileSizeCurrentMB; 30 | public double? FileSizeMaximumMB; 31 | public long? RecordCount { get; set; } 32 | public long? OldestRecordNumber { get; set; } 33 | public string SecurityDescriptor { get; set; } 34 | public bool IsClassicLog { get; set; } 35 | 36 | public DateTime? NewestEvent; 37 | public DateTime? OldestEvent; 38 | public int? Attributes { get; set; } 39 | 40 | public EventLogDetails(InternalLogger internalLogger, string machineName, EventLogConfiguration logConfig, EventLogInformation logInfoObj) { 41 | LogName = logConfig.LogName; 42 | LogType = logConfig.LogType.ToString(); 43 | IsEnabled = logConfig.IsEnabled; 44 | MaximumSizeInBytes = logConfig.MaximumSizeInBytes; 45 | LogFilePath = logConfig.LogFilePath; 46 | LogIsolation = logConfig.LogIsolation; 47 | LogMode = logConfig.LogMode.ToString(); 48 | OwningProviderName = logConfig.OwningProviderName; 49 | try { 50 | ProviderNames = new List(logConfig.ProviderNames); 51 | } catch (Exception ex) { 52 | internalLogger.WriteWarning("Couldn't get provider names for " + LogName + ". Error: " + ex.Message); 53 | ProviderNames = new List(); 54 | } 55 | ProviderBufferSize = logConfig.ProviderBufferSize.GetValueOrDefault(); 56 | ProviderMinimumNumberOfBuffers = logConfig.ProviderMinimumNumberOfBuffers.GetValueOrDefault(); 57 | ProviderMaximumNumberOfBuffers = logConfig.ProviderMaximumNumberOfBuffers.GetValueOrDefault(); 58 | ProviderLatency = logConfig.ProviderLatency.GetValueOrDefault(); 59 | ProviderControlGuid = logConfig.ProviderControlGuid.ToString(); 60 | SecurityDescriptor = logConfig.SecurityDescriptor; 61 | ProviderLevel = logConfig.ProviderLevel.ToString(); 62 | ProviderKeywords = logConfig.ProviderKeywords.ToString(); 63 | IsClassicLog = logConfig.IsClassicLog; 64 | 65 | if (logInfoObj != null) { 66 | FileSize = logInfoObj.FileSize; 67 | RecordCount = logInfoObj.RecordCount; 68 | OldestRecordNumber = logInfoObj.OldestRecordNumber; 69 | LastAccessTime = logInfoObj.LastAccessTime; 70 | LastWriteTime = logInfoObj.LastWriteTime; 71 | CreationTime = logInfoObj.CreationTime; 72 | 73 | FileSizeCurrentMB = ConvertSize(FileSize, "B", "MB", 2); 74 | IsLogFull = logInfoObj.IsLogFull; 75 | Attributes = logInfoObj.Attributes; 76 | } 77 | 78 | FileSizeMaximum = logConfig.MaximumSizeInBytes; 79 | FileSizeMaximumMB = ConvertSize(FileSizeMaximum, "B", "MB", 2); 80 | MachineName = machineName; 81 | } 82 | 83 | private static double ConvertSize(double? value, string fromUnit, string toUnit, int precision) { 84 | if (!value.HasValue) { 85 | return 0; 86 | } 87 | 88 | double size = (double)value.Value; 89 | 90 | switch (fromUnit.ToUpper()) { 91 | case "B": 92 | break; 93 | case "KB": 94 | size *= 1024.0; 95 | break; 96 | case "MB": 97 | size *= 1024.0 * 1024.0; 98 | break; 99 | case "GB": 100 | size *= 1024.0 * 1024.0 * 1024.0; 101 | break; 102 | case "TB": 103 | size *= 1024.0 * 1024.0 * 1024.0 * 1024.0; 104 | break; 105 | } 106 | 107 | switch (toUnit.ToUpper()) { 108 | case "B": 109 | break; 110 | case "KB": 111 | size /= 1024.0; 112 | break; 113 | case "MB": 114 | size /= 1024.0 * 1024.0; 115 | break; 116 | case "GB": 117 | size /= 1024.0 * 1024.0 * 1024.0; 118 | break; 119 | case "TB": 120 | size /= 1024.0 * 1024.0 * 1024.0 * 1024.0; 121 | break; 122 | } 123 | 124 | return Math.Round(size, precision); 125 | } 126 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/EventObjectSlim.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | public class EventObjectSlim { 4 | internal EventObject _eventObject; 5 | public int EventID; // = _eventObject.Id; 6 | public long? RecordID; // = _eventObject.RecordId; 7 | public string GatheredFrom; // = _eventObject.MachineName; 8 | public string GatheredLogName; // = _eventObject.LogName; 9 | public string Type; 10 | 11 | 12 | public EventObjectSlim(EventObject eventObject) { 13 | _eventObject = eventObject; 14 | EventID = _eventObject.Id; 15 | RecordID = _eventObject.RecordId; 16 | GatheredFrom = _eventObject.QueriedMachine; 17 | GatheredLogName = _eventObject.ContainerLog; 18 | } 19 | 20 | internal static string ConvertToObjectAffected(EventObject eventObject) { 21 | if (eventObject.Data.ContainsKey("TargetUserName") && eventObject.Data.ContainsKey("TargetDomainName")) { 22 | return eventObject.Data["TargetDomainName"] + "\\" + eventObject.Data["TargetUserName"]; 23 | } else if (eventObject.Data.ContainsKey("TargetUserName")) { 24 | return eventObject.Data["TargetUserName"]; 25 | } else { 26 | return ""; 27 | } 28 | } 29 | internal static string ConvertToSamAccountName(EventObject eventObject) { 30 | if (eventObject.Data.ContainsKey("SamAccountName")) { 31 | return eventObject.Data["SamAccountName"]; 32 | } else { 33 | return ""; 34 | } 35 | } 36 | internal string ConvertFromOperationType(string s) { 37 | Dictionary known = new Dictionary { 38 | {"%%14674", "Value Added"}, 39 | {"%%14675", "Value Deleted"}, 40 | {"%%14676", "Unknown"} 41 | }; 42 | 43 | if (known.ContainsKey(s)) { 44 | return known[s]; 45 | } else { 46 | return "Unknown Operation"; 47 | } 48 | } 49 | internal static string OverwriteByField(string findField, string expectedValue, string currentValue, string insertValue) { 50 | //OverwriteByField = [ordered] @{ 51 | // 'User Object' = 'Action', 'A directory service object was moved.', 'OldObjectDN' 52 | // 'Field Value' = 'Action', 'A directory service object was moved.', 'NewObjectDN' 53 | //} 54 | if (findField == expectedValue) { 55 | return insertValue; 56 | } else { 57 | return currentValue; 58 | } 59 | 60 | } 61 | internal static string TranslateUacValue(string hexValue) { 62 | if (hexValue == null || hexValue.Trim() == "-") { 63 | return ""; 64 | } 65 | // 0x10 66 | // 0x11 67 | // %%2080 68 | 69 | // Convert the hexadecimal value to an integer 70 | int uacValue = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber); 71 | 72 | // Define the UAC flags 73 | Dictionary uacFlags = new Dictionary { 74 | { 0x0001, "SCRIPT" }, 75 | { 0x0002, "ACCOUNTDISABLE" }, 76 | { 0x0008, "HOMEDIR_REQUIRED" }, 77 | { 0x0010, "LOCKOUT" }, 78 | { 0x0020, "PASSWD_NOTREQD" }, 79 | { 0x0040, "PASSWD_CANT_CHANGE" }, 80 | { 0x0080, "ENCRYPTED_TEXT_PWD_ALLOWED" }, 81 | { 0x0100, "TEMP_DUPLICATE_ACCOUNT" }, 82 | { 0x0200, "NORMAL_ACCOUNT" }, 83 | { 0x0800, "INTERDOMAIN_TRUST_ACCOUNT" }, 84 | { 0x1000, "WORKSTATION_TRUST_ACCOUNT" }, 85 | { 0x2000, "SERVER_TRUST_ACCOUNT" }, 86 | { 0x10000, "DONT_EXPIRE_PASSWORD" }, 87 | { 0x20000, "MNS_LOGON_ACCOUNT" }, 88 | { 0x40000, "SMARTCARD_REQUIRED" }, 89 | { 0x80000, "TRUSTED_FOR_DELEGATION" }, 90 | { 0x100000, "NOT_DELEGATED" }, 91 | { 0x200000, "USE_DES_KEY_ONLY" }, 92 | { 0x400000, "DONT_REQ_PREAUTH" }, 93 | { 0x800000, "PASSWORD_EXPIRED" }, 94 | { 0x1000000, "TRUSTED_TO_AUTH_FOR_DELEGATION" }, 95 | { 0x04000000, "PARTIAL_SECRETS_ACCOUNT" } 96 | }; 97 | 98 | // Map each bit in the UAC value to a UAC flag 99 | List translatedFlags = new List(); 100 | foreach (var flag in uacFlags) { 101 | if ((uacValue & flag.Key) != 0) { 102 | translatedFlags.Add(flag.Value); 103 | } 104 | } 105 | 106 | // Return the translated UAC flags as a comma-separated string 107 | return string.Join(", ", translatedFlags); 108 | } 109 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/EventViewer.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /Sources/EventViewerX/EventViewerX.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472;netstandard2.0;net8.0 5 | Library to work with Event Logs 6 | EventViewerX 7 | EventViewerX 8 | 2.1.0 9 | false 10 | Evotec 11 | Przemyslaw Klys 12 | latest 13 | true 14 | true 15 | windows 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Sources/EventViewerX/EventViewerX.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True -------------------------------------------------------------------------------- /Sources/EventViewerX/EventsHelper.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | /// 4 | /// Event helper methods. 5 | /// 6 | internal static class EventsHelper { 7 | /// 8 | /// Translates a string value to an ImpersonationLevel enum. 9 | /// 10 | /// The value to translate. 11 | /// The translated ImpersonationLevel enum. 12 | public static ImpersonationLevel? GetImpersonationLevel(string value) { 13 | if (Enum.TryParse(value.TrimStart('%', '%'), out ImpersonationLevel impersonationLevel)) { 14 | return impersonationLevel; 15 | } 16 | return null; 17 | } 18 | 19 | /// 20 | /// Translates a string value to a VirtualAccount enum. 21 | /// 22 | /// The value to translate. 23 | /// The translated VirtualAccount enum. 24 | public static VirtualAccount? GetVirtualAccount(string value) { 25 | if (Enum.TryParse(value.TrimStart('%', '%'), out VirtualAccount virtualAccount)) { 26 | return virtualAccount; 27 | } 28 | return null; 29 | } 30 | 31 | /// 32 | /// Translates a string value to an ElevatedToken enum. 33 | /// 34 | /// The value to translate. 35 | /// The translated ElevatedToken enum. 36 | public static ElevatedToken? GetElevatedToken(string value) { 37 | if (Enum.TryParse(value.TrimStart('%', '%'), out ElevatedToken elevatedToken)) { 38 | return elevatedToken; 39 | } 40 | return null; 41 | } 42 | 43 | /// 44 | /// Translates a string value to a LogonType enum. 45 | /// 46 | /// The value to translate. 47 | /// The translated LogonType enum. 48 | public static LogonType? GetLogonType(string value) { 49 | if (Enum.TryParse(value, out LogonType logonType)) { 50 | return logonType; 51 | } 52 | return null; 53 | } 54 | 55 | 56 | /// 57 | /// Translates a string value to a TicketOptions enum. 58 | /// 59 | /// The value to translate. 60 | /// The translated TicketOptions enum. 61 | public static TicketOptions? GetTicketOptions(string value) { 62 | if (Enum.TryParse(value, out TicketOptions ticketOptions)) { 63 | return ticketOptions; 64 | } 65 | return null; 66 | } 67 | 68 | /// 69 | /// Translates a string value to a Status enum. 70 | /// 71 | /// The value to translate. 72 | /// The translated Status enum. 73 | public static Status? GetStatus(string value) { 74 | if (Enum.TryParse(value, out Status status)) { 75 | return status; 76 | } 77 | return null; 78 | } 79 | 80 | /// 81 | /// Translates a string value to a TicketEncryptionType enum. 82 | /// 83 | /// The value to translate. 84 | /// The translated TicketEncryptionType enum. 85 | public static TicketEncryptionType? GetTicketEncryptionType(string value) { 86 | if (Enum.TryParse(value, out TicketEncryptionType ticketEncryptionType)) { 87 | return ticketEncryptionType; 88 | } 89 | return null; 90 | } 91 | 92 | /// 93 | /// Translates a string value to a PreAuthType enum. 94 | /// 95 | /// The value to translate. 96 | /// The translated PreAuthType enum. 97 | public static PreAuthType? GetPreAuthType(string value) { 98 | if (Enum.TryParse(value, out PreAuthType preAuthType)) { 99 | return preAuthType; 100 | } 101 | return null; 102 | } 103 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Helpers/ActiveDirectory/GroupPolicyHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices; 2 | using System.DirectoryServices.ActiveDirectory; 3 | 4 | namespace EventViewerX.Helpers.ActiveDirectory; 5 | 6 | /// 7 | /// Query Active Directory for Group Policy objects. 8 | /// 9 | public class GroupPolicyHelpers { 10 | /// 11 | /// Query Active Directory Forest for a Group Policy object by its DistinguishedName. 12 | /// 13 | /// 14 | /// 15 | public static GroupPolicy QueryGroupPolicyByDistinguishedName(string gpoDn) { 16 | try { 17 | Forest currentForest = Forest.GetCurrentForest(); 18 | foreach (Domain domain in currentForest.Domains) { 19 | var policy = QueryGroupPolicyInDomainByDistinguishedName(domain.Name, gpoDn); 20 | if (policy != null) { 21 | return policy; 22 | } 23 | } 24 | } catch (Exception ex) { 25 | Console.WriteLine($"Error querying group policy by DN: {ex.Message}"); 26 | } 27 | return null; 28 | } 29 | 30 | /// 31 | /// Query Active Directory Domain for a Group Policy object by its DistinguishedName. 32 | /// 33 | /// 34 | /// 35 | /// 36 | private static GroupPolicy QueryGroupPolicyInDomainByDistinguishedName(string domainName, string gpoDn) { 37 | try { 38 | using DirectoryEntry rootDSE = new DirectoryEntry($"LDAP://{domainName}/CN=Policies,CN=System,DC={domainName.Replace(".", ",DC=")}"); 39 | using DirectorySearcher searcher = new DirectorySearcher(rootDSE); 40 | searcher.Filter = $"(&(objectClass=groupPolicyContainer)(distinguishedName={gpoDn}))"; 41 | searcher.PropertiesToLoad.Add("displayName"); 42 | searcher.PropertiesToLoad.Add("name"); 43 | searcher.PropertiesToLoad.Add("distinguishedName"); 44 | 45 | SearchResult result = searcher.FindOne(); 46 | if (result != null) { 47 | string gpoName = result.Properties["displayName"].Count > 0 48 | ? result.Properties["displayName"][0].ToString() 49 | : string.Empty; 50 | string gpoId = result.Properties["name"].Count > 0 51 | ? result.Properties["name"][0].ToString() 52 | : string.Empty; 53 | return new GroupPolicy { 54 | GpoName = gpoName, 55 | GpoId = gpoId, 56 | GpoDomain = domainName 57 | }; 58 | } 59 | } catch (Exception ex) { 60 | Console.WriteLine($"Error querying group policy by DN in domain {domainName}: {ex.Message}"); 61 | } 62 | return null; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/EventViewerX/MyEventSource.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Tracing; 2 | 3 | namespace EventViewerX { 4 | 5 | [EventSource(Name = "MyCompany-MyApplication-MyEventSource")] 6 | public sealed class MyEventSource : EventSource { 7 | public static readonly MyEventSource Log = new MyEventSource(); 8 | 9 | [Event(1, Level = EventLevel.Informational)] 10 | public void MyEvent(int eventId, string param1, string param2, string binaryData) { 11 | if (eventId > 0) { 12 | WriteEvent(eventId, param1, param2, binaryData); 13 | } 14 | } 15 | } 16 | 17 | public class EventLogger { 18 | public static void WriteToEventLog(string eventSourceName, int eventId, EventLevel level, string param1, string param2, string binaryData) { 19 | MyEventSource.Log.MyEvent(eventId, param1, param2, binaryData); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Computers/ADComputerChangeDetailed.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// Active Directory Computer Change Detailed 4 | /// 5136: A directory service object was modified 5 | /// 5137: A directory service object was created 6 | /// 5139: A directory service object was deleted 7 | /// 5141: A directory service object was moved 8 | public class ADComputerChangeDetailed : EventObjectSlim { 9 | public string Computer; 10 | public string Action; 11 | public string ObjectClass; 12 | public string OperationType; 13 | public string Who; 14 | public DateTime When; 15 | public string ComputerObject; // 'Computer Object' 16 | public string FieldChanged; 17 | public string FieldValue; 18 | 19 | //public string ClientDNSName; 20 | 21 | /// 22 | /// Active Directory Computer Change Detailed 23 | /// 24 | /// 25 | public ADComputerChangeDetailed(EventObject eventObject) : base(eventObject) { 26 | // common fields 27 | _eventObject = eventObject; 28 | Type = "ADComputerChangeDetailed"; 29 | Computer = _eventObject.ComputerName; 30 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 31 | Action = _eventObject.MessageSubject; 32 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 33 | When = _eventObject.TimeCreated; 34 | // 35 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 36 | ComputerObject = _eventObject.GetValueFromDataDictionary("ObjectDN"); 37 | FieldChanged = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 38 | FieldValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 39 | // OverwriteByField logic 40 | ComputerObject = OverwriteByField(Action, "A directory service object was moved.", ComputerObject, _eventObject.GetValueFromDataDictionary("OldObjectDN")); 41 | FieldValue = OverwriteByField(Action, "A directory service object was moved.", FieldValue, _eventObject.GetValueFromDataDictionary("NewObjectDN")); 42 | 43 | //ClientDNSName = QueryDnsAsync("1.1.1.1").ConfigureAwait(false).GetAwaiter().GetResult(); 44 | } 45 | 46 | //private static async Task QueryDnsAsync(string clientAddress) { 47 | // if (string.IsNullOrEmpty(clientAddress)) { 48 | // return null; 49 | // } 50 | 51 | // try { 52 | // Settings._logger.WriteVerbose($"Querying DNS for address: {clientAddress}"); 53 | // var result = await ClientX.QueryDns(clientAddress, DnsRecordType.PTR); 54 | // var resolvedNames = string.Join(", ", result.AnswersMinimal.Select(answer => answer.Data)); 55 | // Settings._logger.WriteVerbose($"Resolved names: {resolvedNames}"); 56 | // return resolvedNames; 57 | // } catch (Exception ex) { 58 | // Settings._logger.WriteWarning($"Querying DNS for address: {clientAddress} failed: {ex.Message}"); 59 | // return null; 60 | // } 61 | //} 62 | } 63 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Computers/ADComputerCreateChange.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Computer Created or Changed 5 | /// 4741: A computer account was created 6 | /// 4742: A computer account was changed 7 | /// 8 | public class ADComputerCreateChange : EventObjectSlim { 9 | public string Computer; 10 | public string Action; 11 | public string ComputerAffected; 12 | public string SamAccountName; 13 | public string DisplayName; 14 | public string UserPrincipalName; 15 | public string HomeDirectory; 16 | public string HomePath; 17 | public string ScriptPath; 18 | public string ProfilePath; 19 | public string UserWorkstations; 20 | public string PasswordLastSet; 21 | public string AccountExpires; 22 | public string PrimaryGroupId; 23 | public string AllowedToDelegateTo; 24 | public string OldUacValue; 25 | public string NewUacValue; 26 | public string UserAccountControl; 27 | public string UserParameters; 28 | public string SidHistory; 29 | public string LogonHours; 30 | public string DnsHostName; 31 | public string ServicePrincipalNames; 32 | public string Who; 33 | public DateTime When; 34 | 35 | public ADComputerCreateChange(EventObject eventObject) : base(eventObject) { 36 | // common fields 37 | _eventObject = eventObject; 38 | Type = "ADComputerChange"; 39 | 40 | Computer = _eventObject.ComputerName; 41 | Action = _eventObject.MessageSubject; 42 | ComputerAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 43 | SamAccountName = _eventObject.GetValueFromDataDictionary("SamAccountName"); 44 | DisplayName = _eventObject.GetValueFromDataDictionary("DisplayName"); 45 | UserPrincipalName = _eventObject.GetValueFromDataDictionary("UserPrincipalName"); 46 | HomeDirectory = _eventObject.GetValueFromDataDictionary("HomeDirectory"); 47 | HomePath = _eventObject.GetValueFromDataDictionary("HomePath"); 48 | ScriptPath = _eventObject.GetValueFromDataDictionary("ScriptPath"); 49 | ProfilePath = _eventObject.GetValueFromDataDictionary("ProfilePath"); 50 | UserWorkstations = _eventObject.GetValueFromDataDictionary("UserWorkstations"); 51 | PasswordLastSet = _eventObject.GetValueFromDataDictionary("PasswordLastSet"); 52 | AccountExpires = _eventObject.GetValueFromDataDictionary("AccountExpires"); 53 | PrimaryGroupId = _eventObject.GetValueFromDataDictionary("PrimaryGroupId"); 54 | AllowedToDelegateTo = _eventObject.GetValueFromDataDictionary("AllowedToDelegateTo"); 55 | OldUacValue = _eventObject.GetValueFromDataDictionary("OldUacValue"); 56 | NewUacValue = _eventObject.GetValueFromDataDictionary("NewUacValue"); 57 | UserAccountControl = _eventObject.GetValueFromDataDictionary("UserAccountControl"); 58 | UserParameters = _eventObject.GetValueFromDataDictionary("UserParameters"); 59 | SidHistory = _eventObject.GetValueFromDataDictionary("SidHistory"); 60 | LogonHours = _eventObject.GetValueFromDataDictionary("LogonHours"); 61 | DnsHostName = _eventObject.GetValueFromDataDictionary("DnsHostName"); 62 | ServicePrincipalNames = _eventObject.GetValueFromDataDictionary("ServicePrincipalNames"); 63 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 64 | When = _eventObject.TimeCreated; 65 | 66 | // let's try to translate them 67 | OldUacValue = TranslateUacValue(OldUacValue); 68 | NewUacValue = TranslateUacValue(NewUacValue); 69 | UserAccountControl = TranslateUacValue(UserAccountControl); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Computers/ADComputerDeleted.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Computer Deleted 5 | /// 4743: A computer account was deleted 6 | /// 7 | public class ADComputerDeleted : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string ComputerAffected; 11 | public string Who; 12 | public DateTime When; 13 | 14 | public ADComputerDeleted(EventObject eventObject) : base(eventObject) { 15 | // common fields 16 | _eventObject = eventObject; 17 | Type = "ADComputerDeleted"; 18 | 19 | Computer = _eventObject.ComputerName; 20 | Action = _eventObject.MessageSubject; 21 | 22 | ComputerAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 23 | 24 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 25 | When = _eventObject.TimeCreated; 26 | } 27 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/GroupPolicies/ADGroupPolicyChanges.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | public class ADGroupPolicyChanges : EventObjectSlim { 4 | public string Computer; 5 | public string Action; 6 | public string ObjectClass; 7 | public string OperationType; 8 | public string Who; 9 | public DateTime When; 10 | public string GpoName; 11 | public string AttributeLDAPDisplayName; 12 | public string AttributeValue; 13 | 14 | public ADGroupPolicyChanges(EventObject eventObject) : base(eventObject) { 15 | _eventObject = eventObject; 16 | Type = "ADGroupPolicyChanges"; 17 | Computer = _eventObject.ComputerName; 18 | Action = _eventObject.MessageSubject; 19 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 20 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 21 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 22 | When = _eventObject.TimeCreated; 23 | GpoName = _eventObject.GetValueFromDataDictionary("ObjectDN"); 24 | AttributeLDAPDisplayName = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 25 | AttributeValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 26 | } 27 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/GroupPolicies/ADGroupPolicyChangesDetailed.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | public class ADGroupPolicyChangesDetailed : EventObjectSlim { 4 | public string Computer; 5 | public string Action; 6 | public string ObjectClass; 7 | public string OperationType; 8 | public string Who; 9 | public DateTime When; 10 | public string GpoName; 11 | public string AttributeLDAPDisplayName; 12 | public string AttributeValue; 13 | 14 | public ADGroupPolicyChangesDetailed(EventObject eventObject) : base(eventObject) { 15 | _eventObject = eventObject; 16 | Type = "ADGroupPolicyChangesDetailed"; 17 | Computer = _eventObject.ComputerName; 18 | Action = _eventObject.MessageSubject; 19 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 20 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 21 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 22 | When = _eventObject.TimeCreated; 23 | GpoName = _eventObject.GetValueFromDataDictionary("ObjectDN"); 24 | AttributeLDAPDisplayName = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 25 | AttributeValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 26 | } 27 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/GroupPolicies/ADGroupPolicyEdits.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | public class ADGroupPolicyEdits : EventObjectSlim { 4 | public string Computer; 5 | public string Action; 6 | public string ObjectClass; 7 | public string OperationType; 8 | public string Who; 9 | public DateTime When; 10 | public string GpoName; 11 | public string AttributeLDAPDisplayName; 12 | public string AttributeValue; 13 | 14 | public ADGroupPolicyEdits(EventObject eventObject) : base(eventObject) { 15 | _eventObject = eventObject; 16 | Type = "ADGroupPolicyEdits"; 17 | Computer = _eventObject.ComputerName; 18 | Action = _eventObject.MessageSubject; 19 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 20 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 21 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 22 | When = _eventObject.TimeCreated; 23 | GpoName = _eventObject.GetValueFromDataDictionary("ObjectDN"); 24 | AttributeLDAPDisplayName = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 25 | AttributeValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 26 | } 27 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Groups/ADGroupChange.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Group Change 5 | /// 4735: A security-enabled local group was created 6 | /// 4737: A security-enabled global group was created 7 | /// 4745: A security-enabled universal group was created 8 | /// 4750: A security-enabled universal group was changed 9 | /// 4760: A security-enabled global group was changed 10 | /// 4764: A security-enabled local group was changed 11 | /// 4784: A security-enabled universal group was deleted 12 | /// 4791: A security-enabled global group was deleted 13 | /// 14 | public class ADGroupChange : EventObjectSlim { 15 | 16 | public string Computer; 17 | public string Action; 18 | public string GroupName; 19 | public string Who; 20 | public DateTime When; 21 | public string GroupTypeChange; 22 | public string SamAccountName; 23 | public string SidHistory; 24 | public ADGroupChange(EventObject eventObject) : base(eventObject) { 25 | _eventObject = eventObject; 26 | Type = "ADGroupChange"; 27 | 28 | Computer = _eventObject.ComputerName; 29 | Action = _eventObject.MessageSubject; 30 | 31 | GroupName = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 32 | GroupTypeChange = _eventObject.GetValueFromDataDictionary("GroupTypeChange"); 33 | SamAccountName = _eventObject.GetValueFromDataDictionary("SamAccountName"); 34 | SidHistory = _eventObject.GetValueFromDataDictionary("SidHistory"); 35 | 36 | // common fields 37 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 38 | When = _eventObject.TimeCreated; 39 | } 40 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Groups/ADGroupChangeDetailed.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Group Change Detailed 5 | /// 5136: A directory service object was modified 6 | /// 5137: A directory service object was created 7 | /// 5139: A directory service object was deleted 8 | /// 5141: A directory service object was moved 9 | /// 10 | public class ADGroupChangeDetailed : EventObjectSlim { 11 | public string Computer; 12 | public string Action; 13 | public string ObjectClass; 14 | public string OperationType; 15 | public string Who; 16 | public DateTime When; 17 | public string Group; // 'User Object' 18 | public string FieldChanged; // 'Field Changed' 19 | public string FieldValue; // 'Field Value' 20 | 21 | public ADGroupChangeDetailed(EventObject eventObject) : base(eventObject) { 22 | _eventObject = eventObject; 23 | Type = "ADGroupChangeDetailed"; 24 | 25 | Computer = _eventObject.ComputerName; 26 | Action = _eventObject.MessageSubject; 27 | 28 | Computer = _eventObject.ComputerName; 29 | Action = _eventObject.MessageSubject; 30 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 31 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 32 | Group = _eventObject.GetValueFromDataDictionary("ObjectDN"); 33 | FieldChanged = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 34 | FieldValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 35 | 36 | // OverwriteByField logic 37 | Group = OverwriteByField(Action, "A directory service object was moved.", Group, _eventObject.GetValueFromDataDictionary("OldObjectDN")); 38 | FieldValue = OverwriteByField(Action, "A directory service object was moved.", FieldValue, _eventObject.GetValueFromDataDictionary("NewObjectDN")); 39 | 40 | // common fields 41 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 42 | When = _eventObject.TimeCreated; 43 | } 44 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Groups/ADGroupCreateDelete.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Group Create Delete 5 | /// 4727: 6 | /// 4730: 7 | /// 4731: 8 | /// 4734: 9 | /// 4744: 10 | /// 4748: 11 | /// 4749: 12 | /// 4753: 13 | /// 4754: 14 | /// 4758: 15 | /// 4759: 16 | /// 4763: 17 | /// 18 | public class ADGroupCreateDelete : EventObjectSlim { 19 | public string Computer; 20 | public string Action; 21 | public string GroupName; 22 | public string Who; 23 | public DateTime When; 24 | 25 | public ADGroupCreateDelete(EventObject eventObject) : base(eventObject) { 26 | _eventObject = eventObject; 27 | Type = "ADGroupCreateDelete"; 28 | 29 | Computer = _eventObject.ComputerName; 30 | Action = _eventObject.MessageSubject; 31 | 32 | GroupName = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 33 | 34 | // common fields 35 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 36 | When = _eventObject.TimeCreated; 37 | } 38 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Groups/ADGroupEnumeration.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Group Enumeration 5 | /// 4798: A user's local group membership was enumerated 6 | /// 4799: A security-enabled local group membership was enumerated 7 | /// 8 | public class ADGroupEnumeration : EventObjectSlim { 9 | 10 | public string Computer; 11 | public string Action; 12 | public string GroupName; 13 | public string Who; 14 | public DateTime When; 15 | 16 | public ADGroupEnumeration(EventObject eventObject) : base(eventObject) { 17 | _eventObject = eventObject; 18 | Type = "ADGroupEnumeration"; 19 | 20 | Computer = _eventObject.ComputerName; 21 | Action = _eventObject.MessageSubject; 22 | 23 | GroupName = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 24 | 25 | // common fields 26 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 27 | When = _eventObject.TimeCreated; 28 | } 29 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Groups/ADGroupMembershipChange.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory Group Membership Changes 5 | /// 4728: A member was added to a security-enabled global group 6 | /// 4729: A member was removed from a security-enabled global group 7 | /// 4732: A member was added to a security-enabled local group 8 | /// 4733: A member was removed from a security-enabled local group 9 | /// 4746: A member was added to a security-enabled universal group 10 | /// 4747: A member was removed from a security-enabled universal group 11 | /// 4751: A member was added to a distribution group 12 | /// 4752: A member was removed from a distribution group 13 | /// 4756: A member was added to a security-enabled universal group 14 | /// 4757: A member was removed from a security-enabled universal group 15 | /// 4761: A member was added to a security-enabled global group 16 | /// 4762: A member was removed from a security-enabled global group 17 | /// 4785: A member was added to a security-enabled universal group 18 | /// 4786: A member was removed from a security-enabled universal group 19 | /// 4787: A member was added to a security-enabled universal group 20 | /// 4788: A member was removed from a security-enabled universal group 21 | /// 22 | public class ADGroupMembershipChange : EventObjectSlim { 23 | 24 | public string Computer; 25 | public string Action; 26 | public string GroupName; 27 | public string MemberName; 28 | public string Who; 29 | public DateTime When; 30 | 31 | public ADGroupMembershipChange(EventObject eventObject) : base(eventObject) { 32 | _eventObject = eventObject; 33 | Type = "ADGroupMembershipChange"; 34 | 35 | Computer = _eventObject.ComputerName; 36 | Action = _eventObject.MessageSubject; 37 | 38 | GroupName = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 39 | MemberName = _eventObject.GetValueFromDataDictionary("MemberNameWithoutCN"); 40 | 41 | // common fields 42 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 43 | When = _eventObject.TimeCreated; 44 | } 45 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/LDAP/ADLdapBindingDetails.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// LDAP binding details. 5 | /// 2889: The security of this directory server can be significantly enhanced by configuring the server to reject SASL (Negotiate, Kerberos, NTLM, or Digest) LDAP binds that do not request signing (integrity verification) and LDAP simple binds that are performed on a cleartext (non-SSL/TLS-encrypted) connection. Even if this directory server is not used by Active Directory, it is recommended that this server be configured to reject such binds. For more details and information on how to make this configuration change to the server, please see http://go.microsoft.com/fwlink/?LinkID=87923. 6 | /// 7 | public class ADLdapBindingDetails : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | private string IPPort; 11 | public string AccountName; 12 | private string BindType; 13 | public DateTime When; 14 | 15 | public string LogName = "Directory Service"; 16 | //public List EventID = [2889]; 17 | 18 | 19 | public ADLdapBindingDetails(EventObject eventObject) : base(eventObject) { 20 | _eventObject = eventObject; 21 | 22 | Type = "ADLdapBindingDetails"; 23 | Computer = _eventObject.ComputerName; 24 | Action = _eventObject.MessageSubject; 25 | IPPort = _eventObject.GetValueFromDataDictionary("NoNameA0"); 26 | AccountName = _eventObject.GetValueFromDataDictionary("NoNameA1"); 27 | BindType = _eventObject.GetValueFromDataDictionary("NoNameA2"); 28 | if (BindType == "0") { 29 | BindType = "Unsigned"; 30 | } else if (BindType == "1") { 31 | BindType = "Simple"; 32 | } 33 | When = _eventObject.TimeCreated; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/LDAP/ADLdapBindingSummary.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | // 4 | /// A summary of LDAP binding activity. 5 | /// 2887(0x0B57) - The security of this directory server can be significantly enhanced by configuring the server to reject SASL (Negotiate, Kerberos, NTLM, or Digest) LDAP binds that do not request signing (integrity verification) and LDAP simple binds that are performed on a cleartext (non-SSL/TLS-encrypted) connection. Even if this directory server is not used by Active Directory, it is recommended that this server be configured to reject such binds. For more details and information on how to make this configuration change to the server, please see http://go.microsoft.com/fwlink/?LinkID=87923. 6 | /// 7 | public class ADLdapBindingSummary : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string NumberOfSimpleBindsWithoutSSLTLS; 11 | public string NumberOfNegotiateKerberosNtlmDigestBindsPerformedWithoutSigning; 12 | public string TaskDisplayName; 13 | public string LevelDisplayName; 14 | public DateTime When; 15 | 16 | public string LogName = "Directory Service"; 17 | //public List EventID = [2887]; 18 | 19 | public ADLdapBindingSummary(EventObject eventObject) : base(eventObject) { 20 | _eventObject = eventObject; 21 | 22 | Type = "ADLdapBindingSummary"; 23 | Computer = _eventObject.ComputerName; 24 | Action = _eventObject.MessageSubject; 25 | NumberOfSimpleBindsWithoutSSLTLS = _eventObject.GetValueFromDataDictionary("NoNameA0"); 26 | NumberOfNegotiateKerberosNtlmDigestBindsPerformedWithoutSigning = _eventObject.GetValueFromDataDictionary("NoNameA1"); 27 | When = _eventObject.TimeCreated; 28 | LevelDisplayName = eventObject.LevelDisplayName; 29 | TaskDisplayName = eventObject.TaskDisplayName; 30 | } 31 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/OrganizationalUnit/ADOrganizationalUnitChangeDetailed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventViewerX.Rules.ActiveDirectory { 4 | public class ADOrganizationalUnitChangeDetailed : EventObjectSlim { 5 | 6 | 7 | // ADOrganizationalUnitChangesDetailed = [ordered] @{ 8 | // Enabled = $false 9 | // OUEventsModify = @{ 10 | // Enabled = $true 11 | // Events = 5136, 5137, 5139, 5141 12 | // LogName = 'Security' 13 | // Filter = [ordered] @{ 14 | // 'ObjectClass' = 'organizationalUnit' 15 | // } 16 | // Functions = @{ 17 | // 'OperationType' = 'ConvertFrom-OperationType' 18 | // } 19 | 20 | // Fields = [ordered] @{ 21 | // 'Computer' = 'Domain Controller' 22 | // 'Action' = 'Action' 23 | // 'OperationType' = 'Action Detail' 24 | // 'Who' = 'Who' 25 | // 'Date' = 'When' 26 | // 'ObjectDN' = 'Organizational Unit' 27 | // 'AttributeLDAPDisplayName' = 'Field Changed' 28 | // 'AttributeValue' = 'Field Value' 29 | // #'OldObjectDN' = 'OldObjectDN' 30 | // #'NewObjectDN' = 'NewObjectDN' 31 | // # Common Fields 32 | // 'RecordID' = 'Record ID' 33 | // 'ID' = 'Event ID' 34 | // 'GatheredFrom' = 'Gathered From' 35 | // 'GatheredLogName' = 'Gathered LogName' 36 | // } 37 | // Overwrite = [ordered] @{ 38 | // 'Action Detail#1' = 'Action', 'A directory service object was created.', 'Organizational Unit Created' 39 | // 'Action Detail#2' = 'Action', 'A directory service object was deleted.', 'Organizational Unit Deleted' 40 | // 'Action Detail#3' = 'Action', 'A directory service object was moved.', 'Organizational Unit Moved' 41 | // #'Organizational Unit' = 'Action', 'A directory service object was moved.', 'OldObjectDN' 42 | // #'Field Changed' = 'Action', 'A directory service object was moved.', '' 43 | // #'Field Value' = 'Action', 'A directory service object was moved.', 'NewObjectDN' 44 | // } 45 | // # This Overwrite works in a way where you can swap one value with another value from another field within same Event 46 | // # It's useful if you have an event that already has some fields used but empty and you wnat to utilize them 47 | // # for some content 48 | // OverwriteByField = [ordered] @{ 49 | // 'Organizational Unit' = 'Action', 'A directory service object was moved.', 'OldObjectDN' 50 | // #'Field Changed' = 'Action', 'A directory service object was moved.', '' 51 | // 'Field Value' = 'Action', 'A directory service object was moved.', 'NewObjectDN' 52 | // } 53 | // SortBy = 'Record ID' 54 | // Descending = $false 55 | // IgnoreWords = @{ } 56 | // } 57 | // } 58 | public string Computer; 59 | public string Action; 60 | public string OperationType; 61 | public string Who; 62 | public DateTime When; 63 | public string OrganizationalUnit; // 'User Object' 64 | public string FieldChanged; // 'Field Changed' 65 | public string FieldValue; // 'Field Value' 66 | 67 | 68 | public ADOrganizationalUnitChangeDetailed(EventObject eventObject) : base(eventObject) { 69 | // common fields 70 | _eventObject = eventObject; 71 | Type = "ADOrganizationalUnitChangeDetailed"; 72 | Computer = _eventObject.ComputerName; 73 | Action = _eventObject.MessageSubject; 74 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 75 | When = _eventObject.TimeCreated; 76 | 77 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 78 | OrganizationalUnit = _eventObject.GetValueFromDataDictionary("ObjectDN"); 79 | FieldChanged = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 80 | FieldValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 81 | // OverwriteByField logic 82 | OrganizationalUnit = OverwriteByField(Action, "A directory service object was moved.", OrganizationalUnit, _eventObject.GetValueFromDataDictionary("OldObjectDN")); 83 | FieldValue = OverwriteByField(Action, "A directory service object was moved.", FieldValue, _eventObject.GetValueFromDataDictionary("NewObjectDN")); 84 | 85 | //OperationType = OverwriteByField(Action, "A directory service object was created.", OperationType, "Organizational Unit Created"); 86 | //OperationType = OverwriteByField(Action, "A directory service object was deleted.", OperationType, "Organizational Unit Deleted"); 87 | //OperationType = OverwriteByField(Action, "A directory service object was moved.", OperationType, "Organizational Unit Moved"); 88 | 89 | if (Action == "A directory service object was created.") { 90 | OperationType = "Organizational Unit Created"; 91 | } else if (Action == "A directory service object was deleted.") { 92 | OperationType = "Organizational Unit Deleted"; 93 | } else if (Action == "A directory service object was moved.") { 94 | OperationType = "Organizational Unit Moved"; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Others/ADOtherChangeDetailed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventViewerX.Rules.ActiveDirectory { 4 | 5 | /// 6 | /// Active Directory Other Change Detailed 7 | /// 5136: A directory service object was modified 8 | /// 5137: A directory service object was created 9 | /// 5139: A directory service object was deleted 10 | /// 5141: A directory service object was moved 11 | /// 12 | public class ADOtherChangeDetailed : EventObjectSlim { 13 | 14 | public string Computer; 15 | public string Action; 16 | public string ObjectClass; 17 | public string OperationType; 18 | public string Who; 19 | public DateTime When; 20 | public string User; // 'User Object' 21 | public string FieldChanged; // 'Field Changed' 22 | public string FieldValue; // 'Field Value' 23 | 24 | 25 | public ADOtherChangeDetailed(EventObject eventObject) : base(eventObject) { 26 | _eventObject = eventObject; 27 | 28 | Type = "ADOtherChangeDetailed"; 29 | Computer = _eventObject.ComputerName; 30 | Action = _eventObject.MessageSubject; 31 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 32 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 33 | User = _eventObject.GetValueFromDataDictionary("ObjectDN"); 34 | FieldChanged = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 35 | FieldValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 36 | // common fields 37 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 38 | When = _eventObject.TimeCreated; 39 | 40 | // OverwriteByField logic 41 | User = OverwriteByField(Action, "A directory service object was moved.", User, _eventObject.GetValueFromDataDictionary("OldObjectDN")); 42 | FieldValue = OverwriteByField(Action, "A directory service object was moved.", FieldValue, _eventObject.GetValueFromDataDictionary("NewObjectDN")); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/SMB/SMBServerAudit.cs: -------------------------------------------------------------------------------- 1 | using DnsClientX; 2 | 3 | namespace EventViewerX.Rules.ActiveDirectory; 4 | /// 5 | /// SMB Server Audit 6 | /// 3000: SMB1 access 7 | /// 8 | /// Before running the script, you need to enable the audit policy on the server. 9 | /// Set-SmbServerConfiguration -AuditSmb1Access $true 10 | /// 11 | /// Alternatively via registry: 12 | /// HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters 13 | /// AuditSmb1Access => REG_DWORD => 1 14 | /// 15 | public class SMBServerAudit : EventObjectSlim { 16 | // public EventObject EventObject { get; } 17 | public string Computer; 18 | public string Action; 19 | public string ClientAddress; 20 | public string ClientDNSName = string.Empty; 21 | public DateTime When; 22 | 23 | // private ctor that performs partial initialization 24 | private SMBServerAudit(EventObject eventObject) : base(eventObject) { 25 | //EventObject = eventObject; 26 | 27 | _eventObject = eventObject; 28 | Type = "ADSMBServerAuditV1"; 29 | Computer = _eventObject.ComputerName; 30 | Action = _eventObject.MessageSubject; 31 | ClientAddress = _eventObject.GetValueFromDataDictionary("ClientName"); 32 | When = _eventObject.TimeCreated; 33 | } 34 | 35 | // static instance creation method to hide the async initialization 36 | public static SMBServerAudit Create(EventObject eventObject) { 37 | var smbAudit = new SMBServerAudit(eventObject); 38 | // smbAudit.ClientDNSName = QueryDnsAsync(smbAudit.ClientAddress).ConfigureAwait(false).GetAwaiter().GetResult(); 39 | smbAudit.ClientDNSName = Task.Run(() => QueryDnsAsync(smbAudit.ClientAddress)).Result; 40 | return smbAudit; 41 | } 42 | 43 | 44 | private static async Task QueryDnsAsync(string clientAddress) { 45 | if (string.IsNullOrEmpty(clientAddress)) { 46 | return null; 47 | } 48 | 49 | try { 50 | Settings._logger.WriteVerbose($"Querying DNS for address: {clientAddress}"); 51 | var result = await ClientX.QueryDns(clientAddress, DnsRecordType.PTR); 52 | var resolvedNames = string.Join(", ", result.AnswersMinimal.Select(answer => answer.Data)); 53 | Settings._logger.WriteVerbose($"Resolved names: {resolvedNames}"); 54 | return resolvedNames; 55 | } catch (Exception ex) { 56 | Settings._logger.WriteWarning($"Querying DNS for address: {clientAddress} failed: {ex.Message}"); 57 | return null; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserChangeDetailed.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory User Changes detailed 5 | /// 5136: A directory service object was modified 6 | /// 5137: A directory service object was created 7 | /// 5139: A directory service object was deleted 8 | /// 5141: A directory service object was moved 9 | /// 10 | public class ADUserChangeDetailed : EventObjectSlim { 11 | public string Computer; 12 | public string Action; 13 | public string ObjectClass; 14 | public string OperationType; 15 | public string Who; 16 | public DateTime When; 17 | public string User; // 'User Object' 18 | public string FieldChanged; // 'Field Changed' 19 | public string FieldValue; // 'Field Value' 20 | 21 | 22 | public ADUserChangeDetailed(EventObject eventObject) : base(eventObject) { 23 | _eventObject = eventObject; 24 | 25 | Type = "ADUserChangeDetailed"; 26 | Computer = _eventObject.ComputerName; 27 | Action = _eventObject.MessageSubject; 28 | ObjectClass = _eventObject.GetValueFromDataDictionary("ObjectClass"); 29 | OperationType = ConvertFromOperationType(_eventObject.Data["OperationType"]); 30 | User = _eventObject.GetValueFromDataDictionary("ObjectDN"); 31 | FieldChanged = _eventObject.GetValueFromDataDictionary("AttributeLDAPDisplayName"); 32 | FieldValue = _eventObject.GetValueFromDataDictionary("AttributeValue"); 33 | // common fields 34 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 35 | When = _eventObject.TimeCreated; 36 | 37 | // OverwriteByField logic 38 | User = OverwriteByField(Action, "A directory service object was moved.", User, _eventObject.GetValueFromDataDictionary("OldObjectDN")); 39 | FieldValue = OverwriteByField(Action, "A directory service object was moved.", FieldValue, _eventObject.GetValueFromDataDictionary("NewObjectDN")); 40 | } 41 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserCreateChange.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Includes users added or modified in Active Directory 5 | /// 4720: A user account was created 6 | /// 4738: A user account was changed 7 | /// 8 | public class ADUserCreateChange : EventObjectSlim { 9 | public string Computer; 10 | public string Action; 11 | public string UserAffected; 12 | public string SamAccountName; 13 | public string DisplayName; 14 | public string UserPrincipalName; 15 | public string HomeDirectory; 16 | public string HomePath; 17 | public string ScriptPath; 18 | public string ProfilePath; 19 | public string UserWorkstations; 20 | public string PasswordLastSet; 21 | public string AccountExpires; 22 | public string PrimaryGroupId; 23 | public string AllowedToDelegateTo; 24 | public string OldUacValue; 25 | public string NewUacValue; 26 | public string UserAccountControl; 27 | public string UserParameters; 28 | public string SidHistory; 29 | public string LogonHours; 30 | public string Who; 31 | public DateTime When; 32 | 33 | public ADUserCreateChange(EventObject eventObject) : base(eventObject) { 34 | // main object initialization 35 | _eventObject = eventObject; 36 | // dedicated properties initialization 37 | Type = "ADUserChange"; 38 | Computer = _eventObject.ComputerName; 39 | Action = _eventObject.MessageSubject; 40 | UserAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 41 | SamAccountName = _eventObject.GetValueFromDataDictionary("SamAccountName"); 42 | DisplayName = _eventObject.GetValueFromDataDictionary("DisplayName"); 43 | UserPrincipalName = _eventObject.GetValueFromDataDictionary("UserPrincipalName"); 44 | HomeDirectory = _eventObject.GetValueFromDataDictionary("HomeDirectory"); 45 | HomePath = _eventObject.GetValueFromDataDictionary("HomePath"); 46 | ScriptPath = _eventObject.GetValueFromDataDictionary("ScriptPath"); 47 | ProfilePath = _eventObject.GetValueFromDataDictionary("ProfilePath"); 48 | UserWorkstations = _eventObject.GetValueFromDataDictionary("UserWorkstations"); 49 | PasswordLastSet = _eventObject.GetValueFromDataDictionary("PasswordLastSet"); 50 | AccountExpires = _eventObject.GetValueFromDataDictionary("AccountExpires"); 51 | PrimaryGroupId = _eventObject.GetValueFromDataDictionary("PrimaryGroupId"); 52 | AllowedToDelegateTo = _eventObject.GetValueFromDataDictionary("AllowedToDelegateTo"); 53 | OldUacValue = _eventObject.GetValueFromDataDictionary("OldUacValue"); 54 | NewUacValue = _eventObject.GetValueFromDataDictionary("NewUacValue"); 55 | UserAccountControl = _eventObject.GetValueFromDataDictionary("UserAccountControl"); 56 | UserParameters = _eventObject.GetValueFromDataDictionary("UserParameters"); 57 | SidHistory = _eventObject.GetValueFromDataDictionary("SidHistory"); 58 | LogonHours = _eventObject.GetValueFromDataDictionary("LogonHours"); 59 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 60 | When = _eventObject.TimeCreated; 61 | 62 | // let's try to translate them 63 | OldUacValue = TranslateUacValue(OldUacValue); 64 | NewUacValue = TranslateUacValue(NewUacValue); 65 | UserAccountControl = TranslateUacValue(UserAccountControl); 66 | } 67 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserLockouts.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory User Lockouts 5 | /// 4740: A user account was locked out 6 | /// 7 | public class ADUserLockouts : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string ComputerLockoutOn; 11 | public string UserAffected; 12 | public string Who; 13 | public DateTime When; 14 | 15 | public ADUserLockouts(EventObject eventObject) : base(eventObject) { 16 | _eventObject = eventObject; 17 | Type = "ADUserLockouts"; 18 | 19 | Computer = _eventObject.ComputerName; 20 | Action = _eventObject.MessageSubject; 21 | 22 | ComputerLockoutOn = _eventObject.GetValueFromDataDictionary("TargetDomainName"); 23 | 24 | UserAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 25 | 26 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 27 | When = _eventObject.TimeCreated; 28 | } 29 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserLogon.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory User Logon 5 | /// 4624: An account was successfully logged on 6 | /// 7 | public class ADUserLogon : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string IpAddress; 11 | public string IpPort; 12 | public string ObjectAffected; 13 | public string Who; 14 | public DateTime When; 15 | public string LogonProcessName; 16 | public ImpersonationLevel? ImpersonationLevel; 17 | public VirtualAccount? VirtualAccount; 18 | public ElevatedToken? ElevatedToken; 19 | public LogonType? LogonType; 20 | 21 | public ADUserLogon(EventObject eventObject) : base(eventObject) { 22 | _eventObject = eventObject; 23 | Type = "ADUserLogon"; 24 | 25 | Computer = _eventObject.ComputerName; 26 | Action = _eventObject.MessageSubject; 27 | 28 | IpAddress = _eventObject.GetValueFromDataDictionary("IpAddress"); 29 | IpPort = _eventObject.GetValueFromDataDictionary("IpPort"); 30 | LogonProcessName = _eventObject.GetValueFromDataDictionary("LogonProcessName"); 31 | ImpersonationLevel = EventsHelper.GetImpersonationLevel(_eventObject.GetValueFromDataDictionary("ImpersonationLevel")); 32 | VirtualAccount = EventsHelper.GetVirtualAccount(_eventObject.GetValueFromDataDictionary("VirtualAccount")); 33 | ElevatedToken = EventsHelper.GetElevatedToken(_eventObject.GetValueFromDataDictionary("ElevatedToken")); 34 | LogonType = EventsHelper.GetLogonType(_eventObject.GetValueFromDataDictionary("LogonType")); 35 | 36 | ObjectAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 37 | 38 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 39 | When = _eventObject.TimeCreated; 40 | } 41 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserLogonFailed.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | //public enum FailureReason { 4 | // UnknownUserNameOrBadPassword, 5 | // AccountRestrictions, 6 | // AccountLockedOut, 7 | // AccountExpired, 8 | // LogonTypeNotGranted, 9 | // PasswordExpired 10 | //} 11 | 12 | // public enum FailureReason1 { 13 | // // Define failure reasons based on status codes 14 | // UnknownUserNameOrBadPassword = 0xC000006D, 15 | // AccountRestrictions = 0xC000006E, 16 | // AccountLockedOut = 0xC0000234, 17 | // AccountExpired = 0xC0000193, 18 | // LogonTypeNotGranted = 0xC000015B, 19 | // PasswordExpired = 0xC0000071 20 | // } 21 | 22 | /// 23 | /// Active Directory User Logon Failed (NTLM) 24 | /// 25 | /// Event ID: 4625 26 | /// 27 | public class ADUserLogonFailed : EventObjectSlim { 28 | public string Computer; 29 | public string Action; 30 | public string PackageName; 31 | public string IpAddress; 32 | public string IpPort; 33 | //public string WorkstationName; 34 | public string ObjectAffected; 35 | public string Who; 36 | public DateTime When; 37 | public string LogonProcessName; 38 | public LogonType? LogonType; 39 | public StatusCode? Status { get; private set; } 40 | public SubStatusCode? SubStatus { get; private set; } 41 | public FailureReason? FailureReason { get; private set; } 42 | public string LmPackageName; 43 | public string KeyLength; 44 | public string ProcessId; 45 | public string ProcessName; 46 | public string TransmittedServices; 47 | 48 | 49 | public ADUserLogonFailed(EventObject eventObject) : base(eventObject) { 50 | _eventObject = eventObject; 51 | Type = "ADUserLogonFailed"; 52 | Computer = _eventObject.ComputerName; 53 | Action = _eventObject.MessageSubject; 54 | //Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 55 | Who = _eventObject.GetValueFromDataDictionary("WorkstationName"); 56 | ObjectAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 57 | IpAddress = _eventObject.GetValueFromDataDictionary("IpAddress"); 58 | IpPort = _eventObject.GetValueFromDataDictionary("IpPort"); 59 | //WorkstationName = _eventObject.GetValueFromDataDictionary("WorkstationName"); 60 | LogonProcessName = _eventObject.GetValueFromDataDictionary("LogonProcessName"); 61 | LogonType = ParseLogonType(_eventObject.GetValueFromDataDictionary("LogonType")); 62 | // Parse Status, SubStatus, and FailureReason 63 | Status = ParseStatus(_eventObject.GetValueFromDataDictionary("Status")); 64 | SubStatus = ParseSubStatus(_eventObject.GetValueFromDataDictionary("SubStatus")); 65 | FailureReason = ParseFailureReason(_eventObject.GetValueFromDataDictionary("FailureReason")); 66 | 67 | LmPackageName = _eventObject.GetValueFromDataDictionary("LmPackageName"); 68 | KeyLength = _eventObject.GetValueFromDataDictionary("KeyLength"); 69 | ProcessId = _eventObject.GetValueFromDataDictionary("ProcessId"); 70 | ProcessName = _eventObject.GetValueFromDataDictionary("ProcessName"); 71 | TransmittedServices = _eventObject.GetValueFromDataDictionary("TransmittedServices"); 72 | PackageName = _eventObject.GetValueFromDataDictionary("AuthenticationPackageName"); 73 | When = _eventObject.TimeCreated; 74 | } 75 | 76 | private StatusCode? ParseStatus(string status) { 77 | if (string.IsNullOrEmpty(status)) return null; 78 | 79 | // Remove "0x" prefix if present and parse as hex 80 | status = status.ToLowerInvariant().Replace("0x", ""); 81 | if (uint.TryParse(status, System.Globalization.NumberStyles.HexNumber, null, out uint statusCode)) { 82 | return (StatusCode)statusCode; 83 | } 84 | return null; 85 | } 86 | 87 | private SubStatusCode? ParseSubStatus(string subStatus) { 88 | if (string.IsNullOrEmpty(subStatus)) return null; 89 | 90 | // Remove "0x" prefix if present and parse as hex 91 | subStatus = subStatus.ToLowerInvariant().Replace("0x", ""); 92 | if (uint.TryParse(subStatus, System.Globalization.NumberStyles.HexNumber, null, out uint subStatusCode)) { 93 | return (SubStatusCode)subStatusCode; 94 | } 95 | return null; 96 | } 97 | 98 | private FailureReason? ParseFailureReason(string reason) { 99 | if (string.IsNullOrEmpty(reason)) return null; 100 | 101 | // Remove "%%" prefix if present and parse as integer 102 | reason = reason.Replace("%%", ""); 103 | if (int.TryParse(reason, out int reasonCode)) { 104 | return (FailureReason)reasonCode; 105 | } 106 | return null; 107 | } 108 | 109 | private LogonType? ParseLogonType(string logonType) { 110 | if (int.TryParse(logonType, out int lt)) { 111 | return (LogonType)lt; 112 | } 113 | return null; 114 | } 115 | 116 | /// 117 | /// Gets a human-readable description of the failure 118 | /// 119 | public string GetFailureDescription() { 120 | var description = new System.Text.StringBuilder(); 121 | 122 | if (Status.HasValue) 123 | description.AppendLine($"Status: {Status} (0x{(uint)Status:X8})"); 124 | 125 | if (SubStatus.HasValue) 126 | description.AppendLine($"SubStatus: {SubStatus} (0x{(uint)SubStatus:X8})"); 127 | 128 | if (FailureReason.HasValue) 129 | description.AppendLine($"Reason: {FailureReason}"); 130 | 131 | return description.ToString().TrimEnd(); 132 | } 133 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserLogonKerberos.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory User Logon Kerberos 5 | /// 4768: A Kerberos authentication ticket (TGT) was requested 6 | /// 7 | public class ADUserLogonKerberos : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string ObjectAffected; 11 | public string IpAddress; 12 | public string IpPort; 13 | public string TicketOptions; 14 | public string Status; 15 | public string TicketEncryptionType; 16 | public string PreAuthType; 17 | public DateTime When; 18 | 19 | public ADUserLogonKerberos(EventObject eventObject) : base(eventObject) { 20 | _eventObject = eventObject; 21 | Type = "ADUserLogonKerberos"; 22 | 23 | Computer = _eventObject.ComputerName; 24 | Action = _eventObject.MessageSubject; 25 | ObjectAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 26 | IpAddress = _eventObject.GetValueFromDataDictionary("IpAddress"); 27 | IpPort = _eventObject.GetValueFromDataDictionary("IpPort"); 28 | TicketOptions = _eventObject.GetValueFromDataDictionary("TicketOptions"); 29 | Status = _eventObject.GetValueFromDataDictionary("Status"); 30 | TicketEncryptionType = _eventObject.GetValueFromDataDictionary("TicketEncryptionType"); 31 | PreAuthType = _eventObject.GetValueFromDataDictionary("PreAuthType"); 32 | 33 | When = _eventObject.TimeCreated; 34 | 35 | if (IpAddress == "::1") { 36 | IpAddress = "Localhost"; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserStatus.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// A user account was enabled, disabled, unlocked, password changed, password reset, or deleted 5 | /// 4722: A user account was enabled (this includes computer accounts) 6 | /// 4725: A user account was disabled (this includes computer accounts) 7 | /// 4767: A user account was unlocked 8 | /// 4723: An attempt was made to change an account's password 9 | /// 4724: An attempt was made to reset an account's password 10 | /// 4726: A user account was deleted 11 | /// 12 | public class ADUserStatus : EventObjectSlim { 13 | 14 | public string Computer; 15 | public string Action; 16 | public string Who; 17 | public DateTime When; 18 | public string UserAffected; 19 | 20 | public ADUserStatus(EventObject eventObject) : base(eventObject) { 21 | _eventObject = eventObject; 22 | Type = "ADUsersStatus"; 23 | 24 | Computer = _eventObject.ComputerName; 25 | Action = _eventObject.MessageSubject; 26 | 27 | UserAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 28 | 29 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 30 | When = _eventObject.TimeCreated; 31 | } 32 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/ActiveDirectory/Users/ADUserUnlocked.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.ActiveDirectory; 2 | 3 | /// 4 | /// Active Directory User Unlocked 5 | /// 4767: A user account was unlocked 6 | /// 7 | public class ADUserUnlocked : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string ComputerLockoutOn; 11 | public string UserAffected; 12 | public string Who; 13 | public DateTime When; 14 | 15 | public ADUserUnlocked(EventObject eventObject) : base(eventObject) { 16 | _eventObject = eventObject; 17 | Type = "ADUserUnlocked"; 18 | 19 | Computer = _eventObject.ComputerName; 20 | Action = _eventObject.MessageSubject; 21 | 22 | ComputerLockoutOn = _eventObject.GetValueFromDataDictionary("TargetDomainName"); 23 | 24 | UserAffected = _eventObject.GetValueFromDataDictionary("TargetUserName", "TargetDomainName", "\\", reverseOrder: true); 25 | 26 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 27 | When = _eventObject.TimeCreated; 28 | } 29 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Logging/LogsClearedOther.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Logging; 2 | 3 | /// 4 | /// Logs Cleared Application, System, Others 5 | /// 104: The audit log was cleared 6 | /// 7 | public class LogsClearedOther : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string BackupPath; 11 | public string LogType; 12 | public string Who; 13 | public DateTime When; 14 | 15 | public LogsClearedOther(EventObject eventObject) : base(eventObject) { 16 | _eventObject = eventObject; 17 | 18 | Type = "LogsClearedOther"; 19 | Computer = _eventObject.ComputerName; 20 | Action = _eventObject.MessageSubject; 21 | BackupPath = _eventObject.GetValueFromDataDictionary("BackupPath"); 22 | LogType = ConvertFromOperationType(_eventObject.Data["Channel"]); 23 | 24 | // common fields 25 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 26 | When = _eventObject.TimeCreated; 27 | 28 | 29 | if (BackupPath == "") { 30 | BackupPath = "N/A"; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Logging/LogsClearedSecurity.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Logging; 2 | 3 | /// 4 | /// Logs Cleared Security 5 | /// 1102: The audit log was cleared 6 | /// 1105: Event log automatic backup 7 | /// Url: https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-1105 8 | /// 9 | public class LogsClearedSecurity : EventObjectSlim { 10 | public string Computer; 11 | public string Action; 12 | public string BackupPath; 13 | public string LogType; 14 | public string Who; 15 | public DateTime When; 16 | 17 | public LogsClearedSecurity(EventObject eventObject) : base(eventObject) { 18 | _eventObject = eventObject; 19 | 20 | Type = "LogsClearedSecurity"; 21 | Computer = _eventObject.ComputerName; 22 | Action = _eventObject.MessageSubject; 23 | BackupPath = _eventObject.GetValueFromDataDictionary("BackupPath"); 24 | LogType = ConvertFromOperationType(_eventObject.Data["Channel"]); 25 | 26 | // common fields 27 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 28 | When = _eventObject.TimeCreated; 29 | 30 | if (_eventObject.Id == 1105) { 31 | Who = "Automatic Backup"; 32 | } 33 | if (BackupPath == "") { 34 | BackupPath = "N/A"; 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Logging/LogsFullSecurity.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Logging; 2 | 3 | /// 4 | /// Logs Security Full 5 | /// 1104: The security log is now full 6 | /// 7 | public class LogsFullSecurity : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string LogType; 11 | public string Who; 12 | public DateTime When; 13 | 14 | public LogsFullSecurity(EventObject eventObject) : base(eventObject) { 15 | _eventObject = eventObject; 16 | 17 | Type = "LogsFullSecurity"; 18 | Computer = _eventObject.ComputerName; 19 | Action = _eventObject.MessageSubject; 20 | LogType = ConvertFromOperationType(_eventObject.Data["Channel"]); 21 | 22 | // common fields 23 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 24 | When = _eventObject.TimeCreated; 25 | } 26 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/NPS/NetworkAccessAuthenticationPolicy.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.NPS; 2 | 3 | /// 4 | /// Network Access Authentication Policy 5 | /// 6272: Network Policy Server granted access to a user 6 | /// 6273: Network Policy Server denied access to a user 7 | /// 8 | public class NetworkAccessAuthenticationPolicy : EventObjectSlim { 9 | public string Computer; 10 | public string Action; 11 | public string SecurityID; 12 | public string AccountName; 13 | public string AccountDomain; 14 | public string CalledStationID; 15 | public string CallingStationID; 16 | 17 | public string NASIPv4Address; 18 | public string NASIPv6Address; 19 | public string NASIdentifier; 20 | public string NASPortType; 21 | public string NASPort; 22 | 23 | public string ClientFriendlyName; 24 | public string ClientFriendlyIPAddress; 25 | 26 | public string ConnectionRequestPolicyName; 27 | public string NetworkPolicyName; 28 | public string AuthenticationProvider; 29 | public string AuthenticationServer; 30 | public string AuthenticationType; 31 | public string EAPType; 32 | 33 | public string Reason; 34 | public string ReasonCode; 35 | 36 | public string Who; 37 | public DateTime When; 38 | 39 | public NetworkAccessAuthenticationPolicy(EventObject eventObject) : base(eventObject) { 40 | _eventObject = eventObject; 41 | 42 | Type = "NetworkAccessAuthenticationPolicy"; 43 | Computer = _eventObject.ComputerName; 44 | Action = _eventObject.MessageSubject; 45 | SecurityID = _eventObject.GetValueFromDataDictionary("SubjectUserSid"); 46 | AccountName = _eventObject.GetValueFromDataDictionary("SubjectUserName"); 47 | AccountDomain = _eventObject.GetValueFromDataDictionary("SubjectDomainName"); 48 | 49 | CalledStationID = _eventObject.GetValueFromDataDictionary("CalledStationID"); 50 | CallingStationID = _eventObject.GetValueFromDataDictionary("CallingStationID"); 51 | 52 | NASIPv4Address = _eventObject.GetValueFromDataDictionary("NASIPv4Address"); 53 | NASIPv6Address = _eventObject.GetValueFromDataDictionary("NASIPv6Address"); 54 | 55 | NASIdentifier = _eventObject.GetValueFromDataDictionary("NASIdentifier"); 56 | NASPort = _eventObject.GetValueFromDataDictionary("NASPort"); 57 | NASPortType = _eventObject.GetValueFromDataDictionary("NASPortType"); 58 | 59 | 60 | AuthenticationProvider = _eventObject.GetValueFromDataDictionary("AuthenticationProvider"); 61 | AuthenticationServer = _eventObject.GetValueFromDataDictionary("AuthenticationServer"); 62 | AuthenticationType = _eventObject.GetValueFromDataDictionary("AuthenticationType"); 63 | 64 | EAPType = _eventObject.GetValueFromDataDictionary("EAPType"); 65 | 66 | ClientFriendlyIPAddress = _eventObject.GetValueFromDataDictionary("ClientIPAddress"); 67 | ClientFriendlyName = _eventObject.GetValueFromDataDictionary("ClientName"); 68 | 69 | ConnectionRequestPolicyName = _eventObject.GetValueFromDataDictionary("ProxyPolicyName"); 70 | 71 | NetworkPolicyName = _eventObject.GetValueFromDataDictionary("NetworkPolicyName"); 72 | 73 | Reason = _eventObject.GetValueFromDataDictionary("Reason"); 74 | ReasonCode = _eventObject.GetValueFromDataDictionary("ReasonCode"); 75 | // common fields 76 | Who = _eventObject.GetValueFromDataDictionary("FullyQualifiedSubjectUserName"); 77 | When = _eventObject.TimeCreated; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Windows/ClientGroupPolicies.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Windows; 2 | 3 | internal class ClientGroupPolicies { 4 | //Log Name: Application 5 | // Source: Group Policy Files 6 | // Date: 10.02.2024 16:10:50 7 | // Event ID: 4098 8 | // Task Category: (2) 9 | // Level: Warning 10 | // Keywords: Classic 11 | // User: SYSTEM 12 | // Computer: EVOMONSTER.ad.evotec.xyz 13 | // Description: 14 | // The computer 'Install.ps1' preference item in the 'CopyFiles {76CA620F-5CEB-4316-A0D1-9311E6EE03B9}' Group Policy Object did not apply because it failed with error code '0x80070035 The network path was not found.' This error was suppressed. 15 | // Event Xml: 16 | // 17 | // 18 | // 19 | // 4098 20 | // 0 21 | // 3 22 | // 2 23 | // 0 24 | // 0x80000000000000 25 | // 26 | // 22479 27 | // 28 | // 29 | // Application 30 | // EVOMONSTER.ad.evotec.xyz 31 | // 32 | // 33 | // 34 | // computer 35 | // Install.ps1 36 | // CopyFiles {76CA620F-5CEB-4316-A0D1-9311E6EE03B9} 37 | // 0x80070035 The network path was not found. 38 | // 39 | // 40 | 41 | 42 | 43 | 44 | //Log Name: System 45 | // Source: Microsoft-Windows-GroupPolicy 46 | // Date: 11.02.2024 12:46:49 47 | // Event ID: 1085 48 | // Task Category: None 49 | // Level: Warning 50 | // Keywords: 51 | // User: SYSTEM 52 | // Computer: EVOMONSTER.ad.evotec.xyz 53 | // Description: 54 | // Windows failed to apply the MDM Policy settings. MDM Policy settings might have its own log file. Please click on the "More information" link. 55 | // Event Xml: 56 | // 57 | // 58 | // 59 | // 1085 60 | // 0 61 | // 3 62 | // 0 63 | // 1 64 | // 0x8000000000000000 65 | // 66 | // 34081 67 | // 68 | // 69 | // System 70 | // EVOMONSTER.ad.evotec.xyz 71 | // 72 | // 73 | // 74 | // 1 75 | // 5213 76 | // 0 77 | // 641 78 | // 2149056522 79 | // The device is already enrolled. 80 | // \\AD1.ad.evotec.xyz 81 | // MDM Policy 82 | // {7909AD9E-09EE-4247-BAB9-7029D5F0A278} 83 | // 84 | // 85 | } 86 | -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Windows/OSCrash.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Windows; 2 | 3 | /// 4 | /// Windows OS Crash 5 | /// 6008: The previous system shutdown at time on date was unexpected. 6 | /// 7 | public class OSCrash : EventObjectSlim { 8 | public string Computer; 9 | public string Action; 10 | public string ObjectAffected; 11 | public string ActionDetails; 12 | public string ActionDetailsDate; 13 | public string ActionDetailsTime; 14 | public string Who; 15 | public DateTime When; 16 | 17 | public OSCrash(EventObject eventObject) : base(eventObject) { 18 | _eventObject = eventObject; 19 | 20 | Type = "OSCrash"; 21 | Computer = _eventObject.ComputerName; 22 | Action = _eventObject.GetValueFromDataDictionary("EventAction"); 23 | ObjectAffected = _eventObject.MachineName; 24 | ActionDetails = _eventObject.MessageSubject; 25 | ActionDetailsDate = _eventObject.GetValueFromDataDictionary("NoNameA1"); 26 | ActionDetailsTime = _eventObject.GetValueFromDataDictionary("NoNameA0"); 27 | 28 | When = _eventObject.TimeCreated; 29 | } 30 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Windows/OSStartupShutdownCrash.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Windows; 2 | 3 | /// 4 | /// OS Startup, Shutdown, Crash 5 | /// 12: Windows is starting up 6 | /// 13: Windows is shutting down 7 | /// 41: The system was not cleanly shut down 8 | /// 4608: Windows is starting up 9 | /// 4621: Administrator recovered system from CrashOnAuditFail 10 | /// 6008: The previous system shutdown at time on date was unexpected. 11 | /// 12 | public class OSStartupShutdownCrash : EventObjectSlim { 13 | public string Computer; 14 | public string Action; 15 | public string ObjectAffected; 16 | public string ActionDetails; 17 | public string ActionDetailsDate; 18 | public string ActionDetailsTime; 19 | public string ActionDetailsDateTime; 20 | public DateTime When; 21 | 22 | public OSStartupShutdownCrash(EventObject eventObject) : base(eventObject) { 23 | _eventObject = eventObject; 24 | 25 | Type = "OSStartupShutdownCrash"; 26 | Computer = _eventObject.ComputerName; 27 | Action = _eventObject.GetValueFromDataDictionary("EventAction"); 28 | ObjectAffected = _eventObject.MachineName; 29 | ActionDetails = _eventObject.MessageSubject; 30 | ActionDetailsDate = _eventObject.GetValueFromDataDictionary("NoNameA1"); 31 | ActionDetailsTime = _eventObject.GetValueFromDataDictionary("NoNameA0"); 32 | ActionDetailsDateTime = _eventObject.GetValueFromDataDictionary("ActionDetailsDateTime"); 33 | 34 | When = _eventObject.TimeCreated; 35 | 36 | if (_eventObject.Id == 12) { 37 | Action = "System Start"; 38 | } else if (_eventObject.Id == 13) { 39 | Action = "System Shutdown"; 40 | } else if (_eventObject.Id == 41) { 41 | Action = "System Dirty Reboot"; 42 | } else if (_eventObject.Id == 4608) { 43 | Action = "Windows is starting up"; 44 | } else if (_eventObject.Id == 4621) { 45 | Action = "Administrator recovered system from CrashOnAuditFail"; 46 | } else if (_eventObject.Id == 6008) { 47 | Action = "System Crash"; 48 | } 49 | 50 | var startTime = _eventObject.GetValueFromDataDictionary("StartTime"); 51 | if (startTime != null) { 52 | ActionDetailsDateTime = startTime; 53 | } else { 54 | var text = _eventObject.GetValueFromDataDictionary("#text"); 55 | if (text != null) { 56 | ActionDetailsDateTime = text; 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Rules/Windows/OSTimeChange.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX.Rules.Windows; 2 | 3 | /// 4 | /// OS Time Change 5 | /// 4616: The system time was changed 6 | /// 7 | /// 8 | public class OSTimeChange : EventObjectSlim { 9 | public string Computer; 10 | public string Action; 11 | public string ObjectAffected; 12 | public string PreviousTime; 13 | public string NewTime; 14 | public string Who; 15 | public DateTime When; 16 | 17 | public OSTimeChange(EventObject eventObject) : base(eventObject) { 18 | _eventObject = eventObject; 19 | 20 | Type = "OSTimeChange"; 21 | Computer = _eventObject.ComputerName; 22 | Action = _eventObject.MessageSubject; 23 | ObjectAffected = _eventObject.MachineName; 24 | PreviousTime = _eventObject.GetValueFromDataDictionary("PreviousTime"); 25 | NewTime = _eventObject.GetValueFromDataDictionary("NewTime"); 26 | 27 | Who = _eventObject.GetValueFromDataDictionary("SubjectUserName", "SubjectDomainName", "\\", reverseOrder: true); 28 | When = _eventObject.TimeCreated; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/EventViewerX/SearchEvents.GetProviders.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX { 2 | public partial class SearchEvents : Settings { 3 | 4 | //public static void GetProviderList() { 5 | // // Create a new event log session 6 | // using (EventLogSession session = new EventLogSession()) { 7 | // // Get the names of all providers 8 | // var providerNames = session.GetProviderNames(); 9 | 10 | // // Get all event logs and store them in a dictionary 11 | // var logDictionary = EventLog.GetEventLogs().ToDictionary(log => log.LogDisplayName, log => log.Log, StringComparer.OrdinalIgnoreCase); 12 | 13 | // // For each provider, get its metadata and print its name, ID, and corresponding event log 14 | // foreach (var providerName in providerNames) { 15 | // try { 16 | // using (var providerMetadata = new ProviderMetadata(providerName)) { 17 | // string logName = logDictionary.ContainsKey(providerName) ? logDictionary[providerName] : "Not available"; 18 | // Console.WriteLine($"Provider Name: {providerMetadata.Name}, Provider ID: {providerMetadata.Id}, Log: {logName}"); 19 | // } 20 | // } catch (EventLogException) { 21 | // // Some providers may not support querying for metadata 22 | // // In that case, just print the provider name 23 | // Console.WriteLine($"Provider Name: {providerName}, Provider ID: Not available"); 24 | // } 25 | // } 26 | // } 27 | //} 28 | } 29 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/SearchEvents.GetPublisher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Eventing.Reader; 4 | using System.Linq; 5 | 6 | namespace EventViewerX { 7 | public partial class SearchEvents : Settings { 8 | 9 | public static IEnumerable GetProviders() { 10 | EventLogSession session = new EventLogSession(); 11 | foreach (string providerName in session.GetProviderNames()) { 12 | Metadata metadata = null; 13 | try { 14 | using ProviderMetadata providerMetadata = new ProviderMetadata(providerName); 15 | metadata = new Metadata(providerName, providerMetadata); 16 | } catch (EventLogInvalidDataException ex) { 17 | _logger.WriteWarning($"Error reading data for provider {providerName}: {ex.Message}"); 18 | } catch (EventLogException ex) { 19 | _logger.WriteWarning($"Error reading metadata for provider {providerName}: {ex.Message}"); 20 | } catch (UnauthorizedAccessException ex) { 21 | _logger.WriteWarning($"Access denied reading metadata for provider {providerName}: {ex.Message}"); 22 | } catch (Exception ex) { 23 | _logger.WriteWarning($"Error reading metadata for provider {providerName}: {ex.Message}"); 24 | } 25 | if (metadata != null) { 26 | yield return metadata; 27 | } 28 | } 29 | } 30 | 31 | public static List GetProviderList() { 32 | return GetProviders().ToList(); 33 | } 34 | 35 | } 36 | 37 | public class Metadata { 38 | public string ProviderName; 39 | public string DisplayName; 40 | public List LogNames; 41 | public Guid Id; 42 | 43 | public IList LogLinks { get; set; } 44 | public IEnumerable Events { get; set; } 45 | 46 | public IList Keywords { get; set; } 47 | 48 | public IList Opcodes { get; set; } 49 | 50 | public string MessageFilePath { get; set; } 51 | 52 | public string ResourceFilePath { get; set; } 53 | 54 | public string ParameterFilePath { get; set; } 55 | 56 | public IList Tasks { get; set; } 57 | 58 | public List Errors = new List(); 59 | 60 | public Metadata(string providerName, ProviderMetadata providerMetadata) { 61 | ProviderName = providerName; 62 | Id = providerMetadata.Id; 63 | MessageFilePath = providerMetadata.MessageFilePath; 64 | Keywords = providerMetadata.Keywords; 65 | 66 | TrySetMetadata(() => DisplayName = providerMetadata.DisplayName, "display name", providerName); 67 | TrySetMetadata(() => LogLinks = providerMetadata.LogLinks, "log links", providerName); 68 | TrySetMetadata(() => LogNames = providerMetadata.LogLinks.Select(link => link.LogName).ToList(), "log names", providerName); 69 | TrySetMetadata(() => ParameterFilePath = providerMetadata.ParameterFilePath, "parameter file path", providerName); 70 | TrySetMetadata(() => ResourceFilePath = providerMetadata.ResourceFilePath, "resource file path", providerName); 71 | TrySetMetadata(() => Opcodes = providerMetadata.Opcodes, "opcodes", providerName); 72 | TrySetMetadata(() => Tasks = providerMetadata.Tasks, "tasks", providerName); 73 | TrySetMetadata(() => Events = providerMetadata.Events, "events", providerName); 74 | } 75 | 76 | private void TrySetMetadata(Action setAction, string metadataType, string providerName) { 77 | try { 78 | setAction(); 79 | } catch (Exception ex) { 80 | Errors.Add($"Error reading {metadataType} for provider {providerName}. Details: {ex.Message}"); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/EventViewerX/SearchEvents.LogDetails.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.Eventing.Reader; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace EventViewerX; 5 | 6 | public partial class SearchEvents : Settings { 7 | 8 | public static IEnumerable DisplayEventLogs(string[] listLog = null, string machineName = null) { 9 | EventLogSession session; 10 | if (machineName != null) { 11 | session = new EventLogSession(machineName); 12 | } else { 13 | session = new EventLogSession(); 14 | // let's provide the machine name for the sake of logging 15 | machineName = GetFQDN(); 16 | } 17 | var logNames = session.GetLogNames(); 18 | foreach (string logName in logNames) { 19 | if (listLog != null) { 20 | // Check if any log matches the pattern 21 | if (!listLog.Any(log => { 22 | // Replace '*' with '.*' and '?' with '.' to convert the wildcard to a regex pattern 23 | var regexPattern = "^" + Regex.Escape(log).Replace("\\*", ".*").Replace("\\?", ".") + "$"; 24 | return Regex.IsMatch(logName, regexPattern, RegexOptions.IgnoreCase); 25 | })) { 26 | continue; 27 | } 28 | } 29 | EventLogConfiguration logConfig; 30 | try { 31 | logConfig = new EventLogConfiguration(logName, session); 32 | // Rest of your code... 33 | } catch (EventLogException ex) { 34 | logConfig = null; 35 | _logger.WriteWarning("Couldn't create EventLogConfiguration for " + logName + ". Error: " + ex.Message); 36 | } 37 | EventLogInformation logInfoObj; 38 | try { 39 | logInfoObj = session.GetLogInformation(logName, PathType.LogName); 40 | } catch (Exception ex) { 41 | logInfoObj = null; 42 | _logger.WriteWarning("Couldn't get log information for " + logName + ". Error: " + ex.Message); 43 | } 44 | 45 | if (logConfig == null) { 46 | _logger.WriteWarning("LogConfig is null for " + logName); 47 | continue; 48 | } 49 | EventLogDetails logDetails = new EventLogDetails(_logger, machineName, logConfig, logInfoObj); 50 | yield return logDetails; 51 | 52 | } 53 | } 54 | 55 | public static IEnumerable DisplayEventLogsParallel(string[] listLog = null, List machineNames = null, int maxDegreeOfParallelism = 8) { 56 | if (machineNames == null || !machineNames.Any()) { 57 | machineNames = new List { null }; 58 | } 59 | 60 | BlockingCollection logDetailsCollection = new BlockingCollection(); 61 | 62 | Task.Factory.StartNew(() => { 63 | Parallel.ForEach(machineNames, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }, machineName => { 64 | try { 65 | _logger.WriteVerbose("Querying event log settings on " + machineName); 66 | foreach (var logDetails in DisplayEventLogs(listLog, machineName)) { 67 | logDetailsCollection.Add(logDetails); 68 | } 69 | } catch (Exception ex) { 70 | _logger.WriteWarning("Error querying event log settings on " + machineName + ". Error: " + ex.Message); 71 | } 72 | }); 73 | logDetailsCollection.CompleteAdding(); 74 | }); 75 | 76 | while (!logDetailsCollection.IsCompleted) { 77 | EventLogDetails logDetails; 78 | while (logDetailsCollection.TryTake(out logDetails)) { 79 | yield return logDetails; 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/SearchEvents.QueryNamedEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace EventViewerX { 6 | public partial class SearchEvents : Settings { 7 | 8 | public static IEnumerable FindEventsByNamedEvents(List typeEventsList, List machineNames = null, DateTime? startTime = null, DateTime? endTime = null, TimePeriod? timePeriod = null, int maxThreads = 8, int maxEvents = 0) { 9 | // Create a dictionary to store unique event IDs and log names 10 | var eventInfoDict = new Dictionary>(); 11 | 12 | foreach (var typeEvents in typeEventsList) { 13 | // Look up the list of event IDs and the log name based on typeEvents 14 | if (!eventIdsMap.TryGetValue(typeEvents, out var eventInfo)) { 15 | throw new ArgumentException($"Invalid typeEvents value: {typeEvents}"); 16 | } 17 | 18 | // Add the event IDs to the dictionary 19 | if (!eventInfoDict.TryGetValue(eventInfo.LogName, out var eventIds)) { 20 | eventIds = new HashSet(); 21 | eventInfoDict[eventInfo.LogName] = eventIds; 22 | } 23 | 24 | foreach (var eventId in eventInfo.EventIds) { 25 | eventIds.Add(eventId); 26 | } 27 | } 28 | 29 | // Query each log name with the corresponding event IDs 30 | foreach (var kvp in eventInfoDict) { 31 | var logName = kvp.Key; 32 | var eventIds = kvp.Value.ToList(); 33 | 34 | foreach (var foundEvent in SearchEvents.QueryLogsParallel(logName, eventIds, machineNames, startTime: startTime, endTime: endTime, timePeriod: timePeriod, maxThreads: maxThreads, maxEvents: maxEvents)) { 35 | _logger.WriteDebug($"Found event: {foundEvent.Id} {foundEvent.LogName} {foundEvent.ComputerName}"); 36 | // yield return BuildTargetEvents(foundEvent, typeEventsList); 37 | var targetEvent = BuildTargetEvents(foundEvent, typeEventsList); 38 | if (targetEvent != null) { 39 | yield return targetEvent; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/EventViewerX/SearchEvents.WriteEvent.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | public partial class SearchEvents : Settings { 4 | 5 | /// 6 | /// Writes to EventLog, with optional replacement strings and no raw data. 7 | /// 8 | /// The source. 9 | /// The log. 10 | /// The message. 11 | /// The type. 12 | /// The category. 13 | /// The event identifier. 14 | /// Name of the machine. 15 | /// The replacement strings. 16 | public static void WriteEvent(string source, string log, string message, EventLogEntryType type, int category, int eventId, string machineName, params string[] replacementStrings) { 17 | WriteEvent(source, log, message, type, category, eventId, machineName, null, replacementStrings); 18 | } 19 | 20 | /// 21 | /// Writes to EventLog, with optional replacement strings and no raw data, and with default category of 0. 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static void WriteEvent(string source, string log, string message, EventLogEntryType type, int eventId, string machineName, params string[] replacementStrings) { 31 | WriteEvent(source, log, message, type, 0, eventId, machineName, null, replacementStrings); 32 | } 33 | 34 | /// 35 | /// Writes to EventLog, with replacement strings and/or raw data. 36 | /// 37 | /// The source. 38 | /// The log. 39 | /// The message. 40 | /// The type. 41 | /// The category. 42 | /// The event identifier. 43 | /// Name of the machine. 44 | /// The raw data. 45 | /// The replacement strings. 46 | public static void WriteEvent(string source, string log, string message, EventLogEntryType type, int category, int eventId, string machineName, byte[] rawData, params string[] replacementStrings) { 47 | // Check if the event source exists. If not, create it. 48 | var sourceExists = CreateLogSource(source, log, machineName); 49 | if (sourceExists) { 50 | // Create an EventInstance object for the event log entry. 51 | EventInstance eventInstance = new EventInstance(eventId, category, type); 52 | 53 | // Write an entry to the event log. 54 | using (EventLog eventLog = new EventLog(log)) { 55 | eventLog.Source = source; 56 | if (!String.IsNullOrEmpty(machineName)) { 57 | eventLog.MachineName = machineName; 58 | } 59 | if (rawData == null && (replacementStrings == null || replacementStrings.Length == 0)) { 60 | // If rawData and replacementStrings are not provided, write a simple message to the event log. 61 | eventLog.WriteEntry(message, type, eventId, (short)category); 62 | } else { 63 | var joinedMessage = replacementStrings.Prepend(message).ToArray(); 64 | // If rawData and/or replacementStrings are provided, write them to the event log. 65 | eventLog.WriteEvent(eventInstance, rawData, joinedMessage); 66 | } 67 | } 68 | 69 | } 70 | } 71 | 72 | /// 73 | /// Creates the log source. 74 | /// 75 | /// The source. 76 | /// The log. 77 | /// Name of the machine. 78 | /// 79 | public static bool CreateLogSource(string source, string log, string machineName = null) { 80 | try { 81 | if (string.IsNullOrEmpty(machineName)) { 82 | if (!EventLog.SourceExists(source)) { 83 | EventLog.CreateEventSource(source, log); 84 | } 85 | } else { 86 | if (!EventLog.SourceExists(source, machineName)) { 87 | EventLog.CreateEventSource(new EventSourceCreationData(source, log) { MachineName = machineName }); 88 | } 89 | } 90 | return true; 91 | } catch (System.Security.SecurityException ex) { 92 | // Handle the security exception 93 | _logger.WriteWarning("Couldn't create event log. Error: " + ex.Message); 94 | } catch (System.ArgumentException ex) { 95 | // Handle argument exception 96 | _logger.WriteWarning("Couldn't create event log. Error: " + ex.Message); 97 | } catch (Exception ex) { 98 | // Handle any other type of exception 99 | _logger.WriteWarning("Couldn't create event log. Error: " + ex.Message); 100 | } 101 | 102 | return false; 103 | } 104 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace EventViewerX; 2 | 3 | public class Settings { 4 | public static InternalLogger _logger = new InternalLogger(); 5 | 6 | public bool Error { 7 | get => _logger.IsError; 8 | set => _logger.IsError = value; 9 | } 10 | 11 | public bool Verbose { 12 | get => _logger.IsVerbose; 13 | set => _logger.IsVerbose = value; 14 | } 15 | 16 | public bool Warning { 17 | get => _logger.IsWarning; 18 | set => _logger.IsWarning = value; 19 | } 20 | 21 | public bool Progress { 22 | get => _logger.IsProgress; 23 | set => _logger.IsProgress = value; 24 | } 25 | 26 | public bool Debug { 27 | get => _logger.IsDebug; 28 | set => _logger.IsDebug = value; 29 | } 30 | 31 | /// 32 | /// Number of threads to use for lingering object detection 33 | /// 34 | public int NumberOfThreads = 8; 35 | 36 | protected readonly object _LockObject = new object(); 37 | } -------------------------------------------------------------------------------- /Sources/EventViewerX/WatchEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.Eventing.Reader; 5 | using System.Linq; 6 | using System.Threading; 7 | 8 | namespace EventViewerX { 9 | public class WatchEvents : Settings { 10 | public static volatile int NumberOfEventsFound = 0; 11 | /// 12 | /// List of event IDs to watch for 13 | /// 14 | private ConcurrentBag _watchEventId = new ConcurrentBag(); 15 | 16 | /// 17 | /// Events 18 | /// 19 | readonly ConcurrentDictionary WatchedEvents = new ConcurrentDictionary(); 20 | 21 | private string _machineName; 22 | 23 | public WatchEvents(InternalLogger internalLogger = null) { 24 | if (internalLogger != null) { 25 | _logger = internalLogger; 26 | } 27 | } 28 | 29 | public void Watch(string machineName, string logName, List eventId) { 30 | _machineName = machineName; 31 | _watchEventId = new ConcurrentBag(eventId); 32 | try { 33 | var eventLogSession = new EventLogSession(machineName); 34 | var eventLogWatcher = new EventLogWatcher(new EventLogQuery(logName, PathType.LogName) { 35 | Session = eventLogSession 36 | }); 37 | eventLogWatcher.EventRecordWritten += DetectEventsLogCallback; 38 | eventLogWatcher.Enabled = true; 39 | _logger.WriteVerbose("Created event log subscription to {0}.", machineName); 40 | } catch (Exception ex) { 41 | _logger.WriteWarning("Failed to create event log subscription to Target Machine {0}. Verify network connectivity, firewall settings, permissions, etc. Continuing on to next DC if applicable... ({1})", machineName, ex.Message.Trim()); 42 | } 43 | } 44 | private void DetectEventsLogCallback(object Object, EventRecordWrittenEventArgs Args) { 45 | try { 46 | if (Args.EventRecord == null) { 47 | _logger.WriteWarning("Event log subscription callback was given invalid data. This shouldn't happen."); 48 | } 49 | } catch (Exception ex) { 50 | _logger.WriteWarning("Event log subscription callback threw: ({0})", ex.Message.Trim()); 51 | return; 52 | } 53 | if (Args.EventRecord != null) { 54 | var Event = Args.EventRecord; 55 | if (_watchEventId.Contains(Event.Id)) { 56 | Interlocked.Increment(ref NumberOfEventsFound); 57 | _logger.WriteVerbose("Found event id {0} on {1}.", Event.Id, Event.MachineName); 58 | 59 | var eventObject = new EventObject(Event, _machineName); 60 | WatchedEvents.TryAdd(eventObject, 0); 61 | } 62 | } else { 63 | _logger.WriteWarning("Event log subscription callback was given invalid data. This shouldn't happen."); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Sources/PSEventViewer/CmdletStartEventWatching.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using System.Threading.Tasks; 3 | using EventViewerX; 4 | 5 | namespace PSEventViewer { 6 | [Cmdlet(VerbsLifecycle.Start, "EventWatching")] 7 | public sealed class CmdletStartEventWatching : AsyncPSCmdlet { 8 | private WatchEvents EventWatching { get; set; } 9 | 10 | [Parameter(Mandatory = false, Position = 1)] public int NumberOfThreads { get; set; } = 8; 11 | 12 | protected override Task BeginProcessingAsync() { 13 | // Initialize the logger to be able to see verbose, warning, debug, error, progress, and information messages. 14 | var internalLogger = new InternalLogger(false); 15 | var internalLoggerPowerShell = new InternalLoggerPowerShell(internalLogger, this.WriteVerbose, this.WriteWarning, this.WriteDebug, this.WriteError, this.WriteProgress, this.WriteInformation); 16 | 17 | EventWatching = new WatchEvents(internalLogger) { 18 | NumberOfThreads = NumberOfThreads 19 | }; 20 | return Task.CompletedTask; 21 | } 22 | protected override Task ProcessRecordAsync() { 23 | 24 | return Task.CompletedTask; 25 | } 26 | protected override Task EndProcessingAsync() { 27 | 28 | return Task.CompletedTask; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Sources/PSEventViewer/CmdletWriteWinEvent.cs: -------------------------------------------------------------------------------- 1 | namespace PSEventViewer; 2 | 3 | [Alias("Write-Event")] 4 | [Cmdlet(VerbsCommunications.Write, "WinEvent")] 5 | public sealed class CmdletWriteWinEvent : AsyncPSCmdlet { 6 | [Alias("ComputerName", "ServerName")] 7 | [Parameter(Mandatory = false, ParameterSetName = "GenericEvents")] 8 | public string MachineName; 9 | 10 | [Parameter(Mandatory = true, Position = 0, ParameterSetName = "RecordId")] 11 | [Parameter(Mandatory = true, Position = 0, ParameterSetName = "GenericEvents")] 12 | public string LogName; 13 | 14 | [Alias("Source", "Provider")] 15 | [Parameter(Mandatory = true, ParameterSetName = "GenericEvents")] 16 | public string ProviderName; 17 | 18 | [Parameter(Mandatory = false, ParameterSetName = "GenericEvents")] 19 | public int Category; 20 | 21 | [Alias("EntryType")] 22 | [Parameter(Mandatory = false, ParameterSetName = "GenericEvents")] 23 | public System.Diagnostics.EventLogEntryType EventLogEntryType = System.Diagnostics.EventLogEntryType.Information; 24 | 25 | [Alias("Id")] 26 | [Parameter(Mandatory = true, ParameterSetName = "GenericEvents")] 27 | public int EventId; 28 | 29 | [Parameter(Mandatory = true, ParameterSetName = "GenericEvents")] 30 | public string Message; 31 | 32 | [Parameter(Mandatory = false, ParameterSetName = "GenericEvents")] 33 | public string[] AdditionalFields; 34 | 35 | 36 | protected override Task BeginProcessingAsync() { 37 | // Initialize the logger to be able to see verbose, warning, debug, error, progress, and information messages. 38 | var internalLogger = new InternalLogger(false); 39 | var internalLoggerPowerShell = new InternalLoggerPowerShell(internalLogger, this.WriteVerbose, this.WriteWarning, this.WriteDebug, this.WriteError, this.WriteProgress, this.WriteInformation); 40 | var searchEvents = new SearchEvents(internalLogger); 41 | return Task.CompletedTask; 42 | } 43 | protected override Task ProcessRecordAsync() { 44 | SearchEvents.WriteEvent(ProviderName, LogName, Message, EventLogEntryType, Category, EventId, MachineName, AdditionalFields); 45 | return Task.CompletedTask; 46 | } 47 | } -------------------------------------------------------------------------------- /Sources/PSEventViewer/Communication/AsyncPSCmdlet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Management.Automation; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace PSEventViewer { 8 | public abstract class AsyncPSCmdlet : PSCmdlet, IDisposable { 9 | private enum PipelineType { 10 | Output, 11 | OutputEnumerate, 12 | Error, 13 | Warning, 14 | Verbose, 15 | Debug, 16 | Information, 17 | Progress, 18 | } 19 | 20 | private CancellationTokenSource _cancelSource = new(); 21 | 22 | private BlockingCollection<(object?, PipelineType)>? _currentPipe; 23 | 24 | protected CancellationToken CancelToken { get => _cancelSource.Token; } 25 | 26 | protected override void BeginProcessing() 27 | => RunBlockInAsync(BeginProcessingAsync); 28 | 29 | protected virtual Task BeginProcessingAsync() 30 | => Task.CompletedTask; 31 | 32 | protected override void ProcessRecord() 33 | => RunBlockInAsync(ProcessRecordAsync); 34 | 35 | protected virtual Task ProcessRecordAsync() 36 | => Task.CompletedTask; 37 | 38 | protected override void EndProcessing() 39 | => RunBlockInAsync(EndProcessingAsync); 40 | 41 | protected virtual Task EndProcessingAsync() 42 | => Task.CompletedTask; 43 | 44 | protected override void StopProcessing() 45 | => _cancelSource?.Cancel(); 46 | 47 | private void RunBlockInAsync(Func task) { 48 | using BlockingCollection<(object?, PipelineType)> pipe = new(); 49 | Task blockTask = Task.Run(async () => { 50 | try { 51 | _currentPipe = pipe; 52 | await task(); 53 | } finally { 54 | _currentPipe = null; 55 | pipe.CompleteAdding(); 56 | } 57 | }); 58 | 59 | foreach ((object? data, PipelineType pipelineType) in pipe.GetConsumingEnumerable()) { 60 | switch (pipelineType) { 61 | case PipelineType.Output: 62 | base.WriteObject(data); 63 | break; 64 | 65 | case PipelineType.OutputEnumerate: 66 | base.WriteObject(data, true); 67 | break; 68 | 69 | case PipelineType.Error: 70 | base.WriteError((ErrorRecord)data!); 71 | break; 72 | 73 | case PipelineType.Warning: 74 | base.WriteWarning((string)data!); 75 | break; 76 | 77 | case PipelineType.Verbose: 78 | base.WriteVerbose((string)data!); 79 | break; 80 | 81 | case PipelineType.Debug: 82 | base.WriteDebug((string)data!); 83 | break; 84 | 85 | case PipelineType.Information: 86 | base.WriteInformation((InformationRecord)data!); 87 | break; 88 | 89 | case PipelineType.Progress: 90 | base.WriteProgress((ProgressRecord)data!); 91 | break; 92 | } 93 | } 94 | 95 | blockTask.GetAwaiter().GetResult(); 96 | } 97 | 98 | public new void WriteObject(object? sendToPipeline) => WriteObject(sendToPipeline, false); 99 | 100 | public new void WriteObject(object? sendToPipeline, bool enumerateCollection) { 101 | ThrowIfStopped(); 102 | _currentPipe?.Add( 103 | (sendToPipeline, enumerateCollection ? PipelineType.OutputEnumerate : PipelineType.Output)); 104 | } 105 | 106 | public new void WriteError(ErrorRecord errorRecord) { 107 | ThrowIfStopped(); 108 | _currentPipe?.Add((errorRecord, PipelineType.Error)); 109 | } 110 | 111 | public new void WriteWarning(string message) { 112 | ThrowIfStopped(); 113 | _currentPipe?.Add((message, PipelineType.Warning)); 114 | } 115 | 116 | public new void WriteVerbose(string message) { 117 | ThrowIfStopped(); 118 | _currentPipe?.Add((message, PipelineType.Verbose)); 119 | } 120 | 121 | public new void WriteDebug(string message) { 122 | ThrowIfStopped(); 123 | _currentPipe?.Add((message, PipelineType.Debug)); 124 | } 125 | 126 | public new void WriteInformation(InformationRecord informationRecord) { 127 | ThrowIfStopped(); 128 | _currentPipe?.Add((informationRecord, PipelineType.Information)); 129 | } 130 | 131 | public new void WriteProgress(ProgressRecord progressRecord) { 132 | ThrowIfStopped(); 133 | _currentPipe?.Add((progressRecord, PipelineType.Progress)); 134 | } 135 | 136 | internal void ThrowIfStopped() { 137 | if (_cancelSource.IsCancellationRequested) { 138 | throw new PipelineStoppedException(); 139 | } 140 | } 141 | 142 | public void Dispose() { 143 | _cancelSource?.Dispose(); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Sources/PSEventViewer/PSEventViewer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net472;netstandard2.0;net8.0 5 | PowerShell Module for working with Event Logs 6 | PSEventViewer 7 | PSEventViewer 8 | 2.1.0 9 | false 10 | Evotec 11 | Przemyslaw Klys 12 | latest 13 | true 14 | windows 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Sources/PSEventViewer/PSEventViewer.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True -------------------------------------------------------------------------------- /Tests/Get-EventFilters.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe 'Get-EventsFilters' { 2 | It '-NamedDataFilter single value should return single named filter query "="' { 3 | $XPath = Get-EventsFilter -NamedDataFilter @{ FieldName = 'Value1' } -LogName 'xx' -XPathOnly 4 | $Xpath | Should -Be '*[EventData[Data[@Name=''FieldName''] = ''Value1'']]' 5 | } 6 | It '-NamedDataFilter two-value array should return "or"ed named filter query "="' { 7 | $XPath = Get-EventsFilter -NamedDataFilter @{ FieldName = ('Value1','Value2') } -LogName 'xx' -XPathOnly 8 | $Xpath | Should -Be '*[EventData[Data[@Name=''FieldName''] = ''Value1'' or Data[@Name=''FieldName''] = ''Value2'']]' 9 | } 10 | It '-NamedDataExcludeFilter should return single named filter query "!="' { 11 | $XPath = Get-EventsFilter -NamedDataExcludeFilter @{ FieldName = 'Value1' } -LogName 'xx' -XPathOnly 12 | $Xpath | Should -Be '*[EventData[Data[@Name=''FieldName''] != ''Value1'']]' 13 | } 14 | It '-NamedDataExcludeFilter two-value array should return "and"ed named filter query "!="' { 15 | $XPath = Get-EventsFilter -NamedDataExcludeFilter @{ FieldName = ('Value1','Value2') } -LogName 'xx' -XPathOnly 16 | $Xpath | Should -Be '*[EventData[Data[@Name=''FieldName''] != ''Value1'' and Data[@Name=''FieldName''] != ''Value2'']]' 17 | } 18 | } 19 | 20 | Describe "Get-EventFilters using Path and NamendDataFilter" { 21 | It "basic syntax check for queries of eventlogs" { 22 | $XML = Get-EventsFilter -logname System -Id 7040 -NamedDataExcludeFilter @{ param4 = 'BITS' } 23 | $XML | Should -BeLike '*Query Id="0" Path="system"*' -Because 'We wanted to query a filepath' 24 | $XML | Should -BeLike '*Select Path="system"*' -Because 'Queries using eventfiles do not have a Channel' 25 | } 26 | It "basic syntax check for queries of saved eventlog files" { 27 | $FilePath = [System.IO.Path]::Combine($PSScriptRoot, 'Logs', 'NamedFilterExamples.evtx') 28 | $XML = Get-EventsFilter -Path $FilePath -Id 7040 -NamedDataExcludeFilter @{ param4 = 'BITS' } 29 | $XML | Should -BeLike '*Query Id="0" Path="file://*' -Because 'We wanted to query a filepath' 30 | $XML | Should -Not -Belike '*Select Path*' -Because 'Queries using eventfiles do not have a Channel' 31 | } 32 | } -------------------------------------------------------------------------------- /Tests/Logs/Active Directory Web Services.evtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PSEventViewer/e4377372397fd24570cf441cfa273fff6c57915b/Tests/Logs/Active Directory Web Services.evtx -------------------------------------------------------------------------------- /Tests/Logs/NamedFilterExamples.evtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PSEventViewer/e4377372397fd24570cf441cfa273fff6c57915b/Tests/Logs/NamedFilterExamples.evtx --------------------------------------------------------------------------------