Python在Windows上以不同的用户身份运行subprocess.Popen

7

如何在Windows上使用Python以不同的用户身份启动子进程?最好支持XP及以上版本,但如果只支持Vista和7也可以接受。


我将@EricPruitt发布的答案移动到了CW答案中。如果你想自己发布它,请重新发布答案并在这里@我,我会删除CW答案。 - Steinar Lima
3个回答

6

1
我没有看到捕获stdout/err的方法。如果有帮助,那就太好了。 - wesm
你可以创建一个以目标用户身份运行的Python进程,让该Python进程运行你的子进程,并通过套接字连接或类似方法将 stdin/out/err 转发给“主”Python进程。 - pixelbrei

4

另一种选择是使用 runas ... 命令而不是所需的进程。请注意,Run As 服务应启用并运行。


在Windows 10中,该服务现在被称为“Secondary Logon”。 - undefined

3

我最终增强了子进程:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import subprocess
import win32con
import win32process
import win32security

from subprocess import *

__all__ = ["Popen","PIPE", "STDOUT", "call", "check_call",
    "CalledProcessError", "CREATE_NEW_CONSOLE", "LoginSTARTUPINFO",
    "STARTUPINFO"]

class LoginSTARTUPINFO(object):
    """
    Special STARTUPINFO instance that carries login credentials. When a
    LoginSTARTUPINFO instance is used with Popen, the process will be executed
    with the credentials used to instantiate the class.

    If an existing vanilla STARTUPINFO instance needs to be converted, it
    can be supplied as the last parameter when instantiating LoginSTARTUPINFO.

    The LoginSTARTUPINFO cannot be used with the regular subprocess module.

    >>> import subprocesswin32 as subprocess
    >>> sysuser = LoginSTARTUPINFO("username", "pswd123", "machine")
    >>> stdout, stderr = subprocess.Popen("cmd.exe", stdout=subprocess.PIPE,
    ...     startupinfo=sysuser).communicate()
    """
    def __init__(self, username, domain, password, startupinfo=None):
        m_startupinfo = win32process.STARTUPINFO()

        # Creates an actual win32 STARTUPINFO class using the attributes
        # of whatever STARTUPINFO-like object we are passed.
        for attr in dir(startupinfo):
            if not(attr.startswith("_") or attr not in dir(m_startupinfo)):
                setattr(m_startupinfo, attr, getattr(startupinfo, attr))

        # Login credentials
        self.credentials = (username, domain, password)
        # Proper win32 STARTUPINFO representation for CreateProcess
        self.win32startupinfo = m_startupinfo

def CreateProcess(*args):
    startupinfo = args[-1]

    # If we are passed a LoginSTARTUPINFO, that means we need to use
    # CreateProcessAsUser instead of the CreateProcess in subprocess
    if isinstance(startupinfo, LoginSTARTUPINFO):
        # Gets the actual win32 STARTUPINFO object from LoginSTARTUPINFO
        win32startupinfo = startupinfo.win32startupinfo

        mkprocargs = args[:-1] + (win32startupinfo,)

        login, domain, password = startupinfo.credentials

        # Get a user handle from the credentials
        userhandle = win32security.LogonUser(login, domain, password,
            win32con.LOGON32_LOGON_INTERACTIVE,
            win32con.LOGON32_PROVIDER_DEFAULT)

        try:
            # Return the pipes from CreateProcessAsUser
            return win32process.CreateProcessAsUser(userhandle, *mkprocargs)
        finally:
            # Close the userhandle before throwing whatever error arises
            userhandle.Close()

    return win32process.CreateProcess(*args)

# Overrides the CreateProcess module of subprocess with ours. CreateProcess
# will automatically act like the original CreateProcess when it is not passed
# a LoginSTARTUPINFO object.
STARTUPINFO = subprocess.STARTUPINFO = win32process.STARTUPINFO
subprocess._subprocess.CreateProcess = CreateProcess

以下代码以用户名 username 的身份启动 cmd.exe:
>>> import subprocesswin32 as subprocess
>>> sysuser = LoginSTARTUPINFO("username", "pswd123", "machine")
>>> stdout, stderr = subprocess.Popen("cmd.exe", stdout=subprocess.PIPE,
...     startupinfo=sysuser).communicate()

这是由OP在问题中编辑过的 - 移动到CW答案中。 - Steinar Lima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接