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)可能无法直接访问。实际使用时可能需要调整权限或使用其他系统进程的令牌。

