Example #1
1
func spawn(cmd *exec.Cmd, _ *garden.TTYSpec, stdout io.Writer, stderr io.Writer) (process, io.WriteCloser, error) {
	ro, wo, err := os.Pipe()
	if err != nil {
		return nil, nil, fmt.Errorf("pipe failed: %s", err)
	}

	re, we, err := os.Pipe()
	if err != nil {
		return nil, nil, fmt.Errorf("pipe failed: %s", err)
	}

	ri, wi, err := os.Pipe()
	if err != nil {
		return nil, nil, fmt.Errorf("pipe failed: %s", err)
	}

	go io.Copy(stdout, ro)
	go io.Copy(stderr, re)

	attr := &syscall.ProcAttr{
		Dir:   cmd.Dir,
		Env:   cmd.Env,
		Files: []uintptr{ri.Fd(), wo.Fd(), we.Fd()},
	}

	lookedUpPath, err := lookExtensions(cmd.Path, cmd.Dir)
	if err != nil {
		return nil, nil, fmt.Errorf("look extensions failed: %s", 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 nil, nil, fmt.Errorf("duplicating handle failed: %s", err)
			}

			defer syscall.CloseHandle(syscall.Handle(fd[i]))
		}
	}

	si := new(syscall.StartupInfo)
	si.Cb = uint32(unsafe.Sizeof(*si))
	si.Flags = syscall.STARTF_USESTDHANDLES
	si.StdInput = fd[0]
	si.StdOutput = fd[1]
	si.StdErr = fd[2]

	pi := new(syscall.ProcessInformation)

	flags := uint32(syscall.CREATE_UNICODE_ENVIRONMENT)
	flags |= win32.CREATE_SUSPENDED
	flags |= win32.CREATE_BREAKAWAY_FROM_JOB

	argvp0, err := syscall.UTF16PtrFromString(lookedUpPath)
	if err != nil {
		return nil, nil, fmt.Errorf("stringing failed: %s", err)
	}

	argvp0v0v0v0, err := syscall.UTF16PtrFromString(makeCmdLine(cmd.Args))
	if err != nil {
		return nil, nil, fmt.Errorf("stringing failed: %s", err)
	}

	dirp, err := syscall.UTF16PtrFromString(attr.Dir)
	if err != nil {
		return nil, nil, fmt.Errorf("stringing failed: %s", err)
	}

	err = syscall.CreateProcess(
		argvp0,
		argvp0v0v0v0,
		nil,
		nil,
		true,
		flags,
		createEnvBlock(attr.Env),
		dirp,
		si,
		pi,
	)
	if err != nil {
		return nil, nil, fmt.Errorf("create process: %s", err)
	}

	ri.Close()
	wo.Close()
	we.Close()

	jobName, err := syscall.UTF16PtrFromString(fmt.Sprintf("%d", time.Now().UnixNano()))
	if err != nil {
		return nil, nil, fmt.Errorf("stringing failed: %s", err)
	}

	jobHandle, err := win32.CreateJobObject(nil, jobName)
	if err != nil {
		return nil, nil, fmt.Errorf("create job failed: %s", err)
	}

	err = win32.AssignProcessToJobObject(jobHandle, pi.Process)
	if err != nil {
		return nil, nil, fmt.Errorf("assign failed: %s", err)
	}

	_, err = win32.ResumeThread(pi.Thread)
	if err != nil {
		return nil, nil, fmt.Errorf("resume failed: %s", err)
	}

	return &jobProcess{
		jobHandle:     jobHandle,
		processHandle: pi.Process,
	}, wi, nil
}
Example #2
0
func (c *PipeConn) File() (*os.File, error) {
	var s syscall.Handle
	p, _ := syscall.GetCurrentProcess()
	err := syscall.DuplicateHandle(p, c.handle, p, &s, 0, true, syscall.DUPLICATE_SAME_ACCESS)
	if err != nil {
		return nil, err
	}
	return os.NewFile(uintptr(s), c.addr.String()), nil
}
func startInstance(cmd *exec.Cmd, r *os.File, w *os.File) error {
	var rHandle syscall.Handle
	var wHandle syscall.Handle
	p, _ := syscall.GetCurrentProcess()
	if err := syscall.DuplicateHandle(p, syscall.Handle(r.Fd()), p, &rHandle, 0, true, syscall.DUPLICATE_SAME_ACCESS); err != nil {
		return err
	}
	defer syscall.CloseHandle(rHandle)
	if err := syscall.DuplicateHandle(p, syscall.Handle(w.Fd()), p, &wHandle, 0, true, syscall.DUPLICATE_SAME_ACCESS); err != nil {
		return err
	}
	defer syscall.CloseHandle(wHandle)

	if cmd.Env == nil {
		cmd.Env = os.Environ()
	}
	cmd.Env = append(cmd.Env, fmt.Sprintf("ZSHANDLE_IN=%d", rHandle), fmt.Sprintf("ZSHANDLE_OUT=%d", wHandle))
	return cmd.Start()
}
// Launch creates and begins debugging a new process.
func Launch(cmd []string) (*Process, error) {
	argv0Go, err := filepath.Abs(cmd[0])
	if err != nil {
		return nil, err
	}
	// Make sure the binary exists.
	if filepath.Base(cmd[0]) == cmd[0] {
		if _, err := exec.LookPath(cmd[0]); err != nil {
			return nil, err
		}
	}
	if _, err := os.Stat(argv0Go); err != nil {
		return nil, err
	}
	// Duplicate the stdin/stdout/stderr handles
	files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
	p, _ := syscall.GetCurrentProcess()
	fd := make([]syscall.Handle, len(files))
	for i := range files {
		err := syscall.DuplicateHandle(p, syscall.Handle(files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS)
		if err != nil {
			return nil, err
		}
		defer syscall.CloseHandle(syscall.Handle(fd[i]))
	}

	argv0, err := syscall.UTF16PtrFromString(argv0Go)
	if err != nil {
		return nil, err
	}

	// create suitable command line for CreateProcess
	// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L326
	// adapted from standard library makeCmdLine
	// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L86
	var cmdLineGo string
	if len(cmd) >= 1 {
		for _, v := range cmd {
			if cmdLineGo != "" {
				cmdLineGo += " "
			}
			cmdLineGo += syscall.EscapeArg(v)
		}
	}

	var cmdLine *uint16
	if cmdLineGo != "" {
		if cmdLine, err = syscall.UTF16PtrFromString(cmdLineGo); err != nil {
			return nil, err
		}
	}

	// Initialize the startup info and create process
	si := new(sys.StartupInfo)
	si.Cb = uint32(unsafe.Sizeof(*si))
	si.Flags = syscall.STARTF_USESTDHANDLES
	si.StdInput = sys.Handle(fd[0])
	si.StdOutput = sys.Handle(fd[1])
	si.StdErr = sys.Handle(fd[2])
	pi := new(sys.ProcessInformation)
	err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, DEBUGONLYTHISPROCESS, nil, nil, si, pi)
	if err != nil {
		return nil, err
	}
	sys.CloseHandle(sys.Handle(pi.Process))
	sys.CloseHandle(sys.Handle(pi.Thread))

	dbp := New(int(pi.ProcessId))

	switch runtime.GOARCH {
	case "amd64":
		dbp.arch = AMD64Arch()
	}

	// Note - it should not actually be possible for the
	// call to waitForDebugEvent to fail, since Windows
	// will always fire a CreateProcess event immediately
	// after launching under DEBUGONLYTHISPROCESS.
	var tid, exitCode int
	dbp.execPtraceFunc(func() {
		tid, exitCode, err = dbp.waitForDebugEvent()
	})
	if err != nil {
		return nil, err
	}
	if tid == 0 {
		dbp.postExit()
		return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
	}

	return initializeDebugProcess(dbp, argv0Go, false)
}
Example #5
0
func startProcessAsUser(
	argv0 string, argv []string,
	username string, domain string, password 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.
		var err error
		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]

	token, err := logonUser(username, domain, password, logon32LogonInteractive, logon32ProviderDefault)
	if err != nil {

		return 0, 0, errors.New("logonUser: "******"createEnvironmentBlock: " + err.Error())
		}
		env = &e[0]
	} else {
		env = createEnvBlock(attr.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
}
Example #6
0
// Launch creates and begins debugging a new process.
func Launch(cmd []string) (*Process, error) {
	argv0Go, err := filepath.Abs(cmd[0])
	if err != nil {
		return nil, err
	}

	// Make sure the binary exists and is an executable file
	if filepath.Base(cmd[0]) == cmd[0] {
		if _, err := exec.LookPath(cmd[0]); err != nil {
			return nil, err
		}
	}

	peFile, err := openExecutablePath(argv0Go)
	if err != nil {
		return nil, NotExecutableErr
	}
	peFile.Close()

	// Duplicate the stdin/stdout/stderr handles
	files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
	p, _ := syscall.GetCurrentProcess()
	fd := make([]syscall.Handle, len(files))
	for i := range files {
		err := syscall.DuplicateHandle(p, syscall.Handle(files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS)
		if err != nil {
			return nil, err
		}
		defer syscall.CloseHandle(syscall.Handle(fd[i]))
	}

	argv0, err := syscall.UTF16PtrFromString(argv0Go)
	if err != nil {
		return nil, err
	}

	// create suitable command line for CreateProcess
	// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L326
	// adapted from standard library makeCmdLine
	// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L86
	var cmdLineGo string
	if len(cmd) >= 1 {
		for _, v := range cmd {
			if cmdLineGo != "" {
				cmdLineGo += " "
			}
			cmdLineGo += syscall.EscapeArg(v)
		}
	}

	var cmdLine *uint16
	if cmdLineGo != "" {
		if cmdLine, err = syscall.UTF16PtrFromString(cmdLineGo); err != nil {
			return nil, err
		}
	}

	// Initialize the startup info and create process
	si := new(sys.StartupInfo)
	si.Cb = uint32(unsafe.Sizeof(*si))
	si.Flags = syscall.STARTF_USESTDHANDLES
	si.StdInput = sys.Handle(fd[0])
	si.StdOutput = sys.Handle(fd[1])
	si.StdErr = sys.Handle(fd[2])
	pi := new(sys.ProcessInformation)
	err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
	if err != nil {
		return nil, err
	}
	sys.CloseHandle(sys.Handle(pi.Process))
	sys.CloseHandle(sys.Handle(pi.Thread))

	return newDebugProcess(int(pi.ProcessId), argv0Go)
}