fix: admin-version in second window on windoof (incl build)

This commit is contained in:
2026-09-27 16:25:25 +02:00
parent 88ce254037
commit 0ee10a64c1
4 changed files with 98 additions and 33 deletions
+93 -28
View File
@@ -6,19 +6,32 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"unsafe"
)
var (
shell32 = syscall.NewLazyDLL("shell32.dll")
shellExecuteExW = shell32.NewProc("ShellExecuteExW")
shell32 = syscall.NewLazyDLL("shell32.dll")
shellExecuteExW = shell32.NewProc("ShellExecuteExW")
advapi32 = syscall.NewLazyDLL("advapi32.dll")
openProcessToken = advapi32.NewProc("OpenProcessToken")
getTokenInformation = advapi32.NewProc("GetTokenInformation")
kernel32 = syscall.NewLazyDLL("kernel32.dll")
closeHandle = kernel32.NewProc("CloseHandle")
waitForSingleObject = kernel32.NewProc("WaitForSingleObject")
)
const (
seeMaskNocloseprocess = 0x00000040
tokenQuery = 0x0008
tokenElevationClass = 20
seeMaskNoCloseProcess = 0x00000040
swShow = 1
infinite = 0xFFFFFFFF
errorCancelled = 1223
)
type shellExecuteInfo struct {
@@ -39,6 +52,10 @@ type shellExecuteInfo struct {
hProcess uintptr
}
type tokenElevationData struct {
TokenIsElevated uint32
}
func getInstallDir() (string, error) {
programFiles := os.Getenv("ProgramFiles")
@@ -50,32 +67,70 @@ func getInstallDir() (string, error) {
}
func platformRunPrivileged(config Config, action string) error {
if isWindowsAdmin() {
if isWindowsElevated() {
return runAction(config, action)
}
fmt.Println()
fmt.Println("Für diese Aktion werden Administratorrechte benötigt.")
fmt.Print("Jetzt Administratorrechte anfordern? [J/n]: ")
var answer string
if _, err := fmt.Scanln(&answer); err != nil {
// Enter ohne Eingabe = Ja
answer = ""
}
answer = strings.ToLower(strings.TrimSpace(answer))
if answer != "" && answer != "j" && answer != "ja" {
fmt.Println("Abgebrochen.")
return nil
}
return runElevated(action)
}
func isWindowsAdmin() bool {
// Ein einfacher und robuster Test:
// Versuch, in das Windows-Systemverzeichnis zu schreiben.
//
// Da wir dort natürlich nichts verändern wollen, verwenden wir
// stattdessen "net session". Der Befehl funktioniert nur mit
// Administratorrechten.
func isWindowsElevated() bool {
var token uintptr
cmd := exec.Command("net", "session")
cmd.Stdout = nil
cmd.Stderr = nil
ret, _, _ := openProcessToken.Call(
uintptr(^uint(0)>>1),
tokenQuery,
uintptr(unsafe.Pointer(&token)),
)
return cmd.Run() == nil
if ret == 0 || token == 0 {
return false
}
defer closeHandle.Call(token)
var elevation tokenElevationData
var returned uint32
ret, _, _ = getTokenInformation.Call(
token,
tokenElevationClass,
uintptr(unsafe.Pointer(&elevation)),
uintptr(unsafe.Sizeof(elevation)),
uintptr(unsafe.Pointer(&returned)),
)
if ret == 0 {
return false
}
return elevation.TokenIsElevated != 0
}
func runElevated(action string) error {
exe, err := os.Executable()
if err != nil {
return fmt.Errorf("Pfad des Installers konnte nicht ermittelt werden: %w", err)
return fmt.Errorf(
"Pfad des Installers konnte nicht ermittelt werden: %w",
err,
)
}
verb, err := syscall.UTF16PtrFromString("runas")
@@ -95,34 +150,44 @@ func runElevated(action string) error {
info := shellExecuteInfo{
cbSize: uint32(unsafe.Sizeof(shellExecuteInfo{})),
fMask: seeMaskNocloseprocess,
fMask: seeMaskNoCloseProcess,
lpVerb: verb,
lpFile: file,
lpParameters: parameters,
nShow: 1, // SW_SHOWNORMAL
nShow: swShow,
}
ret, _, callErr := shellExecuteExW.Call(uintptr(unsafe.Pointer(&info)))
ret, _, callErr := shellExecuteExW.Call(
uintptr(unsafe.Pointer(&info)),
)
if ret == 0 {
// ERROR_CANCELLED = 1223:
// Benutzer hat den UAC-Dialog abgebrochen.
if errno, ok := callErr.(syscall.Errno); ok && errno == 1223 {
if errno, ok := callErr.(syscall.Errno); ok &&
errno == errorCancelled {
fmt.Println("Administratorrechte wurden nicht erteilt.")
return nil
}
return fmt.Errorf("Administratorrechte konnten nicht angefordert werden: %w", callErr)
return fmt.Errorf(
"Administratorrechte konnten nicht angefordert werden: %w",
callErr,
)
}
// Auf die erhöhte Instanz warten.
if info.hProcess != 0 {
handle := syscall.Handle(info.hProcess)
defer syscall.CloseHandle(handle)
_, err := syscall.WaitForSingleObject(handle, syscall.INFINITE)
if err != nil {
return fmt.Errorf("auf erhöhte Instanz konnte nicht gewartet werden: %w", err)
ret, _, err := waitForSingleObject.Call(
uintptr(handle),
infinite,
)
if ret == 0xFFFFFFFF {
return fmt.Errorf(
"auf erhöhte Instanz konnte nicht gewartet werden: %w",
err,
)
}
}