当前位置: 首页 > news >正文

go 编译的 windows 进程(exe)以管理员权限启动(UAC)

引言

windows 系统,在打开某些 exe 的时候,会弹出“用户账户控制(UAC)”的弹窗 “你要允许来自xx发布者的此应用对你的设备进行更改吗?”

UACUser Account Control,用户账户控制)是 Windows 操作系统中的一个安全组件,如果程序未通过管理员权限启动,在涉及一些敏感操作时可能会导致应用程序发生错误。

本篇简单介绍 2 种在 go 中以管理员权限启动程序的方式。

实现

这里介绍 2 种方式:

  1. manifest,使用 github.com/akavel/rsrc 库,一般都这么用。
  2. 运行时重新启动,动态提权。

一、manifest

标准的方式,通过嵌入清单文件触发 UAC 提示。

1. 创建 app.manifest 文件,内容如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false"/></requestedPrivileges></security></trustInfo>
</assembly>

2. 嵌入清单到 go 程序

  1. 安装 rsrc 工具
go get github.com/akavel/rsrc
  1. 生成资源文件
rsrc -manifest app.manifest -o app.syso# -arch 平台
# -manifest  manifest文件,可以添加管理员权限启动
# -ico  图标文件
# -o  目标文件(.syso)
# rsrc -arch amd64 -manifest app.manifest -o app.syso -ico favicon.ico

3. 编译 go 程序

直接 go build 即可,**注意:上述通过 rsrc 生成的 syso 需要放到项目路径进行 build **。

二、动态提权

适用于应用程序并不是所有的操作都需要使用管理员权限,只在用户需要的时候进行提示,然后重启应用动态提权。

直接上代码,主要函数解释:

  • isAdmin():通过尝试访问系统设备检测权限。
  • runAsAdmin():使用 ShellExecute 以管理员权限重新启动程序。
package mainimport ("fmt""os""syscall""time""github.com/google/uuid""golang.org/x/sys/windows"
)// 判断程序是否为管理员启动,不是则需要重启// 检测当前是否以管理员权限运行
func isAdmin() bool {_, err := os.Open("\\\\.\\PHYSICALDRIVE0")return err == nil
}// 以管理员权限重新启动进程,并传递事件名称
func runAsAdmin(eventName string) error {exe, _ := os.Executable()args := fmt.Sprintf(`--event-name "%s"`, eventName) // 传递事件名称verbPtr, _ := syscall.UTF16PtrFromString("runas")exePtr, _ := syscall.UTF16PtrFromString(exe)argsPtr, _ := syscall.UTF16PtrFromString(args)cwd, _ := os.Getwd()cwdPtr, _ := syscall.UTF16PtrFromString(cwd)showCmd := int32(windows.SW_NORMAL)return windows.ShellExecute(0, verbPtr, exePtr, argsPtr, cwdPtr, showCmd)
}// 父进程等待事件触发
func waitForChildReady(eventName string) bool {// 创建事件对象(初始状态为未触发)event, err := windows.CreateEvent(nil, // 默认安全属性0,   // 手动重置(false表示自动重置)0,   // 初始状态未触发windows.StringToUTF16Ptr(eventName),)if err != nil {fmt.Println("CreateEvent error:", err)return false}defer windows.CloseHandle(event)// 等待事件触发(最多10秒)const timeout = 10 * time.Secondresult, err := windows.WaitForSingleObject(event, uint32(timeout.Milliseconds()))if err != nil {fmt.Println("WaitForSingleObject error:", err)return false}return result == windows.WAIT_OBJECT_0
}// 子进程触发事件
func signalParent(eventName string) {// 打开事件对象(需要EVENT_MODIFY_STATE权限)event, err := windows.OpenEvent(windows.EVENT_MODIFY_STATE,false,windows.StringToUTF16Ptr(eventName),)if err != nil {fmt.Println("OpenEvent error:", err)os.Exit(1)}defer windows.CloseHandle(event)// 触发事件if err := windows.SetEvent(event); err != nil {fmt.Println("SetEvent error:", err)os.Exit(1)}
}func main() {// 解析命令行参数中的事件名称var eventName stringfor i, arg := range os.Args {if arg == "--event-name" && i+1 < len(os.Args) {eventName = os.Args[i+1]break}}if isAdmin() {// 管理员模式下,触发事件并执行业务逻辑if eventName != "" {signalParent(eventName)}fmt.Println("以管理员模式启动!")// TODO: 主程序逻辑fmt.Println("按任意键退出程序...")// 直接从标准输入读取一个字节,用于检测按键操作buf := make([]byte, 1)os.Stdin.Read(buf)} else {// 非管理员模式,生成唯一事件名称并重启eventName := uuid.New().String()if err := runAsAdmin(eventName); err != nil {fmt.Println("Failed to restart as admin:", err)os.Exit(1)}// 等待子进程就绪if waitForChildReady(eventName) {fmt.Println("Child process started successfully.")os.Exit(0)} else {fmt.Println("Failed to start child process.")os.Exit(1)}}
}
http://www.xdnf.cn/news/84907.html

相关文章:

  • Spark-Streaming简介及核心编程
  • 详解Windows(六)——文件系统
  • 电脑安装adb并且连接华为手机mate60pro后查看设备
  • 服务器操作系统时间同步失败的原因及修复
  • Windows:异常安全的内核对象
  • 如何使用压缩文件便捷地管理远程工作文件?
  • 子网划分的学习
  • 深入探索RAG:用LlamaIndex为大语言模型扩展知识,实现智能检索增强生成
  • Linux:线程基础(虚拟地址,分页)
  • 实现鼠标拖拽图片效果
  • 驱动开发硬核特训 · Day 17:深入掌握中断机制与驱动开发中的应用实战
  • 或者某些 M 理论、Loop Quantum Gravity 的空背景设想
  • 【Java面试笔记:基础】8.对比Vector、ArrayList、LinkedList有何区别?
  • L2-1、打造稳定可控的 AI 输出 —— Prompt 模板与格式控制
  • 局域网内,将linux(Ubuntu)的硬盘映射成Windows上,像本地磁盘一样使用
  • Lua 第8部分 补充知识
  • ProxySQL 读写分离规则配置指南
  • exception:com.alibaba.nacos.api.exception.NacosException: user not found! 解决方法
  • 解决Python与Java交互乱码问题:从编码角度优化数据流
  • 云原生 - Service Mesh
  • 【Linux运维涉及的基础命令与排查方法大全】
  • 位运算练习:起床困难综合征(贪心,位运算)(算法竞赛进阶指南学习笔记)
  • 2025-04-22| Docker: --privileged参数详解
  • 【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
  • 黑阈免激活版:智能管理后台,优化手机性能
  • vscode flutter 插件, vscode运行安卓项目,.gradle 路径配置
  • 刷刷刷刷刷sql题
  • Oracle在ERP市场击败SAP
  • JVM考古现场(二十四):逆熵者·时间晶体的永恒之战
  • PHP通讯录网站源码无需sql数据库