├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── Config ├── README.md └── sample-sysmon-config.xml ├── LICENSE ├── README.md ├── Resources ├── Analyze-SysmonEventData.ps1 ├── Get-SysmonEventData.ps1 └── Update-SysmonDeployment.ps1 ├── Testing-CurrentVersion └── README.md ├── Testing-NewVersion └── README.md ├── Update-Sysmon-Pseudocode.txt ├── Update-Sysmon.ps1 ├── Update-SysmonDomainLauncher.ps1 ├── x64 └── README.md └── x86 └── README.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Do not track backup directories 2 | **/Backups 3 | 4 | #Do not track testing sub-directories 5 | Testing-CurrentVersion/Config 6 | Testing-CurrentVersion/x64 7 | Testing-CurrentVersion/x86 8 | Testing-CurrentVersion/Update-Sysmon.ps1 9 | Testing-NewVersion/Config 10 | Testing-NewVersion/x64 11 | Testing-NewVersion/x86 12 | Testing-NewVersion/Update-Sysmon.ps1 13 | 14 | #Do not track executables 15 | *.exe -------------------------------------------------------------------------------- /Config/README.md: -------------------------------------------------------------------------------- 1 | # Update-Sysmon Config Directory 2 | Stage your production Sysmon deployment Domain Controller, Member Server, and Workstation configuration files in this directory. -------------------------------------------------------------------------------- /Config/sample-sysmon-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | md5,sha256,IMPHASH 6 | False 7 | SysmonFileDelete 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | c:\tools 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Thomas Connell 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Update-Sysmon Overview 2 | Update-Sysmon was created to aid in the deployment/maintenance of the Sysmon service on a large number of computers. System Monitor (Sysmon) is a Windows system service and device driver that, once installed on a system, remains resident across system reboots to monitor and log system activity to the Windows event log. One extremely useful feature of Sysmon is the ability to load custom configurations to filter out noise. These configurations will require constant tuning based on the events occurring in your environment. Update-Sysmon allows you to centrally manage the deployment of configuration files and new Sysmon versions. 3 | 4 | With Update-Sysmon, you can install, uninstall, and update Sysmon. It will detect if the Sysmon service exists and validate the installed version file hash against the version in your deployment directory before choosing to install or update the Sysmon binary and/or configuration. You must stage the Sysmon installation files in x86/x64 sub-folders of the script running directory. Each filename must match the name you choose for the service (default=Sysmon). 5 | 6 | ## Usage ## 7 | ### Install Method #1 ### 8 | Installs Sysmon using "Sysmon.exe" found in the script running directory x86/x64 sub-folders. If Sysmon is already installed, the configuration will be checked for updates. 9 | ~~~~ 10 | PS C:\> Update-Sysmon -Verbose 11 | ~~~~ 12 | 13 | ### Install Method #2 ### 14 | Installs Sysmon using files in the specified directory and uses a specific config file name. Only the configuration is updated if Sysmon is already installed. 15 | ~~~~ 16 | PS C:\> Update-Sysmon -RunDir "C:\Installs\Sysmon" -ConfigFile "Config\workstation-sysmonconfig.xml" -Verbose 17 | ~~~~ 18 | 19 | ### Install Method #3 ### 20 | Installs Sysmon using "StealthService.exe" found in the script running directory x86/x64 sub-folders. The service and running process will be named "StealthService". 21 | ~~~~ 22 | PS C:\> Update-Sysmon -SvcName "StealthService" -Verbose 23 | ~~~~ 24 | 25 | ### Uninstall ### 26 | Forcibly uninstalls Sysmon service named "Sysmon". You may also use the Graceful uninstall method which requires a reboot. 27 | ~~~~ 28 | PS C:\> Update-Sysmon -Uninstall -SvcName "Sysmon" -UninstallMethod "Force" -Verbose 29 | ~~~~ 30 | 31 | ## Domain Deployment ## 32 | 33 | The Update-Sysmon function can be deployed as a computer startup script or a scheduled system task to deploy the Sysmon service on all domain Windows endpoints without any user interaction. An hourly scheduled task is preferred because it will ensure the Sysmon service is always running. Host the function and Sysmon binaries in a share all domain computers can access (such as NETLOGON). Tip: See this [article](https://devblogs.microsoft.com/scripting/how-to-run-powershell-scripts-from-a-shared-directory/) on how to run PowerShell scripts from a shared directory. 34 | 35 | The Update-SysmonDomainLauncher.ps1 script can be used to set the Update-Sysmon parameters based on the domain computer account role and group membership. This allows you to deploy a single policy to all systems while applying Sysmon configurations tailored to the Operating System role (workstation, member server, or domain controller). You can also apply custom settings based on AD security group membership. This is useful for re-directing a group of test computers to a Sysmon deployment share containing a new version of the Sysmon utility along with configuration files for that version. 36 | 37 | ### Deployment Folder Structure ### 38 | 39 | At a minimum, the function expects x86/x64 sub-folders in the script running directory containing the appropriate Sysmon installation binary. Aside from that, you can place the configuration files anywhere you want. This can be as basic or over engineered as you wish. 40 | 41 | This folder structure works best when using the Update-SysmonDomainLauncher.ps1 script: 42 | 43 | . 44 | ├── x64 45 | │ ├── Sysmon.exe 46 | ├── x86 47 | │ ├── Sysmon.exe 48 | ├── Config 49 | │ ├── sysmonconfig-domaincontroller-production.xml 50 | │ ├── sysmonconfig-memberserver-production.xml 51 | │ ├── sysmonconfig-workstation-production.xml 52 | │ ├── sysmonconfig-verbose-production.xml 53 | └── Update-Sysmon.ps1 54 | └── Update-SysmonDomainLauncher.ps1 55 | └── ... 56 | 57 | A Testing-CurrentVersion domain group and deployment folder can be used for testing configuration changes against the current production binary version of Sysmon. A Testing-NewVersion domain group and deployment folder can be used for testing upgrades to a new binary version of Sysmon and configuration. It is useful to re-direct the transcript logging of test systems to a file share in order to easily review test results. Once testing has completed, the files in the root production folder are overwritten with the validated files in the test folder. 58 | 59 | ### Deployment Resources ### 60 | 61 | Your Sysmon deployment will not be successful without two very important resources: 62 | 63 | #### 1) The Sysmon x86/x64 installation files #### 64 | 65 | * https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon 66 | 67 | #### 2) Sysmon configurations tailored to each OS role in your environment #### 68 | 69 | * https://github.com/SwiftOnSecurity/sysmon-config 70 | * https://github.com/olafhartong/sysmon-modular 71 | -------------------------------------------------------------------------------- /Resources/Analyze-SysmonEventData.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script will generate a CSV file for each type of Sysmon event logged. This can be used to improve your configuration file content. 4 | #> 5 | 6 | . .\Get-SysmonEventData.ps1 7 | 8 | foreach ($ID in (1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,255)) 9 | { 10 | $events = @() 11 | $events = Get-SysMonEventData -EventId $ID -EndTime (Get-Date) -StartTime (Get-Date).AddDays(-3) -ErrorAction SilentlyContinue 12 | if ($events.count -ne "0"){$events | Export-Csv -NoTypeInformation -Path .\Sysmon_$($ID)_$($events[0].EventType)_$($events.count)_Events.csv} 13 | Write-Host "$($events.count) Sysmon Event ID $ID events found." 14 | } 15 | -------------------------------------------------------------------------------- /Resources/Get-SysmonEventData.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Searches for specified SysMon Events and retunrs the Event Data as a custom object. 4 | .DESCRIPTION 5 | Searches for specified SysMon Events and retunrs the Event Data as a custom object. 6 | .EXAMPLE 7 | Get-SysMonEventData -EventId 1 -MaxEvents 10 -EndTime (Get-Date) -StartTime (Get-Date).AddDays(-1) 8 | All process creation events in the last 24hr 9 | .EXAMPLE 10 | Get-SysMonEventData -EventId 3 -MaxEvents 20 -Path .\export.evtx 11 | last 20 network connection events from a exported SysMon log. 12 | #> 13 | function Get-SysmonEventData { 14 | [CmdletBinding(DefaultParameterSetName='ID', 15 | HelpUri = 'https://github.com/darkoperator/Posh-Sysmon/blob/master/docs/Get-SysmonEventData.md')] 16 | Param ( 17 | # Sysmon Event ID of records to show 18 | [Parameter(Mandatory=$true, 19 | ParameterSetName='ID', 20 | ValueFromPipelineByPropertyName=$true, 21 | Position=0)] 22 | [ValidateSet(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,255)] 23 | [Int32[]] 24 | $EventId, 25 | 26 | # EventType that a Rule can be written against. 27 | [Parameter(Mandatory=$false, 28 | ParameterSetName='Type', 29 | ValueFromPipelineByPropertyName=$true, 30 | Position=0)] 31 | [string[]] 32 | [ValidateSet('NetworkConnect', 'ProcessCreate', 'FileCreateTime', 33 | 'ProcessTerminate', 'ImageLoad', 'DriverLoad', 34 | 'CreateRemoteThread', 'RawAccessRead', 'ProcessAccess', 'Error', 35 | 'FileCreateStreamHash', 'RegistryValueSet', 'RegistryRename', 36 | 'RegistryAddOrDelete', 'FileCreate','ConfigChange','PipeCreated', 37 | 'PipeConnected', 'WmiEventFilterActivityDetected', 'WmiEventConsumerActivityDetected', 38 | 'WmiEventConsumerToFilterActivityDetected', 'DNSQuery', 'FileDelete')] 39 | $EventType, 40 | 41 | # Specifies the maximum number of events that Get-WinEvent returns. Enter an integer. The default is to return all the events in the logs or files. 42 | [Parameter(Mandatory=$false, 43 | ValueFromPipelineByPropertyName=$true, 44 | Position=1)] 45 | [int] 46 | $MaxEvents, 47 | 48 | # Specifies a path to one or more exported SysMon events in evtx format. 49 | [Parameter(Mandatory=$false, 50 | ValueFromPipeline=$true, 51 | ValueFromPipelineByPropertyName=$true, 52 | HelpMessage='Path to one or more locations.')] 53 | [Alias('PSPath')] 54 | [ValidateNotNullOrEmpty()] 55 | [string[]] 56 | $Path, 57 | 58 | # Start Date to get all event going forward. 59 | [Parameter(Mandatory=$false)] 60 | [datetime] 61 | $StartTime, 62 | 63 | # End data for searching events. 64 | [Parameter(Mandatory=$false)] 65 | [datetime] 66 | $EndTime 67 | ) 68 | 69 | Begin 70 | { 71 | $EventTypeMap = @{ 72 | ProcessCreate = 1 73 | FileCreateTime = 2 74 | NetworkConnect = 3 75 | ProcessTerminate = 5 76 | DriverLoad = 6 77 | ImageLoad = 7 78 | CreateRemoteThread = 8 79 | RawAccessRead = 9 80 | ProcessAccess = 10 81 | FileCreate = 11 82 | RegistryAddOrDelete = 12 83 | RegistryValueSet = 13 84 | RegistryRename = 14 85 | FileCreateStreamHash = 15 86 | ConfigChange = 16 87 | PipeCreated = 17 88 | PipeConnected = 18 89 | WmiEventFilterActivityDetected = 19 90 | WmiEventConsumerActivityDetected = 20 91 | WmiEventConsumerToFilterActivityDetected = 21 92 | DNSQuery = 22 93 | FileDelete = 23 94 | Error = 255 95 | } 96 | 97 | $EventIdtoType = @{ 98 | '1' = 'ProcessCreate' 99 | '2' = 'FileCreateTime' 100 | '3' = 'NetworkConnect' 101 | '5' = 'ProcessTerminate' 102 | '6' = 'DriverLoad' 103 | '7' = 'ImageLoad' 104 | '8' = 'CreateRemoteThread' 105 | '9' = 'RawAccessRead' 106 | '10' = 'ProcessAccess' 107 | '11' = 'FileCreate' 108 | '12' = 'RegistryAddOrDelete' 109 | '13' = 'RegistryValueSet' 110 | '14' = 'RegistryRename' 111 | '15' = 'FileCreateStreamHash' 112 | '16' = 'ConfigChange' 113 | '17' = 'PipeCreated' 114 | '18' = 'PipeConnected' 115 | '19' = 'WmiEventFilterActivityDetected' 116 | '20' = 'WmiEventConsumerActivityDetected' 117 | '21' = 'WmiEventConsumerToFilterActivityDetected' 118 | '22' = 'DNSQuery' 119 | '23' = 'FileDelete' 120 | '255' = 'Error' 121 | 122 | } 123 | } 124 | Process 125 | { 126 | # Hash for filtering 127 | $HashFilter = @{LogName='Microsoft-Windows-Sysmon/Operational'} 128 | 129 | # Hash for command paramteters 130 | $ParamHash = @{} 131 | 132 | if ($MaxEvents -gt 0) 133 | { 134 | $ParamHash.Add('MaxEvents', $MaxEvents) 135 | } 136 | 137 | if ($Path -gt 0) 138 | { 139 | $ParamHash.Add('Path', $Path) 140 | } 141 | 142 | switch ($PSCmdlet.ParameterSetName) { 143 | 'ID' { $HashFilter.Add('Id', $EventId) } 144 | 'Type' { 145 | $EventIds = @() 146 | foreach ($etype in $EventType) 147 | { 148 | $EventIds += $EventTypeMap[$etype] 149 | } 150 | $HashFilter.Add('Id', $EventIds) 151 | } 152 | } 153 | 154 | if ($StartTime) 155 | { 156 | $HashFilter.Add('StartTime', $StartTime) 157 | } 158 | 159 | if ($EndTime) 160 | { 161 | $HashFilter.Add('EndTime', $EndTime) 162 | } 163 | 164 | $ParamHash.Add('FilterHashTable',$HashFilter) 165 | Get-WinEvent @ParamHash | ForEach-Object { 166 | [xml]$evtxml = $_.toxml() 167 | $ProcInfo = [ordered]@{} 168 | $ProcInfo['EventId'] = $evtxml.Event.System.EventID 169 | $ProcInfo['EventType'] = $EventIdtoType[$evtxml.Event.System.EventID] 170 | $ProcInfo['Computer'] = $evtxml.Event.System.Computer 171 | $evtxml.Event.EventData.Data | ForEach-Object { 172 | $ProcInfo[$_.name] = $_.'#text' 173 | } 174 | New-Object psobject -Property $ProcInfo 175 | } 176 | } 177 | End {} 178 | } 179 | -------------------------------------------------------------------------------- /Resources/Update-SysmonDeployment.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script will backup the production Sysmon configuration before replacing it with a tested version. 4 | Author: Thomas Connell 5 | #> 6 | 7 | $ProductionDir = "\\PATH\TO\PRODUCTION\Sysmon" 8 | $CurrentVersionTestDir = "Testing-CurrentVersion" 9 | $NewVersionTestDir = "Testing-NewVersion" 10 | 11 | function Update-FromCurrentVersion($Type) 12 | { 13 | #Created configuration backup directory 14 | New-Item "$ProductionDir\Config\Backups" -ItemType Directory -Force | Out-Null 15 | 16 | if (($Type -eq "workstation") -or ($Type -eq "memberserver") -or ($Type -eq "domaincontroller")) 17 | { 18 | Write-Host "Processing $Type configuration" 19 | 20 | $ProductionFile = "$ProductionDir\Config\sysmonconfig-$Type-production.xml" 21 | $CurrentVersionFile = "$ProductionDir\$CurrentVersionTestDir\Config\sysmonconfig-$Type-testing.xml" 22 | 23 | if (((Get-ChildItem -Path $ProductionFile -ErrorAction SilentlyContinue).LastWriteTime) -ne ((Get-ChildItem -Path $CurrentVersionFile -ErrorAction SilentlyContinue).LastWriteTime)) 24 | { 25 | if (Test-Path $ProductionFile) 26 | { #Backup production configuration 27 | $ProdDate = ((Get-ChildItem -Path $ProductionFile).LastWriteTime).ToString("yyy-MM-dd") 28 | Copy-Item -Path $ProductionFile -Destination "$ProductionDir\Config\Backups\$ProdDate-sysmonconfig-$Type-production.xml" 29 | Write-Host -ForegroundColor Green "Production $Type configuration backed up to $ProductionDir\Config\Backups\$ProdDate-sysmonconfig-$Type-production.xml" 30 | 31 | if ((Test-Path $CurrentVersionFile) -and (Test-Path "$ProductionDir\Config\Backups\$ProdDate-sysmonconfig-$Type-production.xml")) 32 | { #Write test configuration to production 33 | Copy-Item -Path $CurrentVersionFile -Destination $ProductionFile 34 | Write-Host -ForegroundColor Green "Testing $Type configuration dated $((Get-ChildItem -Path $CurrentVersionFile).LastWriteTime) written to production" 35 | } 36 | Else 37 | { 38 | Write-Host -ForegroundColor Red "Writing $Type test configuration to production failed! Verify $CurrentVersionFile exists." 39 | } 40 | } 41 | Else 42 | { 43 | Write-Host -ForegroundColor Red "Backup of $Type production configuration failed! Verify $ProductionFile exists." 44 | } 45 | } 46 | Else 47 | { 48 | Write-Host -ForegroundColor Red "There is nothing to do. The $Type test configuration has not changed." 49 | } 50 | } 51 | Else 52 | { 53 | Write-Host -ForegroundColor Red "`"$Type`" does not match an approved string" 54 | } 55 | } 56 | 57 | function Update-FromNewVersion() 58 | { 59 | #Created backup directories 60 | New-Item "$ProductionDir\Config\Backups" -ItemType Directory -Force | Out-Null 61 | New-Item "$ProductionDir\x64\Backups" -ItemType Directory -Force | Out-Null 62 | New-Item "$ProductionDir\x86\Backups" -ItemType Directory -Force | Out-Null 63 | 64 | if (((Get-ChildItem -Path "$ProductionDir\x64\Sysmon.exe" -ErrorAction SilentlyContinue).LastWriteTime) -ne ((Get-ChildItem -Path "$ProductionDir\$NewVersionTestDir\x64\Sysmon.exe" -ErrorAction SilentlyContinue).LastWriteTime)) 65 | { #Backup production binaries 66 | $Prod64Date = ((Get-ChildItem -Path "$ProductionDir\x64\Sysmon.exe").LastWriteTime).ToString("yyy-MM-dd") 67 | $Prod86Date = ((Get-ChildItem -Path "$ProductionDir\x86\Sysmon.exe").LastWriteTime).ToString("yyy-MM-dd") 68 | Copy-Item -Path "$ProductionDir\x64\Sysmon.exe" -Destination "$ProductionDir\x64\Backups\$Prod64Date-Sysmon.exe" 69 | Copy-Item -Path "$ProductionDir\x86\Sysmon.exe" -Destination "$ProductionDir\x86\Backups\$Prod86Date-Sysmon.exe" 70 | Write-Host -ForegroundColor Green "Production Sysmon binaries backed up to the $ProductionDir\x64 -and- x86\Backups folder" 71 | 72 | #Backup production configuration 73 | Get-ChildItem "$ProductionDir\Config" | Where-Object {$_.PSIsContainer -eq $false} | ForEach { 74 | $ProdDate = ((Get-ChildItem -Path $_.FullName).LastWriteTime).ToString("yyy-MM-dd") 75 | Copy-Item -Path $_.FullName -Destination "$ProductionDir\Config\Backups\$ProdDate-$($_.Name)" -Force 76 | Write-Host -ForegroundColor Green "Production configuration backed up to $ProductionDir\Config\Backups\$ProdDate-$($_.Name)" 77 | } 78 | 79 | if ((Test-Path "$ProductionDir\$NewVersionTestDir\x64\Sysmon.exe") -and (Test-Path "$ProductionDir\x64\Backups\$Prod64Date-Sysmon.exe")) 80 | { #Replace production binaries 81 | Copy-Item -Path "$ProductionDir\$NewVersionTestDir\x64\Sysmon.exe" -Destination "$ProductionDir\x64\Sysmon.exe" 82 | Copy-Item -Path "$ProductionDir\$NewVersionTestDir\x86\Sysmon.exe" -Destination "$ProductionDir\x86\Sysmon.exe" 83 | Write-Host -ForegroundColor Green "Tested Sysmon binaries in the $NewVersionTestDir folder dated $((Get-ChildItem -Path "$ProductionDir\$NewVersionTestDir\x64\Sysmon.exe").LastWriteTime) were written to production" 84 | 85 | #Replace production configuration 86 | Get-ChildItem "$ProductionDir\$NewVersionTestDir\Config" | Where-Object {$_.PSIsContainer -eq $false} | ForEach { 87 | Copy-Item -Path $_.FullName -Destination "$ProductionDir\Config\$($_.Name -replace "testing","production")" 88 | Write-Host -ForegroundColor Green "$ProductionDir\Config\$($_.Name -replace "testing","production"): Production Sysmon configuration replaced with the tested version from the $NewVersionTestDir folder" 89 | } 90 | 91 | #Replace CurrentVersion files 92 | Copy-Item -Path "$ProductionDir\$NewVersionTestDir\x64\Sysmon.exe" -Destination "$ProductionDir\$CurrentVersionTestDir\x64\Sysmon.exe" 93 | Copy-Item -Path "$ProductionDir\$NewVersionTestDir\x86\Sysmon.exe" -Destination "$ProductionDir\$CurrentVersionTestDir\x86\Sysmon.exe" 94 | Write-Host -ForegroundColor Green "Tested Sysmon binaries in the $NewVersionTestDir folder dated $((Get-ChildItem -Path "$ProductionDir\$NewVersionTestDir\x64\Sysmon.exe").LastWriteTime) were written to $CurrentVersionTestDir" 95 | Get-ChildItem "$ProductionDir\$NewVersionTestDir\Config" | Where-Object {$_.PSIsContainer -eq $false} | ForEach { 96 | Copy-Item -Path $_.FullName -Destination "$ProductionDir\$CurrentVersionTestDir\Config\$($_.Name)" 97 | Write-Host -ForegroundColor Green "$ProductionDir\$CurrentVersionTestDir\Config\$($_.Name): $CurrentVersionTestDir Sysmon configuration replaced with the tested version from the $NewVersionTestDir folder" 98 | } 99 | } 100 | Else 101 | { 102 | Write-Host -ForegroundColor Red "Writing tested Sysmon binary to production failed! Verify $ProductionDir\$NewVersionTestDir\x64\Sysmon.exe exists." 103 | } 104 | } 105 | Else 106 | { 107 | Write-Host -ForegroundColor Red "There is nothing to do. The NewVersion Sysmon binary has not changed." 108 | } 109 | } 110 | 111 | function Show-Menu 112 | { 113 | param ( 114 | [string]$Title = 'My Menu' 115 | ) 116 | Clear-Host 117 | Write-Host "================ $Title ================" 118 | 119 | Write-Host "1: Press '1' to update Workstation Sysmon config from the CurrentVersion folder" 120 | Write-Host "1: Press '2' to update Member Server Sysmon config from the CurrentVersion folder" 121 | Write-Host "1: Press '3' to update Domain Controller Sysmon config from the CurrentVersion folder" 122 | Write-Host "2: Press '4' to update all Sysmon files from the NewVersion folder" 123 | Write-Host "Q: Press 'Q' to quit." 124 | } 125 | 126 | Show-Menu –Title 'Sysmon Update Menu' 127 | $selection = Read-Host "Please make a selection" 128 | switch ($selection) 129 | { 130 | '1' { 131 | 'You chose to update the production Workstation Sysmon configuration from the CurrentVersion folder' 132 | Write-Host -nonewline "Continue? (Y/N) " 133 | $response = Read-Host 134 | if ( $response -ne "Y" ) { return } 135 | Update-FromCurrentVersion "workstation" 136 | } '2' { 137 | 'You chose to update the production Member Server Sysmon configuration from the CurrentVersion folder' 138 | Write-Host -nonewline "Continue? (Y/N) " 139 | $response = Read-Host 140 | if ( $response -ne "Y" ) { return } 141 | Update-FromCurrentVersion "memberserver" 142 | } '3' { 143 | 'You chose to update the production Domain Controller Sysmon configuration from the CurrentVersion folder' 144 | Write-Host -nonewline "Continue? (Y/N) " 145 | $response = Read-Host 146 | if ( $response -ne "Y" ) { return } 147 | Update-FromCurrentVersion "domaincontroller" 148 | } '4' { 149 | 'You chose to update all production Sysmon binaries and configurations from the NewVersion folder' 150 | Write-Host -nonewline "Continue? (Y/N) " 151 | $response = Read-Host 152 | if ( $response -ne "Y" ) { return } 153 | Update-FromNewVersion 154 | } 'q' { 155 | return 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Testing-CurrentVersion/README.md: -------------------------------------------------------------------------------- 1 | # Update-Sysmon Testing-CurrentVersion Directory 2 | This optional folder is meant to contain a copy of your production deployment files (Update-Sysmon.ps1, Sysmon binaries in x86/x64 sub-folders, and configurations). It allows you to test new versions of the Update-Sysmon function and/or new configuration files on a group of domain computers before applying to production. This is configurable within the Update-SysmonDomainLauncher.ps1 script. 3 | 4 | Updating the files in this directory will allow you to observe the behavior on a subset of test computers. 5 | 6 | Once new configurations have been tested/approved, use Update-SysmonDeployment.ps1 to backup and synchronize the changes with the production deployment folder. -------------------------------------------------------------------------------- /Testing-NewVersion/README.md: -------------------------------------------------------------------------------- 1 | # Update-Sysmon Testing-NewVersion Directory 2 | This optional folder is meant to contain a copy of your production deployment files (Update-Sysmon.ps1, Sysmon binaries in x86/x64 sub-folders, and configurations). It allows you to test new versions of the Update-Sysmon function, new Sysmon binaries, and new configuration files on a group of domain computers before applying to production. This is configurable within the Update-SysmonDomainLauncher.ps1 script. 3 | 4 | Updating the files in this directory will allow you to observe the behavior on a subset of test computers. 5 | 6 | Once testing has been completed, use Update-SysmonDeployment.ps1 to backup and synchronize the changes with the production deployment and Testing-CurrentVersion folders. -------------------------------------------------------------------------------- /Update-Sysmon-Pseudocode.txt: -------------------------------------------------------------------------------- 1 | If all required files are present (Sysmon binaries/config file) 2 | { 3 | If a graceful uninstall is currently pending 4 | { 5 | Do nothing; Exit script 6 | } 7 | If the target Sysmon service name does not match the currently installed service name 8 | { 9 | Update the service name variable to use the installed service name 10 | *The service is detected based on the Sysmon signature service description 11 | *This will result in a hash mis-match causing Sysmon to be uninstalled 12 | *Sysmon will re-install under the new service name after next script execution 13 | } 14 | If Sysmon service exists 15 | { 16 | (Validate-Sysmon function) 17 | If (Sysmon service registry keys exist) 18 | { 19 | If hash of local Sysmon.exe matches hash of RunDir Sysmon.exe 20 | { 21 | Verify Sysmon/SysmonDrv services are started 22 | (Apply-SysmonConfig function) 23 | If (Sysmon service registry key exists and Sysmon.exe process is running) 24 | { 25 | If (Configuration file hash *does not* match RunDir hash) 26 | { 27 | Apply configuration and update hash to local registry 28 | } 29 | Else (Configuration file hash matches RunDir hash) 30 | { 31 | Do nothing 32 | } 33 | If (Configuration file hash *is missing* from local registry) 34 | { 35 | Apply configuration and write hash to local registry 36 | } 37 | } 38 | Else (Sysmon registry key or process *are not* present) 39 | { 40 | Do nothing 41 | } 42 | } 43 | Else (hash of local Sysmon.exe *does not* match hash of RunDir Sysmon.exe) 44 | { 45 | (Uninstall-Sysmon function "Graceful" method) 46 | If either Sysmon or SysmonDrv service registry keys exist, delete them. 47 | Schedule Sysmon files to delete at next reboot 48 | *Sysmon will continue to run in memory and log events until the next reboot 49 | *After reboot, Sysmon will only begin logging events after next script execution 50 | 51 | (Uninstall-Sysmon function "Force" method) 52 | If either Sysmon or SysmonDrv service registry keys exist, foribly remove Sysmon with the -u swtich 53 | *This method has caused BSoD system crashes on about 3% of systems tested; Only recommended when testing 54 | *Sysmon will only begin logging events after next script execution 55 | } 56 | } 57 | Else (Sysmon service registry keys *do not* exist) 58 | { 59 | Do nothing 60 | } 61 | } 62 | Else (Sysmon/SysmonDrv service *does not* exist) 63 | { 64 | (Install-Sysmon function) 65 | If (Sysmon service registry keys *do not* exist and Sysmon process *is not* running) 66 | { 67 | Install Sysmon 68 | Write configuration file hash to local registry 69 | Force event forwarding client to re-evaluate event subscriptions 70 | } 71 | Else (Sysmon service registry keys exist *or* Sysmon process is running) 72 | { 73 | Do nothing 74 | } 75 | } 76 | } 77 | Else (All required files *are not* present) 78 | { 79 | Do nothing 80 | } -------------------------------------------------------------------------------- /Update-Sysmon.ps1: -------------------------------------------------------------------------------- 1 | function Update-Sysmon 2 | { 3 | <# 4 | .SYNOPSIS 5 | This function can install, uninstall, and update Sysmon. It will detect 6 | if the Sysmon service exists and validate the file hash against the version 7 | from the specified directory before choosing to install or update the Sysmon 8 | configuration. If the hashes do not match, it will uninstall the current 9 | version and install the version from the $RunDir. You must stage the Sysmon 10 | installation files in x86/x64 sub-folders. Each filename must match the name 11 | you choose for the service (default=Sysmon). 12 | 13 | Author: Thomas Connell 14 | 15 | .DESCRIPTION 16 | This function was created to aide in the deployment/maintenance of 17 | the Sysmon service to a large number of computers. It is designed to 18 | be run as a computer startup script or a scheduled system task without any 19 | user interaction. Standalone systems must have a configuration specified, 20 | while domain joined systems can auto-select a configuration. 21 | 22 | System Monitor (Sysmon) is a Windows system service and device driver 23 | that, once installed on a system, remains resident across system reboots to 24 | monitor and log system activity to the Windows event log. It provides 25 | detailed information about process creations, network connections, and 26 | changes to file creation time. 27 | 28 | .PARAMETER RunDir 29 | The directory where we will find the Sysmon binaries. Default: Script directory 30 | .PARAMETER ConfigFile 31 | The Sysmon XML configuration file you would like to import. Can be selected automatically. 32 | .PARAMETER LogDir 33 | The directory for logging Sysmon output. Default: %WinDir%\Temp 34 | .PARAMETER SvcName 35 | Custom Sysmon service name and name of the Sysmon binaries found in x86/x64 sub-folders of the RunDir. Default: Sysmon 36 | .PARAMETER Uninstall 37 | Switch for removal of Sysmon. 38 | .PARAMETER UninstallMethod 39 | Choose from Graceful or Force. Default: Forceably remove Sysmon without requiring a reboot. 40 | 41 | .INPUTS 42 | None. You cannot pipe objects to Update-Sysmon. 43 | .OUTPUTS 44 | None. Update-Sysmon does not generate any output. 45 | 46 | .LINK 47 | Source project: 48 | https://github.com/jokezone/Update-Sysmon 49 | .LINK 50 | Sysmon download: 51 | https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon 52 | .LINK 53 | Community supported Sysmon documentation: 54 | https://github.com/trustedsec/SysmonCommunityGuide 55 | .LINK 56 | Community supported Sysmon configuration: 57 | https://github.com/SwiftOnSecurity/sysmon-config 58 | 59 | .EXAMPLE 60 | PS C:\> Update-Sysmon -Verbose 61 | - Installs Sysmon using "Sysmon.exe" found in the script running directory x86/x64 sub-folders 62 | - If Sysmon is already installed, the configuration will be checked for updates 63 | .EXAMPLE 64 | PS C:\> Update-Sysmon -SvcName "StealthService" -Verbose 65 | - Installs Sysmon using "StealthService.exe" found in the script running directory x86/x64 sub-folders 66 | - The service and running process will be named "StealthService" 67 | .EXAMPLE 68 | PS C:\> Update-Sysmon -Uninstall -SvcName "Sysmon" -UninstallMethod "Force" -Verbose 69 | - Forcibly uninstalls Sysmon service named "Sysmon". You may also use the Graceful uninstall method which requires a reboot 70 | .EXAMPLE 71 | PS C:\> Update-Sysmon -RunDir "C:\Installs\Sysmon" -ConfigFile "Config\workstation-sysmonconfig.xml" -Verbose 72 | - Installs Sysmon using files in the specified directory and uses a specific config file name 73 | - Only the configuration is updated if Sysmon is already installed 74 | .EXAMPLE 75 | PS C:\> PowerShell.exe -Command {. "\\Path\To\Sysmon\Update-Sysmon.ps1";Update-Sysmon} 76 | - Method to load and execute function from a file share without any user interaction 77 | #> 78 | param 79 | ( 80 | [Parameter(Position = 0)] 81 | [string] 82 | $RunDir = $PSScriptRoot, 83 | [Parameter(Position = 1)] 84 | [string] 85 | $ConfigFile = "", 86 | [string] 87 | $LogDir = $env:TEMP, 88 | [string] 89 | $SvcName = "Sysmon", 90 | [switch] 91 | $Uninstall, 92 | [ValidateSet("Graceful","Force")] 93 | [string] 94 | $UninstallMethod 95 | ) 96 | 97 | $LogFile = $LogDir + "\$ENV:COMPUTERNAME-Update-Sysmon-Log.txt" 98 | if (Test-Path -Path $LogFile) 99 | { #Delete log file if it grows too large 100 | Get-ChildItem $LogFile | Where-Object Length -gt 2048000 | Remove-Item -Confirm:$false 101 | } 102 | Start-Transcript $LogFile -Append 103 | 104 | function Uninstall-Sysmon([string]$UninstallMethod,[string]$SvcName) 105 | { 106 | # Use the Force uninstall method to uninstall Sysmon without requiring a reboot 107 | # Use the Graceful uninstall method to avoid system crashes during uninstall 108 | if (-not($UninstallMethod)){$UninstallMethod = "Force"} 109 | Write-Verbose "$(Get-Date): Uninstalling Sysmon from $ENV:COMPUTERNAME..." 110 | $SysmonExePath = "$env:windir\$SvcName.exe" 111 | $SysmonSvcRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$SvcName" 112 | $SysmonDrvRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SysmonDrv" 113 | 114 | if ((Test-Path -Path $SysmonSvcRegPath) -or (Test-Path -Path $SysmonDrvRegPath)) 115 | { 116 | if ($UninstallMethod -eq "Force") 117 | { 118 | & $SysmonExePath -u #v6.02 Sysmon causes memory_corruption BUGCHECK_STR 0x1a_2102 on some systems 119 | } 120 | if ($UninstallMethod -eq "Graceful") 121 | { 122 | Write-Verbose "$(Get-Date): Removing Sysmon service registry keys - Sysmon will continue to run in memory" 123 | Remove-Item -Path $SysmonSvcRegPath -Recurse -Force -ErrorAction SilentlyContinue 124 | Remove-Item -Path $SysmonDrvRegPath -Recurse -Force -ErrorAction SilentlyContinue 125 | 126 | if ((Test-Path $SysmonExePath) -or (Test-Path "$env:windir\SysmonDrv.sys")) 127 | { #Schedule Sysmon files to delete at next reboot 128 | try 129 | { #Append to existing PendingFileRenameOperations registry value to delete Sysmon files at next reboot 130 | Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" | Select-Object -ExpandProperty "PendingFileRenameOperations" -ErrorAction Stop | Out-Null 131 | Write-Verbose "$(Get-Date): Updating existing PendingFileRenameOperations registry value to delete Sysmon files at next reboot." 132 | $values = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations").PendingFileRenameOperations 133 | $values += "\??\$SysmonExePath","","\??\$env:windir\SysmonDrv.sys","" 134 | Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" "PendingFileRenameOperations" $values 135 | } 136 | catch 137 | { #Create PendingFileRenameOperations registry value to delete Sysmon files at next reboot 138 | Write-Verbose "$(Get-Date): Creating PendingFileRenameOperations registry value to delete Sysmon files at next reboot." 139 | New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations" ` 140 | -Value "\??\$SysmonExePath","","\??\$env:windir\SysmonDrv.sys","" ` 141 | -PropertyType MultiString -Force | Out-Null 142 | } 143 | } 144 | else 145 | { 146 | Write-Verbose "$(Get-Date): Unable to schedule Sysmon files to delete at next reboot becuase they do not exist." 147 | } 148 | } 149 | } 150 | else 151 | { 152 | Write-Verbose "$(Get-Date): Unable to uninstall because Sysmon/SysmonDrv service registry keys are missing. Try running Sysmon.exe -u or reboot and try again." 153 | } 154 | } 155 | 156 | function Install-Sysmon([string]$RunDir,[string]$ConfigFile,[string]$SvcName) 157 | { 158 | $SysmonSvcRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$SvcName" 159 | $SysmonDrvRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SysmonDrv" 160 | if (-not((Test-Path -Path $SysmonSvcRegPath) -and (Test-Path -Path $SysmonDrvRegPath) -and (Get-Process -Name $SvcName))) 161 | { #Verify service registry keys and process are not present before attempting an install 162 | if ([Environment]::Is64BitOperatingSystem) 163 | { 164 | Write-Verbose "$(Get-Date): Installing 64-bit Sysmon..." 165 | Invoke-Expression "$RunDir\x64\$SvcName.exe -accepteula -i $RunDir\$ConfigFile" 166 | } 167 | else 168 | { 169 | Write-Verbose "$(Get-Date): Installing 32-bit Sysmon..." 170 | Invoke-Expression "$RunDir\x86\$SvcName.exe -accepteula -i $RunDir\$ConfigFile" 171 | } 172 | if (Test-Path -Path $SysmonDrvRegPath) 173 | { 174 | Write-Verbose "$(Get-Date): Sysmon installed - Configuration file is being hashed for the first time." 175 | New-ItemProperty -Path $SysmonDrvRegPath -Name "ConfigFileHash" ` 176 | -Value (Get-SHA256FileHash "$RunDir\$ConfigFile") ` 177 | -PropertyType STRING -Force | Out-Null 178 | } 179 | else 180 | { 181 | Write-Verbose "$(Get-Date): Sysmon install failed." 182 | } 183 | } 184 | else 185 | { 186 | Write-Verbose "$(Get-Date): Unable to install because Sysmon services or process are present. Please reboot and try again." 187 | } 188 | } 189 | 190 | function Validate-Sysmon([string]$RunDir,[string]$SvcName) 191 | { 192 | $SysmonExePath = "$env:windir\$SvcName.exe" 193 | $SysmonSvcRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$SvcName" 194 | $SysmonDrvRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SysmonDrv" 195 | if ((Test-Path -Path $SysmonSvcRegPath) -and (Test-Path -Path $SysmonDrvRegPath)) 196 | { 197 | if ([Environment]::Is64BitOperatingSystem) 198 | { #64-bit validation 199 | if ((Get-SHA256FileHash $SysmonExePath) -eq (Get-SHA256FileHash "$RunDir\x64\$SvcName.exe")) 200 | { 201 | return $true 202 | } 203 | else 204 | { 205 | Write-Verbose "$(Get-Date): Validation failed because local Sysmon hash does not match source file hash." 206 | } 207 | } 208 | else 209 | { #32-bit validation 210 | if ((Get-SHA256FileHash $SysmonExePath) -eq (Get-SHA256FileHash "$RunDir\x86\$SvcName.exe")) 211 | { 212 | return $true 213 | } 214 | else 215 | { 216 | Write-Verbose "$(Get-Date): Validation failed because local Sysmon hash does not match source file hash." 217 | } 218 | } 219 | } 220 | else 221 | { 222 | Write-Verbose "$(Get-Date): Validation failed because Sysmon services are not registered." 223 | } 224 | } 225 | 226 | function Apply-SysmonConfig([string]$RunDir,[string]$ConfigFile,[string]$SvcName) 227 | { 228 | $SysmonExePath = "$env:windir\$SvcName.exe" 229 | $SysmonSvcRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$SvcName" 230 | $SysmonDrvRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SysmonDrv" 231 | if ((Test-Path -Path $SysmonDrvRegPath) -and (Get-Process -Name $SvcName -ErrorAction SilentlyContinue)) 232 | { 233 | try 234 | { 235 | Get-ItemProperty -Path $SysmonDrvRegPath | Select-Object -ExpandProperty "ConfigFileHash" -ErrorAction Stop | Out-Null 236 | if ((Get-SHA256FileHash "$RunDir\$ConfigFile") -ne (Get-ItemProperty -Path $SysmonDrvRegPath | Select-Object -ExpandProperty "ConfigFileHash")) 237 | { 238 | Write-Verbose "$(Get-Date): Configuration file hash has changed, applying Sysmon configuration: $RunDir\$ConfigFile" 239 | $output = Invoke-Expression "$SysmonExePath -accepteula -c `"$RunDir\$ConfigFile`"" 240 | $output 241 | if ($output -match "Configuration updated") 242 | { 243 | Write-Verbose "$(Get-Date): Updating configuration file hash in local registry" 244 | New-ItemProperty -Path $SysmonDrvRegPath -Name "ConfigFileHash" ` 245 | -Value (Get-SHA256FileHash "$RunDir\$ConfigFile") ` 246 | -PropertyType STRING -Force | Out-Null 247 | } 248 | else 249 | { 250 | Write-Verbose "$(Get-Date): Sysmon configuration update failed" 251 | } 252 | } 253 | } 254 | catch 255 | { 256 | Write-Verbose "$(Get-Date): Configuration file hash not found, applying Sysmon configuration: $RunDir\$ConfigFile" 257 | $output = Invoke-Expression "$SysmonExePath -accepteula -c `"$RunDir\$ConfigFile`"" 258 | $output 259 | if ($output -match "Configuration updated") 260 | { 261 | Write-Verbose "$(Get-Date): Writing configuration file hash to local registry." 262 | New-ItemProperty -Path $SysmonDrvRegPath -Name "ConfigFileHash" ` 263 | -Value (Get-SHA256FileHash "$RunDir\$ConfigFile") ` 264 | -PropertyType STRING -Force | Out-Null 265 | } 266 | else 267 | { 268 | Write-Verbose "$(Get-Date): Sysmon configuration update failed" 269 | } 270 | } 271 | } 272 | else 273 | { 274 | Write-Verbose "$(Get-Date): Unable to apply configuration because Sysmon registry key or process are not present" 275 | } 276 | } 277 | 278 | function Get-SHA256FileHash([string]$File) 279 | { #Used instead of Get-FileHash due to failing FIPS cryptographic algorithm validation 280 | try 281 | { 282 | [System.BitConverter]::ToString((New-Object -TypeName System.Security.Cryptography.SHA256Cng).ComputeHash([System.IO.File]::ReadAllBytes($File))).Replace("-", "") 283 | } 284 | catch 285 | { 286 | Write-Verbose "$(Get-Date): Hashing of file $File failed" 287 | return 288 | } 289 | } 290 | 291 | if ($Uninstall) 292 | { 293 | Uninstall-Sysmon -SvcName $SvcName -UninstallMethod $UninstallMethod 294 | break 295 | } 296 | 297 | function Select-Config([string]$ConfigFile) 298 | { 299 | if (-not($ConfigFile)) 300 | { 301 | <# Select configuration file based on OS type: 302 | 0 = Standalone Workstation 303 | 1 = Member Workstation 304 | 2 = Standalone Server 305 | 3 = Member Server 306 | 4 = Backup Domain Controller 307 | 5 = Primary Domain Controller 308 | #> 309 | $Role = (Get-WmiObject Win32_ComputerSystem).DomainRole 310 | if ($Role -eq 1) {$OSType = "workstation"} 311 | if ($Role -eq 3) {$OSType = "memberserver"} 312 | if ($Role -ge 4) {$OSType = "domaincontroller"} 313 | $ConfigFile = "Config\sysmonconfig-$OSType-production.xml" 314 | } 315 | return $ConfigFile 316 | } 317 | 318 | $ConfigFile = Select-Config $ConfigFile 319 | Write-Verbose "$(Get-Date): Script RunDir: $RunDir" 320 | Write-Verbose "$(Get-Date): Configuration file: $ConfigFile" 321 | Write-Verbose "$(Get-Date): Target service name: $SvcName" 322 | 323 | if ((Test-Path "$RunDir\x64\$SvcName.exe") -and (Test-Path "$RunDir\x86\$SvcName.exe") -and (Test-Path "$RunDir\$ConfigFile") -and ($ConfigFile)) 324 | { #All required files are present 325 | if (-not((Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations" -ErrorAction SilentlyContinue).PendingFileRenameOperations -match "SysmonDrv.sys")) 326 | { #Verify there are no pending uninstalls 327 | $SysmonSvcRegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$SvcName" 328 | if ((Test-Path -Path "$env:windir\SysmonDrv.sys") -and (-not(Test-Path -Path $SysmonSvcRegPath))) 329 | { #Verify the service name provided matches the installed service name 330 | $SvcNameDetected = (Get-WmiObject -Class Win32_Service | Where-Object {$_.Description -eq 'System Monitor service'}).Name 331 | if ($SvcNameDetected) 332 | { 333 | if ((Get-Item "$env:windir\$SvcNameDetected.exe").VersionInfo.InternalName -eq "System Monitor") 334 | { 335 | $SvcName = $SvcNameDetected 336 | Write-Verbose "$(Get-Date): Detected service name: $SvcNameDetected" 337 | } 338 | else 339 | { 340 | Write-Error "$(Get-Date): An existing System Monitor service was detected named $SvcNameDetected, but it did not contain the expected version information" 341 | } 342 | } 343 | } 344 | } 345 | else 346 | { 347 | Write-Verbose "$(Get-Date): A graceful uninstall is currently pending. Reboot and try again." 348 | break 349 | } 350 | if ((Get-Service -Name $SvcName -ErrorAction SilentlyContinue).Name -eq $SvcName) 351 | { #Sysmon service exists 352 | if (Validate-Sysmon -RunDir $RunDir -SvcName $SvcName) 353 | { #Local Sysmon file hash matches source file hash 354 | #Start Sysmon services if they are stopped 355 | Get-Service -Name $SvcName,SysmonDrv | Where-Object Status -eq "Stopped" | Start-Service 356 | Apply-SysmonConfig -RunDir $RunDir -ConfigFile $ConfigFile -SvcName $SvcName 357 | } 358 | else 359 | { #Local Sysmon file hash does not match source file hash 360 | Uninstall-Sysmon -SvcName $SvcName -UninstallMethod $UninstallMethod 361 | } 362 | } 363 | else 364 | { #Sysmon service is missing, install it! 365 | Install-Sysmon -RunDir $RunDir -ConfigFile $ConfigFile -SvcName $SvcName 366 | 367 | #Use GPUpdate to force event forwarding client to re-evaluate event subscriptions 368 | Start-Sleep -Seconds 10 369 | $output = Invoke-Expression "gpupdate /force /target:computer" 370 | } 371 | } 372 | else 373 | { 374 | Write-Error "Required Sysmon installation files are missing from $RunDir" 375 | } 376 | Stop-Transcript 377 | } 378 | -------------------------------------------------------------------------------- /Update-SysmonDomainLauncher.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script will set the Update-Sysmon parameters based on the domain computer account role and group membership. 4 | This allows you to deploy a single policy to all systems while applying Sysmon configurations tailored to the 5 | Operating System type (workstation, member server, or domain controller). You can also apply custom settings 6 | based on AD security group membership. This is useful for re-directing a group of test computers to a Sysmon 7 | deployment share containing a new version of the Sysmon utility along with configuration files for that version. 8 | Author: Thomas Connell 9 | Source project: https://github.com/jokezone/Update-Sysmon 10 | #> 11 | 12 | $FileShare = "\\FILESERVER\ShareName" 13 | $RunDir = $PSScriptRoot 14 | $LogDir = $env:TEMP 15 | $ConfigFile = $null 16 | $DelayExecution = 10 17 | 18 | if ($DelayExecution -gt 0) 19 | { 20 | $delay = Get-Random -Minimum 1 -Maximum $DelayExecution 21 | Write-Host "$(Get-Date): Delaying execution for $delay minutes" 22 | Start-Sleep -Seconds ($delay * 60) 23 | } 24 | 25 | try 26 | { 27 | # Query AD for group membership of computer 28 | $domainDN = ([ADSI]"").distinguishedName 29 | $root = [ADSI]"LDAP://$domainDN" 30 | $search = [adsisearcher]$root 31 | $Search.Filter = "(&(SamAccountName=$ENV:COMPUTERNAME$))" 32 | $computer = $Search.FindOne() 33 | $computerproperties = $computer.Properties 34 | [array]$groups = $computerproperties.memberof 35 | 36 | # Identify the computer role 37 | $Role = (Get-WmiObject Win32_ComputerSystem).DomainRole 38 | if ($Role -eq 1) {$OSType = "workstation"} 39 | if ($Role -eq 3) {$OSType = "memberserver"} 40 | if ($Role -ge 4) {$OSType = "domaincontroller"} 41 | 42 | # Set script parameters 43 | $groupToMatch = "Sysmon-Testing-CurrentVersion" 44 | if($groups -match $groupToMatch) 45 | { 46 | $ConfigFile = "Config\sysmonconfig-$OSType-testing.xml" 47 | $RunDir = $PSScriptRoot + "\Testing-CurrentVersion" 48 | $UninstallMethod = "Graceful" 49 | $LogDir = $FileShare + "\Sysmon-Logs\Testing-CurrentVersion" 50 | } 51 | $groupToMatch = "Sysmon-Verbose" 52 | if($groups -match $groupToMatch) 53 | { 54 | $ConfigFile = "Config\sysmonconfig-$OSType-verbose.xml" 55 | $LogDir = $FileShare + "\Sysmon-Logs\Verbose" 56 | } 57 | $groupToMatch = "Sysmon-Testing-NewVersion" 58 | if($groups -match $groupToMatch) 59 | { 60 | $ConfigFile = "Config\sysmonconfig-$OSType-testing.xml" 61 | $RunDir = $PSScriptRoot + "\Testing-NewVersion" 62 | $UninstallMethod = "Force" 63 | $LogDir = $FileShare + "\Sysmon-Logs\Testing-NewVersion" 64 | } 65 | if (-not($ConfigFile)) 66 | { 67 | $ConfigFile = "Config\sysmonconfig-$OSType-production.xml" 68 | $UninstallMethod = "Graceful" 69 | } 70 | } 71 | catch 72 | { 73 | Write-Host "$(Get-Date): Unable to query AD to dynamically set a configuration file." 74 | } 75 | 76 | # Run Update-Sysmon using calculated parameters 77 | . "$RunDir\Update-Sysmon.ps1" 78 | Update-Sysmon -RunDir $RunDir -ConfigFile $ConfigFile -UninstallMethod $UninstallMethod -LogDir $LogDir -Verbose 79 | -------------------------------------------------------------------------------- /x64/README.md: -------------------------------------------------------------------------------- 1 | # Update-Sysmon x64 Directory 2 | Stage the 64-bit Sysmon executable file in this directory. The file base name (the text before .exe) must match the SvcName parameter value used in your deployment. This will be the installed service name and the Sysmon running process name on each endpoint. Obtain Sysmon binaries directly from [Microsoft](https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon). -------------------------------------------------------------------------------- /x86/README.md: -------------------------------------------------------------------------------- 1 | # Update-Sysmon x86 Directory 2 | Stage the 32-bit Sysmon executable file in this directory. The file base name (the text before .exe) must match the SvcName parameter value used in your deployment. This will be the installed service name and the Sysmon running process name on each endpoint. Obtain Sysmon binaries directly from [Microsoft](https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon). --------------------------------------------------------------------------------