func startProcessAsUser( argv0 string, argv []string, username string, domain string, attr *syscall.ProcAttr, ) (pid int, handle uintptr, err error) { if len(argv0) == 0 { return 0, 0, syscall.EWINDOWS } if attr == nil { attr = &zeroProcAttr } sys := attr.Sys if sys == nil { sys = &zeroSysProcAttr } if len(attr.Files) > 3 { return 0, 0, syscall.EWINDOWS } if len(attr.Files) < 3 { return 0, 0, syscall.EINVAL } if len(attr.Dir) != 0 { // StartProcess assumes that argv0 is relative to attr.Dir, // because it implies Chdir(attr.Dir) before executing argv0. // Windows CreateProcess assumes the opposite: it looks for // argv0 relative to the current directory, and, only once the new // process is started, it does Chdir(attr.Dir). We are adjusting // for that difference here by making argv0 absolute. argv0, err = joinExeDirAndFName(attr.Dir, argv0) if err != nil { return 0, 0, err } } argv0p, err := syscall.UTF16PtrFromString(argv0) if err != nil { return 0, 0, err } var cmdline string // Windows CreateProcess takes the command line as a single string: // use attr.CmdLine if set, else build the command line by escaping // and joining each argument with spaces if sys.CmdLine != "" { cmdline = sys.CmdLine } else { cmdline = makeCmdLine(argv) } var argvp *uint16 if len(cmdline) != 0 { argvp, err = syscall.UTF16PtrFromString(cmdline) if err != nil { return 0, 0, err } } // Acquire the fork lock so that no other threads // create new fds that are not yet close-on-exec // before we fork. syscall.ForkLock.Lock() defer syscall.ForkLock.Unlock() p, _ := syscall.GetCurrentProcess() fd := make([]syscall.Handle, len(attr.Files)) for i := range attr.Files { if attr.Files[i] > 0 { err = syscall.DuplicateHandle(p, syscall.Handle(attr.Files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS) if err != nil { return 0, 0, errors.New("DuplicateHandle: " + err.Error()) } defer syscall.CloseHandle(syscall.Handle(fd[i])) } } si := new(syscall.StartupInfo) si.Cb = uint32(unsafe.Sizeof(*si)) si.Flags = syscall.STARTF_USESTDHANDLES if sys.HideWindow { si.Flags |= syscall.STARTF_USESHOWWINDOW si.ShowWindow = syscall.SW_HIDE } si.StdInput = fd[0] si.StdOutput = fd[1] si.StdErr = fd[2] wsDesktop, err := syscall.UTF16PtrFromString(`winsta0\default`) if err != nil { return 0, 0, err } si.Desktop = wsDesktop sessionID, err := getUserSessionID(username) if err != nil { return 0, 0, err } token, err := wtsQueryUserToken(sessionID) if err != nil { return 0, 0, fmt.Errorf("Query User Token Failed: %s", err.Error()) } defer token.Close() err = enableAllPrivileges(token) if err != nil { return 0, 0, errors.New("enableAllPrivileges: " + err.Error()) } var dirp *uint16 if len(attr.Dir) != 0 { dirp, err = syscall.UTF16PtrFromString(attr.Dir) if err != nil { return 0, 0, err } } else { dirp, err = getUserProfileDirectory(token) if err != nil { return 0, 0, err } } var env *uint16 env, err = createEnvironmentBlock(token, false) if err != nil { return 0, 0, errors.New("createEnvironmentBlock: " + err.Error()) } defer destroyEnvironmentBlock(env) err = impersonateLoggedOnUser(token) if err != nil { return 0, 0, errors.New("impersonateLoggedOnUser: "******"createProcessAsUser: " + err.Error()) } defer syscall.CloseHandle(syscall.Handle(pi.Thread)) return int(pi.ProcessId), uintptr(pi.Process), nil }