[CmdletBinding()] param( [ValidateSet("Check", "Apply")] [string]$Mode = "Check", [Parameter(Mandatory = $true)] [ValidateRange(1, 2147483647)] [int]$ExpectedControlPid, [Parameter(Mandatory = $true)] [datetime]$ExpectedControlCreated, [Parameter(Mandatory = $true)] [ValidateRange(1, 2147483647)] [int]$ExpectedRecoverPid, [Parameter(Mandatory = $true)] [datetime]$ExpectedRecoverCreated, [Parameter(Mandatory = $true)] [ValidateRange(1, 2147483647)] [int]$ExpectedRecoverParentPid, [string]$AgentRoot = "C:\Wooo\Agent99" ) $ErrorActionPreference = "Stop" $taskName = "Wooo-Agent99-Control-Loop" $runtimeWritePerformed = $false function Get-ExactProcess { param([int]$ProcessId) Get-CimInstance Win32_Process -ErrorAction Stop | Where-Object { [int]$_.ProcessId -eq $ProcessId } | Select-Object -First 1 } function Test-ExactCreationTime { param([object]$Process, [datetime]$ExpectedCreated) if (-not $Process -or -not $Process.CreationDate) { return $false } $actual = ([datetime]$Process.CreationDate).ToUniversalTime() $expected = $ExpectedCreated.ToUniversalTime() [bool]($actual.Ticks -eq $expected.Ticks) } function Test-ControlIdentity { param([object]$Process) [bool]( $Process -and [string]$Process.Name -eq "powershell.exe" -and (Test-ExactCreationTime $Process $ExpectedControlCreated) -and ([string]$Process.CommandLine) -match '(?i)agent99-run\.ps1"?\s+-Mode\s+ControlTick(\s|$)' ) } function Test-RecoverIdentity { param([object]$Process) [bool]( $Process -and [string]$Process.Name -eq "powershell.exe" -and (Test-ExactCreationTime $Process $ExpectedRecoverCreated) -and [int]$Process.ParentProcessId -eq $ExpectedRecoverParentPid -and ([string]$Process.CommandLine) -match '(?i)agent99-run\.ps1"?\s+-Mode\s+Recover\s+-ControlledApply' ) } function Wait-ProcessAbsent { param([int]$ProcessId, [int]$TimeoutSeconds) $stopwatch = [Diagnostics.Stopwatch]::StartNew() while ($stopwatch.Elapsed.TotalSeconds -lt $TimeoutSeconds) { if (-not (Get-Process -Id $ProcessId -ErrorAction SilentlyContinue)) { return $true } Start-Sleep -Milliseconds 200 } [bool](-not (Get-Process -Id $ProcessId -ErrorAction SilentlyContinue)) } function Test-RecoveryMutexActive { $mutex = $null $acquired = $false try { $mutex = [Threading.Mutex]::new($false, "Global\WoooAgent99RecoveryTransportV1") try { $acquired = [bool]$mutex.WaitOne(0) } catch [Threading.AbandonedMutexException] { $acquired = $true } [bool](-not $acquired) } finally { if ($acquired -and $mutex) { try { $mutex.ReleaseMutex() } catch {} } if ($mutex) { $mutex.Dispose() } } } function Write-DurableReceipt { param([object]$Value, [string]$Path) $temp = "$Path.tmp-$PID-$([guid]::NewGuid().ToString('N'))" $stream = $null $writer = $null try { $encoding = New-Object Text.UTF8Encoding($false) $stream = [IO.File]::Open($temp, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None) $writer = New-Object IO.StreamWriter($stream, $encoding) $writer.Write(($Value | ConvertTo-Json -Depth 8)) $writer.Flush() $stream.Flush($true) $writer.Dispose(); $writer = $null $stream.Dispose(); $stream = $null $beforeHash = (Get-FileHash -LiteralPath $temp -Algorithm SHA256).Hash.ToLowerInvariant() Move-Item -LiteralPath $temp -Destination $Path -Force $afterHash = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() if ($beforeHash -ne $afterHash) { throw "receipt_readback_mismatch" } } finally { if ($writer) { $writer.Dispose() } if ($stream) { $stream.Dispose() } Remove-Item -LiteralPath $temp -Force -ErrorAction SilentlyContinue } } $maintenanceMutex = [Threading.Mutex]::new($false, "Global\WoooAgent99ControlLoopMaintenanceV1") $maintenanceMutexAcquired = $false try { try { $maintenanceMutexAcquired = [bool]$maintenanceMutex.WaitOne(0) } catch [Threading.AbandonedMutexException] { $maintenanceMutexAcquired = $true } if (-not $maintenanceMutexAcquired) { [pscustomobject]@{ schemaVersion = "agent99_control_loop_maintenance_v2" timestamp = (Get-Date).ToString("o") mode = $Mode precheckPassed = $false blockers = @("maintenance_writer_active") terminal = "blocked_single_writer_active" runtimeWritePerformed = $false secretValueRead = $false } | ConvertTo-Json -Depth 5 -Compress exit 75 } $blockers = New-Object System.Collections.Generic.List[string] if ($env:COMPUTERNAME -ne "WOOO-SUPER") { $blockers.Add("computer_identity_mismatch") } $task = Get-ScheduledTask -TaskName $taskName -TaskPath "\" -ErrorAction SilentlyContinue if (-not $task) { $blockers.Add("control_task_missing") } else { $actions = @($task.Actions) if ($actions.Count -ne 1) { $blockers.Add("control_task_action_count_mismatch") } if ($actions.Count -eq 1) { if ([string]$actions[0].Execute -notmatch '(?i)\\WindowsPowerShell\\v1\.0\\powershell\.exe$') { $blockers.Add("control_task_executable_mismatch") } if ([string]$actions[0].Arguments -notmatch '(?i)agent99-run\.ps1"?\s+-Mode\s+ControlTick(\s|$)') { $blockers.Add("control_task_arguments_mismatch") } } } $control = Get-ExactProcess $ExpectedControlPid $recover = Get-ExactProcess $ExpectedRecoverPid if (-not $control) { $blockers.Add("control_process_missing") } else { if (-not (Test-ControlIdentity $control)) { $blockers.Add("control_process_identity_mismatch") } } if (-not $recover) { $blockers.Add("recover_process_missing") } else { if (-not (Test-RecoverIdentity $recover)) { $blockers.Add("recover_process_identity_mismatch") } } $precheckPassed = [bool]($blockers.Count -eq 0) $controlStopped = $false $recoverStopped = $false $leaseMetadataRemoved = $false $mutexReleased = $false $taskAfter = $task $operationIntentRef = "" $deviceMutationAttempted = $false $operationFailureType = "" $terminal = if ($precheckPassed) { "maintenance_freeze_check_verified" } else { "blocked_identity_mismatch" } if ($Mode -eq "Apply" -and $precheckPassed) { $intentStamp = Get-Date -Format "yyyyMMdd-HHmmss-fff" $operationIntentRef = "agent99-control-loop-maintenance-intent-$intentStamp-$([guid]::NewGuid().ToString('N').Substring(0, 8)).json" $operationIntentPath = Join-Path $AgentRoot "evidence\$operationIntentRef" $operationIntent = [pscustomobject]@{ schemaVersion = "agent99_control_loop_maintenance_intent_v1" timestamp = (Get-Date).ToString("o") taskName = $taskName taskEnabledBefore = [bool]$task.Settings.Enabled expectedControlPid = $ExpectedControlPid expectedControlCreated = $ExpectedControlCreated.ToUniversalTime().ToString("o") expectedRecoverPid = $ExpectedRecoverPid expectedRecoverCreated = $ExpectedRecoverCreated.ToUniversalTime().ToString("o") expectedRecoverParentPid = $ExpectedRecoverParentPid plannedActions = @("disable_control_task", "stop_exact_control_tree", "stop_exact_recover_tree", "remove_exact_recovery_lease") rollback = "keep task frozen on partial mutation; restore only after patched runtime verifier" vmPowerChangePerformed = $false vmwareVmxProcessTerminated = $false secretValueRead = $false } Write-DurableReceipt $operationIntent $operationIntentPath $runtimeWritePerformed = $true try { $deviceMutationAttempted = $true Disable-ScheduledTask -TaskName $taskName -TaskPath "\" | Out-Null Stop-ScheduledTask -TaskName $taskName -TaskPath "\" -ErrorAction SilentlyContinue $controlStopped = Wait-ProcessAbsent $ExpectedControlPid 15 if (-not $controlStopped) { $controlRecheck = Get-ExactProcess $ExpectedControlPid if (-not (Test-ControlIdentity $controlRecheck)) { throw "control_recheck_identity_mismatch" } & "$env:SystemRoot\System32\taskkill.exe" /PID $ExpectedControlPid /T /F 2>&1 | Out-Null $controlStopped = Wait-ProcessAbsent $ExpectedControlPid 15 } if (-not (Wait-ProcessAbsent $ExpectedRecoverPid 5)) { $recoverRecheck = Get-ExactProcess $ExpectedRecoverPid if (-not (Test-RecoverIdentity $recoverRecheck)) { throw "recover_recheck_identity_mismatch" } & "$env:SystemRoot\System32\taskkill.exe" /PID $ExpectedRecoverPid /T /F 2>&1 | Out-Null } $recoverStopped = Wait-ProcessAbsent $ExpectedRecoverPid 15 $controlStopped = [bool]($controlStopped -or (Wait-ProcessAbsent $ExpectedControlPid 5)) $leasePath = Join-Path $AgentRoot "state\recovery-transport-lease.json" $mutexReleased = [bool](-not (Test-RecoveryMutexActive)) if ($recoverStopped -and $mutexReleased -and (Test-Path -LiteralPath $leasePath)) { $lease = Get-Content -LiteralPath $leasePath -Raw | ConvertFrom-Json if ( [int]$lease.processId -eq $ExpectedRecoverPid -and [string]$lease.runId -match "^(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|agent99-recovery-[0-9]{8}-[0-9]{6}-[0-9a-f]{8})$" ) { Remove-Item -LiteralPath $leasePath -Force } } $leaseMetadataRemoved = [bool](-not (Test-Path -LiteralPath $leasePath)) $taskAfter = Get-ScheduledTask -TaskName $taskName -TaskPath "\" -ErrorAction Stop $verified = [bool](-not $taskAfter.Settings.Enabled -and $controlStopped -and $recoverStopped -and $mutexReleased -and $leaseMetadataRemoved) $terminal = if ($verified) { "control_loop_maintenance_frozen" } else { "maintenance_freeze_unverified" } } catch { $operationFailureType = $_.Exception.GetType().Name $taskAfter = Get-ScheduledTask -TaskName $taskName -TaskPath "\" -ErrorAction SilentlyContinue $controlStopped = Wait-ProcessAbsent $ExpectedControlPid 1 $recoverStopped = Wait-ProcessAbsent $ExpectedRecoverPid 1 $mutexReleased = [bool](-not (Test-RecoveryMutexActive)) $leasePath = Join-Path $AgentRoot "state\recovery-transport-lease.json" $leaseMetadataRemoved = [bool](-not (Test-Path -LiteralPath $leasePath)) $terminal = if ($deviceMutationAttempted) { "maintenance_partial_frozen_manual_recovery" } else { "maintenance_apply_failed_no_mutation" } } } $result = [pscustomobject]@{ schemaVersion = "agent99_control_loop_maintenance_v2" timestamp = (Get-Date).ToString("o") mode = $Mode taskName = $taskName expectedControlPid = $ExpectedControlPid expectedControlCreated = $ExpectedControlCreated.ToUniversalTime().ToString("o") expectedRecoverPid = $ExpectedRecoverPid expectedRecoverCreated = $ExpectedRecoverCreated.ToUniversalTime().ToString("o") expectedRecoverParentPid = $ExpectedRecoverParentPid precheckPassed = $precheckPassed blockers = @($blockers) taskEnabledBefore = if ($task) { [bool]$task.Settings.Enabled } else { $null } taskEnabledAfter = if ($taskAfter) { [bool]$taskAfter.Settings.Enabled } else { $null } operationIntentRef = $operationIntentRef deviceMutationAttempted = $deviceMutationAttempted operationFailureType = $operationFailureType rollbackStatus = if ($deviceMutationAttempted -and $terminal -ne "control_loop_maintenance_frozen") { "task_kept_frozen_manual_recovery_required" } else { "not_required" } controlStopped = $controlStopped recoverStopped = $recoverStopped leaseMetadataRemoved = $leaseMetadataRemoved terminal = $terminal runtimeWritePerformed = $runtimeWritePerformed vmPowerChangePerformed = $false vmwareVmxProcessTerminated = $false vmxLockDeleted = $false hostRebootPerformed = $false secretValueRead = $false } if ($Mode -eq "Apply") { $path = Join-Path $AgentRoot ("evidence\agent99-control-loop-maintenance-" + (Get-Date -Format "yyyyMMdd-HHmmss-fff") + "-" + ([guid]::NewGuid().ToString('N').Substring(0, 8)) + ".json") Write-DurableReceipt $result $path $result | Add-Member -NotePropertyName receiptRef -NotePropertyValue (Split-Path -Leaf $path) -Force } $result | ConvertTo-Json -Depth 8 -Compress if (-not $precheckPassed) { exit 64 } if ($Mode -eq "Check") { exit 0 } if ($terminal -eq "control_loop_maintenance_frozen") { exit 0 } exit 70 } finally { if ($maintenanceMutexAcquired -and $maintenanceMutex) { try { $maintenanceMutex.ReleaseMutex() } catch {} } if ($maintenanceMutex) { $maintenanceMutex.Dispose() } }