Private/Processes/Processes.ps1

function Get-VSOProcessSnapshot {
    return @(Get-CimInstance -ClassName Win32_Process -ErrorAction Stop | Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine,CreationDate)
}

function Split-VSOCommandLine {
    param([string]$CommandLine)
    if (-not ('VSOManager.NativeArguments' -as [type])) {
        Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
namespace VSOManager {
  public static class NativeArguments {
    [DllImport("shell32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
    private static extern IntPtr CommandLineToArgvW(string command, out int count);
    [DllImport("kernel32.dll")] private static extern IntPtr LocalFree(IntPtr memory);
    public static string[] Split(string command) {
      int count; IntPtr memory=CommandLineToArgvW(command,out count);
      if(memory==IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
      try { string[] result=new string[count];
        for(int i=0;i<count;i++) result[i]=Marshal.PtrToStringUni(Marshal.ReadIntPtr(memory,i*IntPtr.Size));
        return result;
      } finally { LocalFree(memory); }
    }
  }
}
'@
 -ErrorAction Stop
    }
    return [VSOManager.NativeArguments]::Split($CommandLine)
}

function Test-VSOProtectedWrapperShape {
    param($Ast)
    $decoders=@($Ast.FindAll({param($n) $n -is [Management.Automation.Language.FunctionDefinitionAst] -and $n.Name -ceq 'D'},$false))
    if ($decoders.Count -ne 1 -or ($decoders[0].Extent.Text -replace '\s','') -cne 'functionD([string]$v){[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($v))}') { return $false }
    $assignments=@($Ast.FindAll({param($n) $n -is [Management.Automation.Language.AssignmentStatementAst]},$false))
    $required=@('$source','$expected','$text','$h','$actual','$sb','$invoke')
    $parts=@{}
    foreach ($name in $required) {
        $items=@($assignments|Where-Object {$_.Left.Extent.Text -ceq $name})
        if ($items.Count -ne 1) { return $false }; $parts[$name]=$items[0]
    }
    if (($parts['$text'].Right.Extent.Text -replace '\s','') -cne '[IO.File]::ReadAllText($stage,[Text.Encoding]::UTF8)') { return $false }
    if (($parts['$h'].Right.Extent.Text -replace '\s','') -cne '[Security.Cryptography.SHA256]::Create()') { return $false }
    if (($parts['$actual'].Right.Extent.Text -replace '\s','') -notmatch '\.ComputeHash\(\$enc\.GetBytes\(\$text\)\)') { return $false }
    if (($parts['$sb'].Right.Extent.Text -replace '\s','') -cne '[ScriptBlock]::Create($text)') { return $false }
    $guard=$false
    foreach ($condition in @($Ast.FindAll({param($n) $n -is [Management.Automation.Language.IfStatementAst]},$false))) {
        foreach ($clause in $condition.Clauses) {
            if (($clause.Item1.Extent.Text -replace '\s','') -ceq '$actual-cne$expected' -and $condition.Extent.StartOffset -lt $parts['$sb'].Extent.StartOffset -and $clause.Item2.Find({param($n) $n -is [Management.Automation.Language.ThrowStatementAst]},$false)) { $guard=$true }
        }
    }
    if (-not $guard) { return $false }
    $tables=@($parts['$invoke'].Right.FindAll({param($n) $n -is [Management.Automation.Language.HashtableAst]},$false))
    if ($tables.Count -ne 1) { return $false }
    $expected=@{VSOTrustedHost='$true';VSOTrustedMemoryHost='$true';VSOOriginalSourcePath='$source';VSOExpectedSourceSha256='$expected';VSOExpectedUserSid='$sid';DirectAction='$action';StartupProbe='$probe'}
    if ($tables[0].KeyValuePairs.Count -ne $expected.Count) { return $false }
    foreach ($pair in $tables[0].KeyValuePairs) {
        $key=$pair.Item1.Extent.Text
        if (-not $expected.ContainsKey($key) -or ($pair.Item2.Extent.Text -replace '\s','').TrimEnd(';') -cne $expected[$key]) { return $false }
    }
    $calls=@($Ast.FindAll({param($n) $n -is [Management.Automation.Language.CommandAst] -and ($n.Extent.Text -replace '\s','') -ceq '&$sb@invoke'},$false))
    return ($calls.Count -eq 1)
}

function Test-VSOEncodedHost {
    param([string]$Text, [string]$Root, [string[]]$Hashes, [int]$Depth=0)
    if ($Depth -gt 2 -or $Text.Length -gt 131072) { return $false }
    $tokens=$null; $errors=$null
    $ast=[Management.Automation.Language.Parser]::ParseInput($Text,[ref]$tokens,[ref]$errors)
    if ($errors.Count) { return $false }
    # These are data reads from the inspected official protected-memory wrapper.
    # Never invoke a decoded command or evaluate an AST from process data.
    $identityAssignments=@($ast.FindAll({param($n) $n -is [Management.Automation.Language.AssignmentStatementAst]},$false))
    $sourceNodes=@($identityAssignments|Where-Object {$_.Left.Extent.Text -ceq '$source'})
    $hashNodes=@($identityAssignments|Where-Object {$_.Left.Extent.Text -ceq '$expected'})
    $sourceText=if ($sourceNodes.Count -eq 1) {$sourceNodes[0].Right.Extent.Text}else{''}
    $hashText=if ($hashNodes.Count -eq 1) {$hashNodes[0].Right.Extent.Text}else{''}
    $sourceMatch=[regex]::Matches($sourceText,'^D\(''([A-Za-z0-9+/=]+)''\)$')
    $hashMatch=[regex]::Matches($hashText,'^''([A-Fa-f0-9]{64})''$')
    if ($sourceMatch.Count -eq 1 -and $hashMatch.Count -eq 1 -and (Test-VSOProtectedWrapperShape $ast)) {
        try {
            $source=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($sourceMatch[0].Groups[1].Value))
            $full=[IO.Path]::GetFullPath($source)
            if ($full -iin @((Join-Path $Root 'VSO7.ps1'),(Join-Path $Root 'VSO7_7.0.0_RC_R80.ps1')) -and $hashMatch[0].Groups[1].Value.ToLowerInvariant() -cin $Hashes) { return $true }
        } catch { return $false }
    }
    # The VSO7 Windows Terminal broker embeds the exact wrapper in a literal payload.
    $pipeConstructors=@($ast.FindAll({param($node) $node -is [Management.Automation.Language.CommandAst] -and $node.GetCommandName() -eq 'New-Object' -and $node.Extent.Text.Contains('IO.Pipes.NamedPipeServerStream')},$false))
    $payloadConsumers=@($ast.FindAll({param($node) $node -is [Management.Automation.Language.InvokeMemberExpressionAst] -and ($node.Extent.Text -replace '\s','') -ceq '[ScriptBlock]::Create($payload)'},$true))
    if ($pipeConstructors.Count -eq 0 -or $payloadConsumers.Count -eq 0 -or -not $Text.Contains('VSO7.Terminal.')) { return $false }
    foreach ($assignment in @($ast.FindAll({param($node) $node -is [Management.Automation.Language.AssignmentStatementAst] -and $node.Left.Extent.Text -ceq '$payload'},$false))) {
        $values=@($assignment.Right.FindAll({param($node) $node -is [Management.Automation.Language.StringConstantExpressionAst]},$true))
        if ($values.Count -eq 1 -and (Test-VSOEncodedHost $values[0].Value $Root $Hashes ($Depth+1))) { return $true }
    }
    return $false
}

function Get-VSOProcesses {
    param([string]$Root, $Context, $Receipt, [object[]]$Snapshot)
    if (-not $PSBoundParameters.ContainsKey('Snapshot')) {
        try { $Snapshot=@(Get-VSOProcessSnapshot) }
        catch { return [pscustomobject]@{ Reliable=$false; Processes=@(); Diagnostic=$_.Exception.Message } }
    }
    $hashes=@()
    try {
        if ($Receipt) {
            $hashes=@([string](Get-VSOProperty $Receipt MemorySourceSha256 ''))
        } elseif ([IO.File]::Exists((Join-Path $Root 'VSO7.ps1'))) { $hashes=@(Get-VSOSourceTextHash (Join-Path $Root 'VSO7.ps1') $Context) }
    } catch { return [pscustomobject]@{Reliable=$false;Processes=@();Diagnostic=$_.Exception.Message} }
    $owned=[Collections.Generic.List[object]]::new(); $reliable=$true
    $system=Get-VSOSystemDirectory $Context
    $windowsPS=Join-Path $system 'WindowsPowerShell\v1.0\powershell.exe'
    $windowsCmd=Join-Path $system 'cmd.exe'
    foreach ($process in $Snapshot) {
        if ($process.Name -notin @('powershell.exe','cmd.exe')) { continue }
        if (-not $process.CommandLine -or -not $process.ExecutablePath -or -not $process.CreationDate) { $reliable=$false; continue }
        $evidence=$null
        if ($process.Name -ieq 'powershell.exe' -and $process.ExecutablePath -ieq $windowsPS) {
            try { $arguments=@(Split-VSOCommandLine $process.CommandLine) } catch { $reliable=$false; continue }
            for ($i=1; $i -lt $arguments.Count-1; $i++) {
                if ($arguments[$i] -in @('-Command','-c','-CommandWithArgs','-EncodedArguments')) { break }
                if ($arguments[$i] -ieq '-File') {
                    $file=$arguments[$i+1]
                    if ($file -iin @((Join-Path $Root 'VSO7.ps1'),(Join-Path $Root 'VSO7_7.0.0_RC_R80.ps1'))) { $evidence='ExactScriptPath' }
                    if ($file -ieq (Join-Path $Root 'Tools\VSO7_STARTUP_GATE.ps1')) {
                        for ($j=$i+2; $j -lt $arguments.Count-1; $j++) { if ($arguments[$j] -ieq '-Root' -and $arguments[$j+1].TrimEnd('\','.') -ieq $Root.TrimEnd('\')) { $evidence='ExactStartupGate' } }
                    }
                    break
                }
                if ($arguments[$i] -ieq '-EncodedCommand') {
                    try {
                        $decoded=[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($arguments[$i+1]))
                        if (Test-VSOEncodedHost $decoded $Root $hashes) { $evidence='ProtectedMemorySourceAndHash' }
                        elseif ($decoded -match 'VSO7|VSOTrustedMemoryHost') { $reliable=$false }
                    } catch { $reliable=$false }
                    break
                }
            }
        }
        if ($process.Name -ieq 'cmd.exe' -and $process.ExecutablePath -ieq $windowsCmd) {
            $launcher=[regex]::Escape((Join-Path $Root 'START VSO7.bat'))
            if ($process.CommandLine -match ('(?i)^"?'+[regex]::Escape($windowsCmd)+'"?\s+/d\s+/s\s+/c\s+""'+$launcher+'""\s*$')) { $evidence='ExactOfficialLauncher' }
            $managedArguments='/d /s /c "set "PSModulePath=" & call "'+(Join-Path $Root 'START VSO7.bat')+'""'
            if ($process.CommandLine -match ('(?i)^"?'+[regex]::Escape($windowsCmd)+'"?\s+'+[regex]::Escape($managedArguments)+'\s*$')) { $evidence='ExactOfficialLauncher' }
        }
        # Shared terminals, arbitrary descendants and unrelated PowerShell processes are excluded.
        if ($evidence) { $owned.Add([pscustomobject]@{ ProcessId=[int]$process.ProcessId; ParentProcessId=[int]$process.ParentProcessId; CreationDate=$process.CreationDate; ExecutablePath=$process.ExecutablePath; CommandLine=$process.CommandLine; Evidence=$evidence }) }
    }
    return [pscustomobject]@{ Reliable=$reliable; Processes=@($owned.ToArray()); Diagnostic=$(if ($reliable) {$null}else{$Context.Text.ProcessVisibility}) }
}

function New-VSOLaunchInfo {
    param([string]$ApplicationPath, $Context)
    $info=[Diagnostics.ProcessStartInfo]::new()
    $info.FileName=Join-Path (Get-VSOSystemDirectory $Context) 'cmd.exe'
    $info.Arguments='/d /s /c "set "PSModulePath=" & call "'+(Join-Path $ApplicationPath 'START VSO7.bat')+'""'
    $info.WorkingDirectory=$ApplicationPath
    # cmd is an intermediate process: it otherwise passes Core's module paths
    # to the official Windows PowerShell launcher. Let Desktop construct its own
    # paths. This changes only the child's environment, never the caller's.
    # Keep ShellExecute's separate application window. Clear the variable in
    # the child cmd with a constant prefix; validated paths forbid cmd expansion.
    $info.UseShellExecute=$true
    $info.CreateNoWindow=$false
    $info.WindowStyle=[Diagnostics.ProcessWindowStyle]::Normal
    return $info
}

function Invoke-VSOLauncher {
    param($Installation, $Context)
    $root=Resolve-VSOPath $Installation.Path $Context -Installation
    $app=Resolve-VSOPath $Installation.ApplicationPath $Context
    if (-not (Test-VSOWithin $app $root -AllowEqual)) { Throw-VSOError $Context Path @($app) }
    if ([IO.Path]::GetFileName($app) -ine 'VSO7') { Throw-VSOError $Context Vso7Folder }
    if ($Installation.Receipt) { Test-VSOInstalledFiles $Installation.Receipt $Context }
    $null=Get-VSODistribution $app $Context -ExpectedVersion $Installation.Version
    $info=New-VSOLaunchInfo $app $Context
    $child=[Diagnostics.Process]::Start($info)
    if (-not $child) { Throw-VSOError $Context Failure }
    try { return [pscustomobject]@{ PSTypeName='VSO.LaunchResult'; Path=$root; ApplicationPath=$app; Version=$Installation.Version; Launcher='START VSO7.bat'; LauncherProcessId=$child.Id; StartupVerified=$false; Message=$Context.Text.Started } }
    finally { $child.Dispose(); Write-VSOProgress $Context Verify -Completed }
}

function Stop-VSOAttributedProcess {
    param($Attributed, $Context)
    $live=@(Get-VSOProcessSnapshot | Where-Object ProcessId -EQ $Attributed.ProcessId)
    # Stopping one owned host can let its sibling exit before this turn. Absence
    # needs no termination; an occupied/reused PID still requires exact identity.
    if ($live.Count -eq 0) { return }
    if ($live.Count -ne 1 -or $live[0].CreationDate -ne $Attributed.CreationDate -or $live[0].CommandLine -cne $Attributed.CommandLine -or $live[0].ExecutablePath -ine $Attributed.ExecutablePath) { Throw-VSOError $Context ProcessUnknown }
    try { $process=Get-Process -Id $Attributed.ProcessId -ErrorAction Stop }
    catch {
        # Win32_Process may briefly retain a terminating host after Get-Process
        # no longer finds it. Wait only for absence; never acquire/kill another PID
        # from this branch. Any occupied identity change still fails immediately.
        for ($attempt=0; $attempt -lt 4; $attempt++) {
            $remaining=@(Get-VSOProcessSnapshot | Where-Object ProcessId -EQ $Attributed.ProcessId)
            if ($remaining.Count -eq 0) { return }
            if ($remaining.Count -ne 1 -or $remaining[0].CreationDate -ne $Attributed.CreationDate -or $remaining[0].CommandLine -cne $Attributed.CommandLine -or $remaining[0].ExecutablePath -ine $Attributed.ExecutablePath) { Throw-VSOError $Context ProcessUnknown }
            if ($attempt -lt 3) { Start-Sleep -Milliseconds 250 }
        }
        Throw-VSOError $Context ProcessUnknown
    }
    try {
        $null=$process.Handle
        if ($process.HasExited) { return }
        # CIM's DMTF timestamp has microsecond precision; Process.StartTime can
        # retain sub-microsecond FILETIME ticks. Preserve identity without requiring
        # an impossible equality at a finer precision than the snapshot.
        $difference=[Math]::Abs($process.StartTime.ToUniversalTime().Ticks-([DateTime]$Attributed.CreationDate).ToUniversalTime().Ticks)
        if ($difference -ge 10) { Throw-VSOError $Context ProcessUnknown }
        $process.Kill()
        if (-not $process.WaitForExit(10000)) { Throw-VSOError $Context Failure }
    } catch {
        if (-not $process.HasExited) { throw }
    } finally { $process.Dispose() }
}