Go语言Windows服务:如何以LocalSystem账户在用户会话运行ffmpeg?
问题:如何让LocalSystem身份的Windows服务在用户会话中以LocalSystem账户运行ffmpeg?
我用Go开发了一个以LocalSystem身份运行的Windows服务,需要调用ffmpeg捕获屏幕。目前是通过WinAPI的WTSQueryUserToken + DuplicateTokenEx获取用户令牌,在用户会话中启动ffmpeg。但在无管理员权限的会话中获取令牌时会报错:A specified logon session does not exist. It may already have been terminated.,想知道能不能让ffmpeg直接以LocalSystem账户身份在用户会话中运行,以此解决权限问题。
当前代码示例:
token, err := wintoken.GetInteractiveToken(wintoken.TokenLinked) // 使用 "github.com/fourcorelabs/wintoken" //..... a.cmd.SysProcAttr = &syscall.SysProcAttr{ Token: syscall.Token(token.Token()), CreationFlags: 0x08000000, }
可行方案:直接以LocalSystem身份在用户会话中启动ffmpeg
LocalSystem账户本身拥有跨会话创建进程的权限,不需要借用用户令牌,通过以下步骤即可实现:
获取目标用户会话ID
优先通过WTSGetActiveConsoleSessionId()获取当前活跃控制台的会话ID;如果需要指定特定用户,可通过WTSEnumerateSessions遍历所有会话筛选。修改服务令牌的会话关联
利用LocalSystem的SE_TCB_NAME权限,调用SetTokenInformation将服务自身令牌的TokenSessionId设置为目标用户会话ID。直接启动ffmpeg进程
无需传入用户令牌,调整进程创建标志后启动ffmpeg,即可让它在目标用户会话中以LocalSystem身份运行。
修改后的Go代码示例:
import ( "syscall" "unsafe" ) var ( wtsapi32 = syscall.NewLazyDLL("wtsapi32.dll") procWTSGetActiveConsoleSessionId = wtsapi32.NewProc("WTSGetActiveConsoleSessionId") advapi32 = syscall.NewLazyDLL("advapi32.dll") procSetTokenInformation = advapi32.NewProc("SetTokenInformation") ) // 获取当前活跃控制台的会话ID func getActiveSessionId() uint32 { ret, _, _ := procWTSGetActiveConsoleSessionId.Call() return uint32(ret) } // 设置令牌的会话ID func setTokenSessionId(token syscall.Token, sessionId uint32) error { sessionInfo := sessionId _, _, err := procSetTokenInformation.Call( uintptr(token), uintptr(11), // TokenSessionId参数标识 uintptr(unsafe.Pointer(&sessionInfo)), uintptr(unsafe.Sizeof(sessionInfo)), ) if err != syscall.Errno(0) { return err } return nil } // 在目标用户会话中启动ffmpeg func startFFmpeg() error { sessionId := getActiveSessionId() // 获取当前服务的LocalSystem令牌 currentToken, err := syscall.OpenCurrentProcessToken() if err != nil { return err } defer currentToken.Close() // 将令牌关联到目标用户会话 err = setTokenSessionId(currentToken, sessionId) if err != nil { return err } // 启动ffmpeg,设置窗口隐藏标志 cmd := syscall.Command("ffmpeg", "-f", "gdigrab", "-i", "desktop", "output.mp4") cmd.SysProcAttr = &syscall.SysProcAttr{ CreationFlags: 0x08000000, // CREATE_NO_WINDOW,隐藏ffmpeg窗口 } return cmd.Start() }
关键说明
- LocalSystem默认拥有修改令牌会话ID的权限,这是跨会话创建进程的核心前提,无需额外申请权限。
- 跳过用户令牌获取步骤后,彻底规避了无管理员权限会话中的令牌获取失败问题。
- 如果需要ffmpeg显示控制台窗口,可将
CreationFlags改为syscall.CREATE_NEW_CONSOLE。
内容的提问来源于stack exchange,提问作者Artem
相关产品推荐
相关产品推荐

