Golang在Windows中以系统用户身份运行进程的方法

Golang在Windows中以系统用户身份运行进程的方法 如何在Windows中以系统用户身份运行进程,可能使用WinAPI?本质上,我想实现与执行以下命令相同的结果:

psexec -i -h -d -s ffmpeg -f gdigrab -framerate 5 -i desktop -c:v libx264 -preset veryfast -bf 2 -g 150 out.mp4
1 回复

更多关于Golang在Windows中以系统用户身份运行进程的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Windows中以系统用户身份运行进程,可以通过调用Windows API实现。以下是使用golang.org/x/sys/windows包和CreateProcessWithTokenW函数的示例代码:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "golang.org/x/sys/windows"
)

var (
    advapi32 = windows.NewLazySystemDLL("advapi32.dll")
    kernel32 = windows.NewLazySystemDLL("kernel32.dll")

    procCreateProcessWithTokenW = advapi32.NewProc("CreateProcessWithTokenW")
    procOpenProcessToken        = advapi32.NewProc("OpenProcessToken")
    procDuplicateTokenEx        = advapi32.NewProc("DuplicateTokenEx")
    procCloseHandle             = kernel32.NewProc("CloseHandle")
)

func runAsSystem(cmd string, args []string) error {
    // 获取系统进程的句柄(PID 4通常是System进程)
    const SYSTEM_PID = 4
    hProcess, err := windows.OpenProcess(
        windows.PROCESS_QUERY_INFORMATION,
        false,
        SYSTEM_PID,
    )
    if err != nil {
        return fmt.Errorf("OpenProcess failed: %v", err)
    }
    defer windows.CloseHandle(hProcess)

    // 打开进程令牌
    var hToken windows.Token
    ret, _, err := procOpenProcessToken.Call(
        uintptr(hProcess),
        uintptr(windows.TOKEN_DUPLICATE|windows.TOKEN_QUERY),
        uintptr(unsafe.Pointer(&hToken)),
    )
    if ret == 0 {
        return fmt.Errorf("OpenProcessToken failed: %v", err)
    }
    defer windows.CloseHandle(windows.Handle(hToken))

    // 复制令牌
    var hDuplicatedToken windows.Token
    ret, _, err = procDuplicateTokenEx.Call(
        uintptr(hToken),
        uintptr(windows.MAXIMUM_ALLOWED),
        uintptr(0),
        uintptr(windows.SecurityImpersonation),
        uintptr(windows.TokenPrimary),
        uintptr(unsafe.Pointer(&hDuplicatedToken)),
    )
    if ret == 0 {
        return fmt.Errorf("DuplicateTokenEx failed: %v", err)
    }
    defer windows.CloseHandle(windows.Handle(hDuplicatedToken))

    // 准备命令行
    cmdLine := syscall.StringToUTF16Ptr(cmd)
    for _, arg := range args {
        cmdLine = syscall.StringToUTF16Ptr(fmt.Sprintf("%s %s", syscall.UTF16ToString(cmdLine), arg))
    }

    // 创建进程启动信息
    var si windows.StartupInfo
    var pi windows.ProcessInformation
    si.Cb = uint32(unsafe.Sizeof(si))

    // 使用复制的令牌创建进程
    ret, _, err = procCreateProcessWithTokenW.Call(
        uintptr(hDuplicatedToken),
        0,
        0,
        uintptr(unsafe.Pointer(cmdLine)),
        0,
        0,
        0,
        uintptr(unsafe.Pointer(&si)),
        uintptr(unsafe.Pointer(&pi)),
    )
    if ret == 0 {
        return fmt.Errorf("CreateProcessWithTokenW failed: %v", err)
    }

    // 关闭句柄
    windows.CloseHandle(pi.Thread)
    windows.CloseHandle(pi.Process)

    return nil
}

func main() {
    // 示例:运行ffmpeg命令
    cmd := "ffmpeg"
    args := []string{
        "-f", "gdigrab",
        "-framerate", "5",
        "-i", "desktop",
        "-c:v", "libx264",
        "-preset", "veryfast",
        "-bf", "2",
        "-g", "150",
        "out.mp4",
    }

    err := runAsSystem(cmd, args)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
}

对于需要交互式会话的情况,可以使用CreateProcessAsUser并指定会话ID:

func runAsSystemInteractive(cmd string, args []string) error {
    // ...(获取令牌的代码同上)...

    // 获取当前会话ID
    sessionID, err := windows.WTSGetActiveConsoleSessionId()
    if err != nil {
        return fmt.Errorf("WTSGetActiveConsoleSessionId failed: %v", err)
    }

    // 设置令牌会话ID
    err = windows.SetTokenInformation(
        hDuplicatedToken,
        windows.TokenSessionId,
        (*byte)(unsafe.Pointer(&sessionID)),
        uint32(unsafe.Sizeof(sessionID)),
    )
    if err != nil {
        return fmt.Errorf("SetTokenInformation failed: %v", err)
    }

    // 使用CreateProcessAsUser创建交互式进程
    var si windows.StartupInfo
    var pi windows.ProcessInformation
    si.Cb = uint32(unsafe.Sizeof(si))

    err = windows.CreateProcessAsUser(
        hDuplicatedToken,
        nil,
        syscall.StringToUTF16Ptr(cmd+" "+joinArgs(args)),
        nil,
        nil,
        false,
        windows.CREATE_NEW_CONSOLE,
        nil,
        nil,
        &si,
        &pi,
    )
    if err != nil {
        return fmt.Errorf("CreateProcessAsUser failed: %v", err)
    }

    windows.CloseHandle(pi.Thread)
    windows.CloseHandle(pi.Process)
    return nil
}

func joinArgs(args []string) string {
    result := ""
    for _, arg := range args {
        if result != "" {
            result += " "
        }
        result += arg
    }
    return result
}

注意:运行这些代码需要管理员权限,并且系统进程(PID 4)可能无法直接访问。实际使用时可能需要调整权限或使用其他系统进程的令牌。

回到顶部