You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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账户本身拥有跨会话创建进程的权限,不需要借用用户令牌,通过以下步骤即可实现:

  1. 获取目标用户会话ID
    优先通过WTSGetActiveConsoleSessionId()获取当前活跃控制台的会话ID;如果需要指定特定用户,可通过WTSEnumerateSessions遍历所有会话筛选。

  2. 修改服务令牌的会话关联
    利用LocalSystem的SE_TCB_NAME权限,调用SetTokenInformation将服务自身令牌的TokenSessionId设置为目标用户会话ID。

  3. 直接启动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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:03:13