187 lines
7.0 KiB
PowerShell
187 lines
7.0 KiB
PowerShell
# GOTT Remote Agent - Windows PowerShell Client
|
|
# Verbindet sich per WebSocket zum GOTT-Server und fuehrt Befehle aus.
|
|
#
|
|
# Verwendung:
|
|
# irm https://gott.example.com/remote.ps1 | iex
|
|
# oder:
|
|
# .\remote.ps1 -Url "wss://gott.example.com/api/remote"
|
|
|
|
param(
|
|
[string]$Url = "{{WS_URL}}"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
Write-Host ""
|
|
Write-Host " ============================" -ForegroundColor Cyan
|
|
Write-Host " GOTT Remote Agent" -ForegroundColor Cyan
|
|
Write-Host " ============================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host " Verbinde zu: $Url" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
function Send-WsMessage {
|
|
param([System.Net.WebSockets.ClientWebSocket]$ws, [string]$msg, [System.Threading.CancellationToken]$ct)
|
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes($msg)
|
|
$ws.SendAsync((New-Object System.ArraySegment[byte] -ArgumentList @(,$bytes)),
|
|
[System.Net.WebSockets.WebSocketMessageType]::Text, $true, $ct).GetAwaiter().GetResult() | Out-Null
|
|
}
|
|
|
|
function Receive-WsMessage {
|
|
param([System.Net.WebSockets.ClientWebSocket]$ws, [System.Threading.CancellationToken]$ct)
|
|
$buf = New-Object byte[] 65536
|
|
$ms = New-Object System.IO.MemoryStream
|
|
try {
|
|
do {
|
|
$seg = New-Object System.ArraySegment[byte] -ArgumentList @(,$buf)
|
|
$result = $ws.ReceiveAsync($seg, $ct).GetAwaiter().GetResult()
|
|
if ($result.MessageType -eq [System.Net.WebSockets.WebSocketMessageType]::Close) {
|
|
return $null
|
|
}
|
|
$ms.Write($buf, 0, $result.Count)
|
|
} while (-not $result.EndOfMessage)
|
|
return [System.Text.Encoding]::UTF8.GetString($ms.ToArray())
|
|
} finally {
|
|
$ms.Dispose()
|
|
}
|
|
}
|
|
|
|
try {
|
|
$ws = New-Object System.Net.WebSockets.ClientWebSocket
|
|
$cts = New-Object System.Threading.CancellationTokenSource
|
|
$ct = $cts.Token
|
|
|
|
# Ctrl+C abfangen → CancellationToken ausloesen
|
|
[Console]::TreatControlCAsInput = $false
|
|
$cancelHandler = [System.ConsoleCancelEventHandler]{
|
|
param($s, $e)
|
|
$e.Cancel = $true
|
|
$cts.Cancel()
|
|
}
|
|
[Console]::add_CancelKeyPress($cancelHandler)
|
|
|
|
# TLS 1.2 erzwingen (fuer aeltere Windows-Versionen)
|
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
|
|
|
|
# Verbindung aufbauen
|
|
try {
|
|
$ws.ConnectAsync([Uri]$Url, $ct).GetAwaiter().GetResult() | Out-Null
|
|
} catch {
|
|
Write-Host " [FEHLER] Verbindung fehlgeschlagen: $_" -ForegroundColor Red
|
|
Write-Host " URL: $Url" -ForegroundColor Gray
|
|
return
|
|
}
|
|
Write-Host " [OK] Verbunden!" -ForegroundColor Green
|
|
|
|
# Hello-Message senden
|
|
$hostname = $env:COMPUTERNAME
|
|
$username = $env:USERNAME
|
|
$osVersion = [System.Environment]::OSVersion.VersionString
|
|
$hello = @{
|
|
type = "hello"
|
|
hostname = $hostname
|
|
username = $username
|
|
os = $osVersion
|
|
} | ConvertTo-Json -Compress
|
|
Send-WsMessage $ws $hello $ct
|
|
|
|
Write-Host " Host: $username@$hostname ($osVersion)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " Warte auf Befehle... (Strg+C zum Beenden)" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
|
|
# Synchroner Empfangs-Loop
|
|
while ($ws.State -eq [System.Net.WebSockets.WebSocketState]::Open) {
|
|
# Blockierend auf naechste Nachricht warten
|
|
$json = Receive-WsMessage $ws $ct
|
|
if ($null -eq $json) {
|
|
Write-Host " Server hat Verbindung geschlossen." -ForegroundColor Yellow
|
|
break
|
|
}
|
|
|
|
try {
|
|
$msg = $json | ConvertFrom-Json
|
|
} catch {
|
|
continue
|
|
}
|
|
|
|
switch ($msg.type) {
|
|
"execute" {
|
|
$command = $msg.command
|
|
$timestamp = Get-Date -Format "HH:mm:ss"
|
|
Write-Host " [$timestamp] Ausfuehren: " -NoNewline -ForegroundColor Cyan
|
|
Write-Host $command -ForegroundColor White
|
|
|
|
$exitCode = 0
|
|
$stdout = ""
|
|
$stderr = ""
|
|
|
|
try {
|
|
# EncodedCommand umgeht alle Quoting-Probleme (Anführungszeichen, Sonderzeichen)
|
|
$encodedCmd = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
|
|
$output = & powershell.exe -NoProfile -NonInteractive -EncodedCommand $encodedCmd 2>&1
|
|
$stdoutLines = @($output | Where-Object { $_ -isnot [System.Management.Automation.ErrorRecord] })
|
|
$stderrLines = @($output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }) |
|
|
ForEach-Object { $_.ToString() }
|
|
$stdout = ($stdoutLines | Out-String).TrimEnd()
|
|
$stderr = ($stderrLines | Out-String).TrimEnd()
|
|
$exitCode = $LASTEXITCODE
|
|
if ($null -eq $exitCode) { $exitCode = 0 }
|
|
} catch {
|
|
$stderr = $_.Exception.Message
|
|
$exitCode = 1
|
|
}
|
|
|
|
# Ergebnis senden
|
|
$response = @{
|
|
type = "result"
|
|
exitCode = $exitCode
|
|
output = if ($stdout) { $stdout } else { "" }
|
|
error = if ($stderr) { $stderr } else { "" }
|
|
} | ConvertTo-Json -Compress -Depth 3
|
|
|
|
Send-WsMessage $ws $response $ct
|
|
|
|
# Kurzausgabe lokal
|
|
if ($stdout) {
|
|
$preview = if ($stdout.Length -gt 200) { $stdout.Substring(0, 200) + "..." } else { $stdout }
|
|
Write-Host " -> $preview" -ForegroundColor Gray
|
|
}
|
|
if ($stderr) {
|
|
Write-Host " [stderr] $stderr" -ForegroundColor Red
|
|
}
|
|
Write-Host " [Exit: $exitCode]" -ForegroundColor $(if ($exitCode -eq 0) { "Green" } else { "Red" })
|
|
Write-Host ""
|
|
}
|
|
"pong" {
|
|
# Heartbeat-Antwort - ignorieren
|
|
}
|
|
default {
|
|
Write-Host " Unbekannte Nachricht: $($msg.type)" -ForegroundColor Yellow
|
|
}
|
|
}
|
|
}
|
|
} catch [System.OperationCanceledException] {
|
|
# Ctrl+C
|
|
} catch [System.AggregateException] {
|
|
# Kann durch CancellationToken in .GetAwaiter().GetResult() kommen
|
|
} catch [System.Net.WebSockets.WebSocketException] {
|
|
Write-Host " Verbindung verloren." -ForegroundColor Red
|
|
} catch {
|
|
Write-Host " Fehler: $_" -ForegroundColor Red
|
|
} finally {
|
|
# Event-Handler entfernen
|
|
try { [Console]::remove_CancelKeyPress($cancelHandler) } catch { }
|
|
|
|
if ($ws -and $ws.State -eq [System.Net.WebSockets.WebSocketState]::Open) {
|
|
try {
|
|
$closeCt = (New-Object System.Threading.CancellationTokenSource(3000)).Token
|
|
$ws.CloseAsync([System.Net.WebSockets.WebSocketCloseStatus]::NormalClosure, "bye", $closeCt).GetAwaiter().GetResult() | Out-Null
|
|
} catch { }
|
|
}
|
|
if ($cts) { $cts.Dispose() }
|
|
if ($ws) { $ws.Dispose() }
|
|
Write-Host ""
|
|
Write-Host " Verbindung beendet." -ForegroundColor Yellow
|
|
}
|