Files
AH-Oil-Trader/tools/autologon_einrichten.ps1
Axel HocksandClaude Opus 5 41e527c492 Auto-Logon aktiv — Registry-Weg, sicherer Weg blieb wirkungslos
Endstand verifiziert: AutoAdminLogon=1, DefaultUserName=ah,
DefaultDomainName=HOMEBASE, Aufgabe OilAutoLogonLock Bereit und auf
autologon_lock.ps1 zeigend. Die Boot-Luecke ist damit konfiguratorisch
geschlossen.

⚠ Der SICHERE Weg hat nicht funktioniert: Sysinternals Autologon beendete sich
mit ExitCode 0, schrieb aber nichts — weder ueber den Dialog noch ueber die
Argument-Form. Ausgeschlossen: DevicePasswordLessBuildVersion (nicht gesetzt),
fehlende Elevation, haengender Prozess. Ursache ungeklaert. Aktiv ist deshalb
der Registry-Weg, und damit steht das Passwort im KLARTEXT unter
HKLM\...\Winlogon\DefaultPassword — lesbar fuer jeden lokalen Admin und jedes
Backup-Image. Entfernen-Kommando in CLAUDE.md.

⚠ Dritter Fall derselben Fehlerklasse in diesem Skript, jetzt behoben: die
Speicherart wurde aus einer stale Variable gelesen, die VOR dem
Registry-Schreiben gefuellt war. Die Ausgabe widersprach sich direkt — erst
korrekt 'steht jetzt im KLARTEXT', zwei Zeilen spaeter 'nicht im Klartext '.
Der Zustand wird jetzt nach jedem Schreibvorgang frisch gelesen.

Lehre aus allen drei Faellen: eine Erfolgsmeldung muss den Zustand NACH der
Aktion pruefen, nicht den davor.

Offen: die Wirkung ist erst beim naechsten Reboot belegt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 06:58:02 +02:00

258 lines
13 KiB
PowerShell

# Auto-Logon einrichten — schliesst die letzte Luecke der Boot-Pipeline.
#
# WOZU: `OilTradingServer` und `HyperliquidDashboard` haengen an einem LOGON-
# Trigger. Nach einem Windows-Update-Reboot laeuft nichts, bis sich jemand
# anmeldet — real in der Nacht zum 2026-07-20 passiert (Reboot 01:30, Server bis
# 07:42 tot, nur der broker-seitige SL schuetzte, -95,48 EUR). Ein Task ohne
# angemeldete Sitzung hilft NICHT: der Bot braucht das laufende MT5-TERMINAL,
# und das ist eine GUI-Anwendung — es braucht eine echte Desktop-Sitzung.
#
# ⚠⚠ WARUM SYSINTERNALS UND NICHT DIE REGISTRY. Der verbreitete Weg schreibt
# das Passwort als `DefaultPassword` im KLARTEXT nach HKLM\...\Winlogon — dort
# kann es jeder lokale Administrator und jedes Backup-Image lesen. Sysinternals
# `Autologon` legt es als verschluesseltes LSA-Secret ab.
#
# ⚠ RESTRISIKO, das bleibt: nach dem Reboot steht eine ANGEMELDETE Sitzung mit
# offenem MT5 und Live-Broker-Verbindung da. Schritt 2 sperrt deshalb den
# Bildschirm — aber nur nach einem Auto-Logon, nicht nach jeder Anmeldung
# (s. `autologon_lock.ps1`).
#
# AUFRUF (Fenster muss ALS ADMINISTRATOR laufen):
# powershell -ExecutionPolicy Bypass -File tools\autologon_einrichten.ps1
#
# ⚠ Das Passwort steht NICHT in dieser Datei und wird nirgends gespeichert —
# es wird im GUI-Dialog von Autologon eingegeben.
#
# ⚠⚠ ZWEI FEHLER AUS DEM ERSTEN VERSUCH (2026-08-07), hier behoben:
# 1. `$ErrorActionPreference = 'Stop'` PLUS `schtasks /Delete` auf eine noch
# nicht existierende Aufgabe = das Skript brach ab, BEVOR /Create lief.
# Native Programme schreiben nach stderr, und PowerShell macht daraus unter
# 'Stop' einen Abbruch. Jetzt wird vorher geprueft, ob es sie ueberhaupt gibt.
# 2. Die Erfolgsmeldung log: fehlendes `DefaultPassword` wurde als
# "LSA-Secret ✅" gemeldet — dabei war schlicht GAR NICHTS gesetzt. Jetzt
# ist `AutoAdminLogon = 1` die Bedingung, und erst danach wird ueberhaupt
# ueber die Speicherart geurteilt.
# AUFRUF-VARIANTEN:
# ... -File tools\autologon_einrichten.ps1 GUI-Dialog (Standard)
# ... -File tools\autologon_einrichten.ps1 -Direkt ohne Dialog, Passwort
# wird hier abgefragt
# ⚠ `-Direkt` ist der zuverlaessige Weg. Der Dialog OEFFNET sich zwar
# (Fenstertitel "Autologon - Sysinternals", verifiziert), muss aber mit ENABLE
# bestaetigt werden — wird er nur geschlossen, passiert nichts.
# ⚠ Abwaegung bei `-Direkt`: Autologon 3.10 nimmt Benutzer/Domaene/Passwort als
# Argumente. Das Passwort steht damit fuer den Moment des Aufrufs in der
# Kommandozeile des Kindprozesses (fuer Administratoren kurz sichtbar). Es wird
# NICHT in eine Datei geschrieben, nicht in die PowerShell-History (Read-Host)
# und landet anschliessend als verschluesseltes LSA-Secret — nicht im Klartext
# in der Registry.
param([switch]$Direkt)
$ErrorActionPreference = 'Stop'
$hier = Split-Path -Parent $MyInvocation.MyCommand.Path
$k = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
$t = 'OilAutoLogonLock'
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
if (-not (New-Object Security.Principal.WindowsPrincipal($id)).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host " ABBRUCH: Dieses Fenster hat keine Administratorrechte." -ForegroundColor Red
exit 1
}
$exe = Join-Path $hier 'autologon\Autologon64.exe'
if (-not (Test-Path $exe)) { $exe = Join-Path $hier 'autologon\Autologon.exe' }
if (-not (Test-Path $exe)) {
Write-Host " ABBRUCH: Autologon.exe fehlt unter tools\autologon\." -ForegroundColor Red
exit 1
}
Write-Host "=================================================================="
Write-Host " SCHRITT 1 - Auto-Logon aktivieren (verschluesseltes LSA-Secret)"
Write-Host "=================================================================="
Write-Host " Benutzer: $env:USERNAME Domaene: $env:COMPUTERNAME (lokal)"
# Aktivierung ohne Dialog — als Funktion, damit sie auch als Rueckfall nach
# einem abgebrochenen Dialog aufgerufen werden kann (sonst muesste der Nutzer
# das Skript ein drittes Mal starten).
function Invoke-DirektAktivierung {
# ⚠ Read-Host -AsSecureString: die Eingabe erscheint nicht auf dem Schirm
# und landet nicht in der PowerShell-History.
$sec = Read-Host " Passwort fuer $env:USERNAME (Eingabe bleibt unsichtbar)" -AsSecureString
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
try {
$pw = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
if (-not $pw) {
Write-Host " Kein Passwort eingegeben." -ForegroundColor Red
return $false
}
Write-Host " Aktiviere Auto-Logon ..."
# ⚠⚠ ZWEI FEHLER AUS DEM VORIGEN VERSUCH, hier behoben:
# (a) `& $exe ...` WARTET BEI GUI-ANWENDUNGEN NICHT — das Skript las die
# Registry, bevor Autologon ueberhaupt fertig war.
# (b) `$null = ... 2>&1` hat die Rueckmeldung VERSCHLUCKT. Genau die
# Information, die zur Diagnose noetig war, wurde weggeworfen.
# Jetzt: warten, Exit-Code auswerten, Ausgabe zeigen.
$log = Join-Path $env:TEMP 'autologon_out.txt'
$p = Start-Process -FilePath $exe `
-ArgumentList $env:USERNAME, $env:COMPUTERNAME, $pw `
-Wait -PassThru -RedirectStandardOutput $log `
-RedirectStandardError "$log.err" -WindowStyle Hidden
Write-Host " Autologon ExitCode: $($p.ExitCode)"
foreach ($f in @($log, "$log.err")) {
if ((Test-Path $f) -and (Get-Item $f).Length -gt 0) {
Write-Host " Ausgabe: $((Get-Content $f -Raw).Trim())"
}
Remove-Item $f -EA SilentlyContinue
}
return $true
} finally {
# Klartext so kurz wie moeglich im Speicher halten
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
Remove-Variable pw -ErrorAction SilentlyContinue
}
}
Write-Host ""
if ($Direkt) {
if (-not (Invoke-DirektAktivierung)) { exit 1 }
} else {
Write-Host " Es oeffnet sich gleich der Autologon-Dialog." -ForegroundColor Cyan
Write-Host " --> Passwort eintragen und auf ENABLE klicken." -ForegroundColor Cyan
Write-Host " (Nur schliessen reicht NICHT - dann passiert nichts."
Write-Host " Zuverlaessiger: Skript mit -Direkt aufrufen.)"
Write-Host ""
Start-Process -FilePath $exe -ArgumentList '/accepteula' -Wait
}
# ── Ergebnis ehrlich pruefen ────────────────────────────────────────────
$an = (Get-ItemProperty -Path $k -Name AutoAdminLogon -EA SilentlyContinue).AutoAdminLogon
$user = (Get-ItemProperty -Path $k -Name DefaultUserName -EA SilentlyContinue).DefaultUserName
$klartext = (Get-ItemProperty -Path $k -Name DefaultPassword -EA SilentlyContinue).DefaultPassword
$aktiv = ($an -eq '1')
Write-Host ""
Write-Host " AutoAdminLogon : $(if ($an) { $an } else { '(nicht gesetzt)' })"
Write-Host " DefaultUserName : $(if ($user) { $user } else { '(nicht gesetzt)' })"
# ⚠ RUECKFALL statt Sackgasse: schlaegt der Dialog-Weg fehl, wird hier direkt
# angeboten, es ohne Dialog zu erledigen — sonst muesste das Skript ein drittes
# Mal von Hand gestartet werden (real zweimal passiert).
if (-not $aktiv -and -not $Direkt) {
Write-Host ""
Write-Host " ==> Der Dialog wurde ohne 'Enable' geschlossen." -ForegroundColor Yellow
$a = Read-Host " Jetzt ohne Dialog erledigen? (j/n)"
if ($a -match '^[jJyY]') {
if (Invoke-DirektAktivierung) {
$an = (Get-ItemProperty -Path $k -Name AutoAdminLogon -EA SilentlyContinue).AutoAdminLogon
$user = (Get-ItemProperty -Path $k -Name DefaultUserName -EA SilentlyContinue).DefaultUserName
$klartext = (Get-ItemProperty -Path $k -Name DefaultPassword -EA SilentlyContinue).DefaultPassword
$aktiv = ($an -eq '1')
Write-Host ""
Write-Host " AutoAdminLogon : $(if ($an) { $an } else { '(nicht gesetzt)' })"
Write-Host " DefaultUserName : $(if ($user) { $user } else { '(nicht gesetzt)' })"
}
}
}
# ── Letzter Ausweg: Registry-Weg ────────────────────────────────────────
# ⚠⚠ BEWUSST ALS LETZTE OPTION und nur nach ausdruecklicher Zustimmung. Hier
# landet das Passwort im KLARTEXT unter HKLM\...\Winlogon\DefaultPassword —
# lesbar fuer jeden lokalen Administrator und in jedem Backup-Image. Der einzige
# Vorteil: er funktioniert immer, ohne Fremdprogramm. Wenn Autologon (der
# sichere Weg) partout nicht greift, ist das die Alternative — die Entscheidung
# gehoert dem Nutzer, nicht dem Skript.
if (-not $aktiv) {
Write-Host ""
Write-Host " ==> AUTO-LOGON IST NICHT AKTIV." -ForegroundColor Yellow
Write-Host " Sysinternals Autologon hat nichts geschrieben."
Write-Host ""
Write-Host " ALTERNATIVE: der Registry-Weg." -ForegroundColor Cyan
Write-Host " ⚠ Dabei steht das Passwort im KLARTEXT in der Registry" -ForegroundColor Yellow
Write-Host " (HKLM\...\Winlogon\DefaultPassword). Jeder lokale Admin und"
Write-Host " jedes Backup-Image kann es lesen. Er funktioniert dafuer immer."
$a = Read-Host " Registry-Weg jetzt verwenden? (j/n)"
if ($a -notmatch '^[jJyY]') {
Write-Host " Abgebrochen. Schritt 2 uebersprungen."
exit 1
}
$sec = Read-Host " Passwort fuer $env:USERNAME (Eingabe bleibt unsichtbar)" -AsSecureString
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
try {
$pw2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
if (-not $pw2) { Write-Host " Kein Passwort." -ForegroundColor Red; exit 1 }
Set-ItemProperty -Path $k -Name AutoAdminLogon -Value '1' -Type String
Set-ItemProperty -Path $k -Name DefaultUserName -Value $env:USERNAME -Type String
Set-ItemProperty -Path $k -Name DefaultDomainName -Value $env:COMPUTERNAME -Type String
Set-ItemProperty -Path $k -Name DefaultPassword -Value $pw2 -Type String
} finally {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
Remove-Variable pw2 -EA SilentlyContinue
}
$an = (Get-ItemProperty -Path $k -Name AutoAdminLogon -EA SilentlyContinue).AutoAdminLogon
$aktiv = ($an -eq '1')
Write-Host ""
Write-Host " AutoAdminLogon : $an"
}
if (-not $aktiv) {
Write-Host " ==> Auch das hat nicht gegriffen. Schritt 2 uebersprungen." -ForegroundColor Red
exit 1
}
# ⚠⚠ NEU EINLESEN, NICHT den alten Wert wiederverwenden. Beim ersten Bau stand
# hier eine stale Variable aus der Zeit VOR dem Registry-Schreiben — die Ausgabe
# widersprach sich dann direkt: erst korrekt "steht jetzt im KLARTEXT", zwei
# Zeilen spaeter "nicht im Klartext (LSA-Secret) ✅". DRITTER Fall derselben
# Fehlerklasse in diesem Skript (Erfolg melden, wo keiner ist bzw. wo das
# Gegenteil zutrifft). Der Zustand wird deshalb immer frisch gelesen.
$klartext = (Get-ItemProperty -Path $k -Name DefaultPassword -EA SilentlyContinue).DefaultPassword
Write-Host ""
if ($klartext) {
Write-Host " SPEICHERART: ⚠ Passwort im KLARTEXT in der Registry." -ForegroundColor Yellow
Write-Host " Entfernen (deaktiviert damit auch den Auto-Logon):"
Write-Host " Remove-ItemProperty -Path '$k' -Name DefaultPassword"
} else {
Write-Host " SPEICHERART: verschluesseltes LSA-Secret ✅" -ForegroundColor Green
}
if ($klartext) {
Write-Host " DefaultPassword : ⚠ KLARTEXT in der Registry!" -ForegroundColor Yellow
} else {
Write-Host " DefaultPassword : nicht im Klartext (LSA-Secret) ✅" -ForegroundColor Green
}
# ── Schritt 2: Bildschirmsperre nach dem Auto-Logon ─────────────────────
Write-Host ""
Write-Host "=================================================================="
Write-Host " SCHRITT 2 - Bildschirm nach dem Auto-Logon sperren"
Write-Host "=================================================================="
# ⚠ Erst pruefen, dann loeschen. `schtasks /Delete` auf eine fehlende Aufgabe
# schreibt nach stderr und wuerde unter $ErrorActionPreference='Stop' abbrechen.
$vorhanden = $false
try {
$null = & schtasks /Query /TN $t 2>&1
$vorhanden = ($LASTEXITCODE -eq 0)
} catch { $vorhanden = $false }
if ($vorhanden) {
$null = & schtasks /Delete /TN $t /F 2>&1
Write-Host " alte Aufgabe entfernt"
}
$lock = Join-Path $hier 'autologon_lock.ps1'
$cmd = "powershell -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$lock`""
$null = & schtasks /Create /TN $t /SC ONLOGON /RU "$env:USERNAME" /DELAY 0000:30 /TR $cmd /F 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host " Aufgabe '$t' angelegt." -ForegroundColor Green
Write-Host " Sperrt 30 s nach der Anmeldung - aber NUR, wenn der Rechner"
Write-Host " gerade gebootet hat. Nach manueller Anmeldung passiert nichts."
Write-Host " Die Sitzung laeuft weiter: MT5 und beide Server arbeiten."
} else {
Write-Host " ⚠ Aufgabe konnte nicht angelegt werden (Code $LASTEXITCODE)." -ForegroundColor Yellow
}
Write-Host ""
Write-Host " Rueckgaengig:"
Write-Host " $exe /accepteula (dann Disable)"
Write-Host " schtasks /Delete /TN $t /F"