95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
|
|
// Copyright (c) 2026 S.A. (SNOWARE). Written for SCU Team.
|
||
|
|
// Licensed under MulanPubL-2.0.
|
||
|
|
// You may obtain a copy at: http://license.coscl.org.cn/MulanPubL-2.0
|
||
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
|
||
|
|
|
||
|
|
// Perm 模块:Windows UAC 管理员权限检测与提权。
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"os"
|
||
|
|
"syscall"
|
||
|
|
"unsafe"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Permission 权限管理器,封装管理员检测和提权操作。
|
||
|
|
type Permission struct{}
|
||
|
|
|
||
|
|
// 预加载 shell32.dll 中的 Windows API。
|
||
|
|
var (
|
||
|
|
shell32 = syscall.NewLazyDLL("shell32.dll")
|
||
|
|
isUserAnAdmin = shell32.NewProc("IsUserAnAdmin")
|
||
|
|
shellExecuteW = shell32.NewProc("ShellExecuteW")
|
||
|
|
)
|
||
|
|
|
||
|
|
// NewPermission 创建 Permission 实例。
|
||
|
|
func NewPermission() *Permission {
|
||
|
|
return &Permission{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// HasUAC 调用 IsUserAnAdmin 检查当前进程是否以管理员权限运行。
|
||
|
|
// 返回 true 表示已拥有管理员权限。
|
||
|
|
func (p *Permission) HasUAC() bool {
|
||
|
|
ret, _, _ := isUserAnAdmin.Call()
|
||
|
|
hasAdmin := ret != 0
|
||
|
|
log.Debug().Bool("has_admin", hasAdmin).Msg("检查管理员权限")
|
||
|
|
return hasAdmin
|
||
|
|
}
|
||
|
|
|
||
|
|
// runExeAsAdmin 通过 ShellExecuteW("runas") 以管理员身份启动指定的可执行文件。
|
||
|
|
//
|
||
|
|
// 参数 exe: 目标可执行文件的绝对路径。
|
||
|
|
// 成功时当前进程应退出,由新的管理员进程接管。
|
||
|
|
func (p *Permission) runExeAsAdmin(exe string) error {
|
||
|
|
log.Info().Str("exe", exe).Msg("尝试以管理员身份运行")
|
||
|
|
|
||
|
|
verb := "runas"
|
||
|
|
cwd, _ := os.Getwd()
|
||
|
|
|
||
|
|
verbPtr, _ := syscall.UTF16PtrFromString(verb)
|
||
|
|
exePtr, _ := syscall.UTF16PtrFromString(exe)
|
||
|
|
cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
|
||
|
|
|
||
|
|
var showCmd int32 = 1 // SW_NORMAL
|
||
|
|
|
||
|
|
ret, _, _ := shellExecuteW.Call(
|
||
|
|
0,
|
||
|
|
uintptr(unsafe.Pointer(verbPtr)),
|
||
|
|
uintptr(unsafe.Pointer(exePtr)),
|
||
|
|
0,
|
||
|
|
uintptr(unsafe.Pointer(cwdPtr)),
|
||
|
|
uintptr(showCmd),
|
||
|
|
)
|
||
|
|
|
||
|
|
if ret <= 32 {
|
||
|
|
err := syscall.GetLastError()
|
||
|
|
log.Error().Err(err).Int32("ret", int32(ret)).Msg("管理员提权失败")
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Info().Msg("管理员提权成功,当前进程将退出")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// getSelf 获取当前可执行文件的绝对路径。
|
||
|
|
func (p *Permission) getSelf() (string, error) {
|
||
|
|
exe, err := os.Executable()
|
||
|
|
if err != nil {
|
||
|
|
log.Error().Err(err).Msg("获取自身可执行文件路径失败")
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
log.Debug().Str("exe_path", exe).Msg("获取自身路径")
|
||
|
|
return exe, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// runSelfAsAdmin 以管理员身份重新执行自身 (srcc.exe)。
|
||
|
|
// 返回当前可执行文件路径和可能的错误。
|
||
|
|
func (p *Permission) runSelfAsAdmin() (string, error) {
|
||
|
|
exe, err := p.getSelf()
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
err = p.runExeAsAdmin(exe)
|
||
|
|
return exe, err
|
||
|
|
}
|