Set-StrictMode -Version Latest function Get-CampaignLaSessionProcessCorrelations { [CmdletBinding()] param() $matches = [Collections.Generic.List[object]]::new() if ($IsWindows) { $pattern = '(?i)(?:^|\s)(?:--config|--session-config)\s+(?:"([^"]+)"|(\S+))' foreach ($candidate in @(Get-CimInstance Win32_Process -ErrorAction Stop)) { $commandLine = [string]$candidate.CommandLine if ([string]::IsNullOrWhiteSpace($commandLine)) { continue } foreach ($match in [Text.RegularExpressions.Regex]::Matches( $commandLine, $pattern)) { $value = if ($match.Groups[1].Success) { $match.Groups[1].Value } else { $match.Groups[2].Value } if ([IO.Path]::IsPathFullyQualified($value)) { $matches.Add([pscustomobject]@{ ProcessId = [int]$candidate.ProcessId SessionConfigPath = [IO.Path]::GetFullPath($value) }) } } } } elseif ($IsLinux) { foreach ($directory in [IO.Directory]::EnumerateDirectories('/proc')) { $leaf = [IO.Path]::GetFileName($directory) $processId = 0 if (-not [int]::TryParse( $leaf, [Globalization.NumberStyles]::None, [Globalization.CultureInfo]::InvariantCulture, [ref]$processId)) { continue } try { $bytes = [IO.File]::ReadAllBytes((Join-Path $directory 'cmdline')) if ($bytes.Length -eq 0) { continue } $arguments = @([Text.Encoding]::UTF8.GetString($bytes).Split( [char]0, [StringSplitOptions]::RemoveEmptyEntries)) for ($index = 0; $index + 1 -lt $arguments.Count; $index++) { if ($arguments[$index] -cin @('--config', '--session-config') -and [IO.Path]::IsPathFullyQualified($arguments[$index + 1])) { $matches.Add([pscustomobject]@{ ProcessId = $processId SessionConfigPath = [IO.Path]::GetFullPath( $arguments[$index + 1]) }) } } } catch [IO.IOException] { # A process may exit between /proc enumeration and cmdline read. } catch [UnauthorizedAccessException] { # Other-user processes cannot be the owner-readable gate child. } } } else { throw 'Campaign LA process correlation supports Windows and Linux only.' } return @($matches) } function Get-CampaignLaCorrelatedProcessIds { [CmdletBinding()] param([Parameter(Mandatory = $true)][string]$SessionConfigPath) if (-not [IO.Path]::IsPathFullyQualified($SessionConfigPath)) { throw 'Session-config correlation requires an absolute path.' } $SessionConfigPath = [IO.Path]::GetFullPath($SessionConfigPath) $comparison = if ($IsWindows) { [StringComparison]::OrdinalIgnoreCase } else { [StringComparison]::Ordinal } $matches = [Collections.Generic.HashSet[int]]::new() foreach ($candidate in @(Get-CampaignLaSessionProcessCorrelations)) { if ([string]::Equals( $candidate.SessionConfigPath, $SessionConfigPath, $comparison)) { $null = $matches.Add([int]$candidate.ProcessId) } } return @($matches | Sort-Object) }