Public/New-NSPProcmonFilter.ps1
|
function New-NSPProcmonFilter { <# .SYNOPSIS Writes a Process Monitor filter file (.pmc) for the sharing-violation proof - natively, no Python or ProcMon needed. Returns the file. .DESCRIPTION ProcMon OR's include rules within one column and requires a match in EVERY column that has includes. So "Result is SHARING VIOLATION" and a process-name include must never share a file - together they would hide the sensor's own (successful) open. The kinds keep them apart: Violations Result is SHARING VIOLATION. The primary proof: any process. Activity Operation is CreateFile/CloseFile, and Path contains one of -Path (the data folder, e.g. '\Clients\'). Shows the handle overlap on the contested files. Processes Process Name is one of -ProcessName (MsSense.exe and MsMpEng.exe are always added), and Operation is CreateFile/CloseFile. Narrow app-plus-Defender view. FileOpens Operation is CreateFile/CloseFile, nothing else. What the analysis exports. Custom -Rule as given. The filter only changes what ProcMon shows: the capture keeps every event. .PARAMETER Kind Which filter to write (see the description). .PARAMETER OutputPath The .pmc file to write (replaced if it exists). .PARAMETER Path Path fragments for Activity (matched with "contains"). .PARAMETER ProcessName Executable names for Processes. .PARAMETER Rule For Custom: objects with Column (ProcessName, PID, Operation, Result, Detail, ImagePath, Path, Duration), Relation (Is, IsNot, LessThan, MoreThan, BeginsWith, EndsWith, Contains, Excludes), Value and Action (Include or Exclude). .EXAMPLE New-NSPProcmonFilter -Kind Violations -OutputPath C:\Temp\Violations.pmc .EXAMPLE New-NSPProcmonFilter -Kind Processes -ProcessName 'pvxwin64.exe' -OutputPath C:\Temp\AppAndEdr.pmc #> [CmdletBinding(SupportsShouldProcess)] [OutputType([IO.FileInfo])] param( [Parameter(Mandatory)][ValidateSet('Violations', 'Activity', 'Processes', 'FileOpens', 'Custom')][string]$Kind, [Parameter(Mandatory)][string]$OutputPath, [string[]]$Path = @(), [string[]]$ProcessName = @(), [object[]]$Rule = @() ) $new = { param($Column, $Relation, $Value) [pscustomobject]@{ Column = $Column; Relation = $Relation; Value = $Value; Action = 'Include' } } $opens = @((& $new 'Operation' 'Is' 'CreateFile'), (& $new 'Operation' 'Is' 'CloseFile')) $rules = switch ($Kind) { 'Violations' { , @(& $new 'Result' 'Is' 'SHARING VIOLATION') } 'Activity' { $fragments = @($Path | Where-Object { $_ } | Select-Object -Unique) if (-not $fragments) { throw 'Activity needs at least one -Path fragment (e.g. ''\Clients\'').' } , ($opens + @($fragments | ForEach-Object { & $new 'Path' 'Contains' $_ })) } 'Processes' { $names = @(@($ProcessName) + 'MsSense.exe', 'MsMpEng.exe' | Where-Object { $_ } | Select-Object -Unique) , (@($names | ForEach-Object { & $new 'ProcessName' 'Is' $_ }) + $opens) } 'FileOpens' { , $opens } 'Custom' { if (-not $Rule) { throw 'Custom needs -Rule.' } , @($Rule | ForEach-Object { [pscustomobject]@{ Column = $_.Column; Relation = $_.Relation; Value = $_.Value; Action = $(if ($_.Action) { $_.Action } else { 'Include' }) } }) } } $bytes = ConvertTo-NSPProcmonConfigBytes -Rule $rules if (-not $PSCmdlet.ShouldProcess($OutputPath, "Write ProcMon filter ($Kind, $(@($rules).Count) rules)")) { return } $parent = Split-Path -Parent $OutputPath if ($parent -and -not (Test-Path -LiteralPath $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } [IO.File]::WriteAllBytes($OutputPath, $bytes) Get-Item -LiteralPath $OutputPath } |