Private/Collectors.Processes.ps1

function Get-ICProcessEvidence {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object]$Context
    )

    $warnings = New-Object System.Collections.ArrayList
    $files = New-Object System.Collections.ArrayList
    $userByProcessId = @{}

    try {
        foreach ($process in Get-Process -IncludeUserName -ErrorAction Stop) {
            $userByProcessId[[int]$process.Id] = $process.UserName
        }
    }
    catch {
        Add-ICCollectorWarning -List $warnings -Message "Process owners are partial: $($_.Exception.Message)"
    }

    $processes = @()
    try {
        $processes = @(Get-CimInstance -ClassName Win32_Process -ErrorAction Stop | ForEach-Object {
            $pidValue = [int]$_.ProcessId
            [pscustomobject][ordered]@{
                ProcessId = $pidValue
                ParentProcessId = [int]$_.ParentProcessId
                Name = $_.Name
                ExecutablePath = $_.ExecutablePath
                CommandLine = if ($Context.Configuration.IncludeProcessCommandLines) { $_.CommandLine } else { $null }
                Owner = if ($userByProcessId.ContainsKey($pidValue)) { $userByProcessId[$pidValue] } else { $null }
                SessionId = $_.SessionId
                CreationDateUtc = ConvertTo-ICIso8601 -Value $_.CreationDate
                ThreadCount = $_.ThreadCount
                HandleCount = $_.HandleCount
                WorkingSetSize = $_.WorkingSetSize
                KernelModeTime = $_.KernelModeTime
                UserModeTime = $_.UserModeTime
            }
        } | Sort-Object ProcessId)
    }
    catch { Add-ICCollectorWarning -List $warnings -Message "Process inventory: $($_.Exception.Message)" }

    Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Processes -RelativePath 'evidence/processes/processes.json' -Data $processes -Csv)

    $hashRecords = @()
    if ($Context.Configuration.HashProcessExecutables) {
        $paths = @($processes.ExecutablePath | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } | Sort-Object -Unique | Select-Object -First $Context.Configuration.MaximumExecutableHashes)
        $hashRecords = @($paths | ForEach-Object {
            $path = [string]$_
            $hash = $null
            $signatureStatus = $null
            $signer = $null
            $errorMessage = $null
            try {
                if (Test-Path -LiteralPath $path -PathType Leaf) {
                    $hash = (Get-FileHash -LiteralPath $path -Algorithm SHA256 -ErrorAction Stop).Hash.ToLowerInvariant()
                    if (Test-ICCommandAvailable -Name 'Get-AuthenticodeSignature') {
                        $signature = Get-AuthenticodeSignature -FilePath $path -ErrorAction Stop
                        $signatureStatus = $signature.Status.ToString()
                        if ($null -ne $signature.SignerCertificate) {
                            $signer = $signature.SignerCertificate.Subject
                        }
                    }
                }
                else {
                    $errorMessage = 'Path is not accessible or no longer exists.'
                }
            }
            catch { $errorMessage = $_.Exception.Message }

            [pscustomobject][ordered]@{
                Path = $path
                SHA256 = $hash
                SignatureStatus = $signatureStatus
                SignerSubject = $signer
                Error = $errorMessage
            }
        })
        Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Processes -RelativePath 'evidence/processes/executable-hashes.json' -Data $hashRecords -Csv)
    }

    $uniqueImages = @($processes.ExecutablePath | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } | Sort-Object -Unique).Count
    return New-ICCollectorResultData -OutputFiles @($files) -Warnings @($warnings) -Metrics ([ordered]@{
        ProcessCount = $processes.Count
        UniqueExecutablePaths = $uniqueImages
        ExecutablesHashed = $hashRecords.Count
        CommandLinesIncluded = [bool]$Context.Configuration.IncludeProcessCommandLines
    })
}