Private/Collectors.Persistence.ps1

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

    $warnings = New-Object System.Collections.ArrayList
    $files = New-Object System.Collections.ArrayList

    $runKeys = New-Object System.Collections.Generic.List[string]
    foreach ($path in @(
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce',
        'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run',
        'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\RunOnce',
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run',
        'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
        'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce',
        'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run'
    )) {
        $runKeys.Add($path)
    }

    try {
        foreach ($hive in Get-ChildItem -LiteralPath 'Registry::HKEY_USERS' -ErrorAction Stop) {
            $sidName = Split-Path -Leaf $hive.Name
            if ($sidName -match '_Classes$') {
                continue
            }
            $runKeys.Add("Registry::HKEY_USERS\$sidName\SOFTWARE\Microsoft\Windows\CurrentVersion\Run")
            $runKeys.Add("Registry::HKEY_USERS\$sidName\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce")
            $runKeys.Add("Registry::HKEY_USERS\$sidName\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run")
        }
    }
    catch { Add-ICCollectorWarning -List $warnings -Message "Loaded user registry hives: $($_.Exception.Message)" }

    $registryAutoruns = @()
    try {
        $registryAutoruns += @(Get-ICRegistryValues -Path @($runKeys))
        $registryAutoruns += @(Get-ICRegistryValues -Path @(
            'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon',
            'HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows'
        ) -ValueName @('Shell', 'Userinit', 'Taskman', 'VmApplet', 'Load', 'Run'))
        $registryAutoruns += @(Get-ICRegistryValues -Path @(
            'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows',
            'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows'
        ) -ValueName @('AppInit_DLLs', 'LoadAppInit_DLLs', 'RequireSignedAppInit_DLLs'))
        $registryAutoruns += @(Get-ICRegistryValues -Path @(
            'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls',
            'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa'
        ) -ValueName @('Authentication Packages', 'Notification Packages', 'Security Packages'))
    }
    catch { Add-ICCollectorWarning -List $warnings -Message "Autorun registry snapshot: $($_.Exception.Message)" }
    Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Persistence -RelativePath 'evidence/persistence/registry-autoruns.json' -Data $registryAutoruns -Csv)

    $ifeo = New-Object System.Collections.ArrayList
    foreach ($root in @(
        'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options',
        'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options'
    )) {
        if (-not (Test-Path -LiteralPath $root)) {
            continue
        }
        try {
            foreach ($subKey in Get-ChildItem -LiteralPath $root -ErrorAction Stop) {
                $values = Get-ICRegistryValues -Path @($subKey.PSPath) -ValueName @('Debugger', 'GlobalFlag', 'VerifierDlls')
                foreach ($value in $values) {
                    if ($null -ne $value.Data -and -not [string]::IsNullOrWhiteSpace([string]$value.Data)) {
                        [void]$ifeo.Add($value)
                    }
                }
            }
        }
        catch { Add-ICCollectorWarning -List $warnings -Message "IFEO snapshot '$root': $($_.Exception.Message)" }
    }
    Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Persistence -RelativePath 'evidence/persistence/ifeo-debuggers.json' -Data @($ifeo) -Csv)

    $startupDirectories = New-Object System.Collections.Generic.HashSet[string] ([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($folderName in @('Startup', 'CommonStartup')) {
        try {
            $folder = [System.Environment]::GetFolderPath($folderName)
            if (-not [string]::IsNullOrWhiteSpace($folder)) { [void]$startupDirectories.Add($folder) }
        }
        catch { }
    }
    try {
        foreach ($profile in Get-CimInstance -ClassName Win32_UserProfile -ErrorAction Stop) {
            if (-not [string]::IsNullOrWhiteSpace([string]$profile.LocalPath)) {
                [void]$startupDirectories.Add((Join-Path $profile.LocalPath 'AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup'))
            }
        }
    }
    catch { Add-ICCollectorWarning -List $warnings -Message "User profile startup paths: $($_.Exception.Message)" }

    $startupFiles = New-Object System.Collections.ArrayList
    foreach ($directory in $startupDirectories) {
        if (-not (Test-Path -LiteralPath $directory -PathType Container)) {
            continue
        }
        try {
            foreach ($item in Get-ChildItem -LiteralPath $directory -File -Recurse -Force -ErrorAction Stop) {
                $metadata = Get-ICFileMetadata -Path $item.FullName -Hash:$Context.Configuration.HashPersistenceFiles
                $metadata | Add-Member -NotePropertyName StartupDirectory -NotePropertyValue $directory -Force
                [void]$startupFiles.Add($metadata)
            }
        }
        catch { Add-ICCollectorWarning -List $warnings -Message "Startup folder '$directory': $($_.Exception.Message)" }
    }
    Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Persistence -RelativePath 'evidence/persistence/startup-files.json' -Data @($startupFiles) -Csv)

    $wmiSubscriptions = New-Object System.Collections.ArrayList
    if ($Context.Configuration.CollectWmiSubscriptions) {
        $classes = @(
            '__EventFilter',
            'CommandLineEventConsumer',
            'ActiveScriptEventConsumer',
            'LogFileEventConsumer',
            'NTEventLogEventConsumer',
            'SMTPEventConsumer',
            '__FilterToConsumerBinding'
        )
        foreach ($className in $classes) {
            try {
                foreach ($instance in Get-CimInstance -Namespace 'root/subscription' -ClassName $className -ErrorAction Stop) {
                    [void]$wmiSubscriptions.Add([pscustomobject][ordered]@{
                        Class = $className
                        RelativePath = Get-ICPropertyValue -InputObject $instance.CimSystemProperties -Name 'Path'
                        Name = Get-ICPropertyValue -InputObject $instance -Name 'Name'
                        Query = Get-ICPropertyValue -InputObject $instance -Name 'Query'
                        QueryLanguage = Get-ICPropertyValue -InputObject $instance -Name 'QueryLanguage'
                        EventNamespace = Get-ICPropertyValue -InputObject $instance -Name 'EventNamespace'
                        CommandLineTemplate = Get-ICPropertyValue -InputObject $instance -Name 'CommandLineTemplate'
                        ExecutablePath = Get-ICPropertyValue -InputObject $instance -Name 'ExecutablePath'
                        ScriptingEngine = Get-ICPropertyValue -InputObject $instance -Name 'ScriptingEngine'
                        ScriptText = Get-ICPropertyValue -InputObject $instance -Name 'ScriptText'
                        Filter = [string](Get-ICPropertyValue -InputObject $instance -Name 'Filter')
                        Consumer = [string](Get-ICPropertyValue -InputObject $instance -Name 'Consumer')
                    })
                }
            }
            catch { Add-ICCollectorWarning -List $warnings -Message "WMI subscription class '$className': $($_.Exception.Message)" }
        }
    }
    Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector Persistence -RelativePath 'evidence/persistence/wmi-subscriptions.json' -Data @($wmiSubscriptions))

    return New-ICCollectorResultData -OutputFiles @($files) -Warnings @($warnings) -Metrics ([ordered]@{
        RegistryAutorunValues = $registryAutoruns.Count
        IfeoDebuggerValues = $ifeo.Count
        StartupFiles = $startupFiles.Count
        WmiSubscriptionObjects = $wmiSubscriptions.Count
        PersistenceFilesHashed = [bool]$Context.Configuration.HashPersistenceFiles
    })
}