Как можно заблокировать windows от услуги windows? - PullRequest
0 голосов
/ 08 марта 2020

Я пишу windows сервис в Python, который в какой-то момент должен заблокировать windows, если возникает конкретное c условие (например, человек оставляет p c без присмотра для когда-то). Я нашел решение использовать user32.LockWorkStation() с помощью модуля ctypes.

ctypes.windll.user32.LockWorkStation()

Однако после сбоя при блокировке ОС я заметил, что LockWorkstation специально работает в интерактивном режиме [рабочий стол], который не поддерживаются сервисами windows. это на самом деле потратило довольно много моего времени, так как работает при отладке! В любом случае,

Цитирование из Функция LockWorkStation :

Функция LockWorkStation вызывается только процессами, выполняющимися на интерактивном рабочем столе. Кроме того, пользователь должен войти в систему, а рабочая станция уже не может быть заблокирована. У

windows сервисов есть свойство, позволяющее им взаимодействовать с рабочим столом (на вкладке «Вход в систему»). ), однако, эта функция, если я не ошибаюсь, отключена в последних версиях windows и, кроме того, не является хорошей идеей, чтобы включить ее.

Также цитируем MSDN :

Службы не имеют циклов сообщений, если им не разрешено взаимодействовать с рабочим столом. Если сообщение l oop не предоставлено скрытой формой, как в этом примере, служба должна быть запущена под учетной записью локальной системы, и для включения взаимодействия с рабочим столом требуется ручное вмешательство. То есть администратор должен вручную установить флажок «Разрешить взаимодействовать с рабочим столом» на вкладке «Вход в систему» ​​диалогового окна свойств службы. В этом случае сообщение l oop предоставляется автоматически. Эта опция доступна только тогда, когда служба запускается под локальной системной учетной записью. Взаимодействие с рабочим столом не может быть включено программно.

Поэтому я ищу другие решения, которые позволили бы мне заблокировать windows от службы windows. Как мне этого добиться?

Ответы [ 2 ]

2 голосов
/ 09 марта 2020

Сводка

Чтобы взаимодействовать с сеансом пользователя в службе, сначала необходимо использовать идентификатор сеанса пользователя. В основном вам нужно будет использовать WTSGetActiveConsoleSessionId, WTSGetActiveConsoleSessionId и CreateEnvironmentBlock до вызова CreateProcessAsUser. Вот фрагмент кода, который делает трюк:

import win32process
import win32con
import win32ts

console_session_id = win32ts.WTSGetActiveConsoleSessionId()
console_user_token = win32ts.WTSQueryUserToken(console_session_id)
startup = win32process.STARTUPINFO()
priority = win32con.NORMAL_PRIORITY_CLASS
environment = win32profile.CreateEnvironmentBlock(console_user_token, False)
handle, thread_id ,pid, tid = win32process.CreateProcessAsUser(console_user_token, None, "rundll32.exe user32.dll,LockWorkStation", None, None, True, priority, environment, None, startup)

Если вам нужно вызвать конкретное c приложение, вы можете вызвать его следующим образом:

win32process.CreateProcessAsUser(console_user_token, your_app_exe, app_args, None, None, True, priority, environment, None, startup)

Это на самом деле, как службы в windows взаимодействовать с пользовательскими сессиями. Используя этот метод, вам больше не нужны учетные данные пользователя.

Длинное объяснение:

Когда дело доходит до Windows сервисов для доступа / взаимодействия с пользовательским сеансом (сеанс> 0). Обычно рекомендуется использовать CreateProcessAsUser (). можно было бы go включить и сделать что-то подобное ( ref ):

user = "username"
pword = "123456"
domain = "." # means current domain

logontype = win32con.LOGON32_LOGON_INTERACTIVE
# some may suggest to use BATCH mode instead in case you fail! but this doesn't work either!
# logontype = win32con.LOGON32_LOGON_BATCH
provider = win32con.LOGON32_PROVIDER_WINNT50
token = win32security.LogonUser(user, domain, pword, logontype, provider)

startup = win32process.STARTUPINFO()
process_information = PROCESS_INFORMATION()
cwd = os.path.dirname(__file__)
lock_file = os.path.join(cwd,'system_locker.exe')
appname = lock_file 
priority = win32con.NORMAL_PRIORITY_CLASS
result = win32process.CreateProcessAsUser(token, appname, None, None, None, True, priority, None, None, startup)

, но если вы go таким образом, вы столкнетесь с ошибкой:

(1314, 'CreateProcessAsUser', 'A required privilege is not held by the client.')

Существует множество предложений по устранению этой проблемы, например отключение UA C, et c. но ни один не будет работать, и если вы посмотрите документацию MSDN о CreateProcessAsUser , вы увидите:

Как правило, процесс, вызывающий функцию CreateProcessAsUser должен иметь привилегию SE_INCREASE_QUOTA_NAME и может потребовать привилегию SE_ASSIGNPRIMARYTOKEN_NAME, если токен не может быть назначен. Если эта функция завершается с ошибкой ERROR_PRIVILEGE_NOT_HELD (1314), используйте вместо этого функцию CreateProcessWithLogonW. CreateProcessWithLogonW не требует специальных привилегий, но указанной учетной записи пользователя необходимо разрешить вход в интерактивном режиме. Как правило, лучше всего использовать CreateProcessWithLogonW для создания процесса с альтернативными учетными данными.

, что означает использование CreateProcessWithLogonW. Если кто-то пойдет и попробует это, например, так:

from ctypes import *
from ctypes.wintypes import *

INVALID_HANDLE_VALUE = -1
CREATE_UNICODE_ENVIRONMENT = 0x00000400

CData = Array.__base__
LPBYTE = POINTER(BYTE)


class PROCESS_INFORMATION(Structure):
    '''http://msdn.microsoft.com/en-us/library/ms684873'''
    _fields_ = [
        ('hProcess', HANDLE),
        ('hThread', HANDLE),
        ('dwProcessId', DWORD),
        ('dwThreadId', DWORD),
    ]


LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)


class STARTUPINFOW(Structure):
    'http://msdn.microsoft.com/en-us/library/ms686331'
    _fields_ = [
        ('cb', DWORD),
        ('lpReserved', LPWSTR),
        ('lpDesktop', LPWSTR),
        ('lpTitle', LPWSTR),
        ('dwX', DWORD),
        ('dwY', DWORD),
        ('dwXSize', DWORD),
        ('dwYSize', DWORD),
        ('dwXCountChars', DWORD),
        ('dwYCountChars', DWORD),
        ('dwFillAttribute', DWORD),
        ('dwFlags', DWORD),
        ('wShowWindow', WORD),
        ('cbReserved2', WORD),
        ('lpReserved2', LPBYTE),
        ('hStdInput', HANDLE),
        ('hStdOutput', HANDLE),
        ('hStdError', HANDLE),
    ]


LPSTARTUPINFOW = POINTER(STARTUPINFOW)


# http://msdn.microsoft.com/en-us/library/ms682431
windll.advapi32.CreateProcessWithLogonW.restype = BOOL
windll.advapi32.CreateProcessWithLogonW.argtypes = [
                                                    LPCWSTR,  # lpUsername
                                                    LPCWSTR,  # lpDomain
                                                    LPCWSTR,  # lpPassword
                                                    DWORD,  # dwLogonFlags
                                                    LPCWSTR,  # lpApplicationName
                                                    LPWSTR,  # lpCommandLine (inout)
                                                    DWORD,  # dwCreationFlags
                                                    LPCWSTR,  # lpEnvironment  (force Unicode)
                                                    LPCWSTR,  # lpCurrentDirectory
                                                    LPSTARTUPINFOW,  # lpStartupInfo
                                                    LPPROCESS_INFORMATION,  # lpProcessInfo (out)
                                                ]


def CreateProcessWithLogonW(
                            lpUsername=None,
                            lpDomain=None,
                            lpPassword=None,
                            dwLogonFlags=0,
                            lpApplicationName=None,
                            lpCommandLine=None,
                            dwCreationFlags=0,
                            lpEnvironment=None,
                            lpCurrentDirectory=None,
                            startupInfo=None
):
    if (lpCommandLine is not None and
            not isinstance(lpCommandLine, CData)
        ):
        lpCommandLine = create_unicode_buffer(lpCommandLine)
    dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT
    if startupInfo is None:
        startupInfo = STARTUPINFOW(sizeof(STARTUPINFOW))
    processInformation = PROCESS_INFORMATION(
        INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE)
    success = windll.advapi32.CreateProcessWithLogonW(
        lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName,
        lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory,
        byref(startupInfo), byref(processInformation))
    if not success:
        raise WinError()
    return processInformation

....

result = CreateProcessWithLogonW(user, domain, pword, 0, None, "rundll32.exe user32.dll,LockWorkStation")

Он / она столкнется с ошибкой:

(13, 'Access is denied.', None, 5)

Другая похожая реализация, которая терпит неудачу, выглядит следующим образом (принято с ):


from ctypes import wintypes
from subprocess import PIPE

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)

ERROR_INVALID_HANDLE = 0x0006
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
INVALID_DWORD_VALUE = wintypes.DWORD(-1).value

DEBUG_PROCESS                    = 0x00000001
DEBUG_ONLY_THIS_PROCESS          = 0x00000002
CREATE_SUSPENDED                 = 0x00000004
DETACHED_PROCESS                 = 0x00000008
CREATE_NEW_CONSOLE               = 0x00000010
CREATE_NEW_PROCESS_GROUP         = 0x00000200
CREATE_UNICODE_ENVIRONMENT       = 0x00000400
CREATE_SEPARATE_WOW_VDM          = 0x00000800
CREATE_SHARED_WOW_VDM            = 0x00001000
INHERIT_PARENT_AFFINITY          = 0x00010000
CREATE_PROTECTED_PROCESS         = 0x00040000
EXTENDED_STARTUPINFO_PRESENT     = 0x00080000
CREATE_BREAKAWAY_FROM_JOB        = 0x01000000
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000
CREATE_DEFAULT_ERROR_MODE        = 0x04000000
CREATE_NO_WINDOW                 = 0x08000000

STARTF_USESHOWWINDOW    = 0x00000001
STARTF_USESIZE          = 0x00000002
STARTF_USEPOSITION      = 0x00000004
STARTF_USECOUNTCHARS    = 0x00000008
STARTF_USEFILLATTRIBUTE = 0x00000010
STARTF_RUNFULLSCREEN    = 0x00000020
STARTF_FORCEONFEEDBACK  = 0x00000040
STARTF_FORCEOFFFEEDBACK = 0x00000080
STARTF_USESTDHANDLES    = 0x00000100
STARTF_USEHOTKEY        = 0x00000200
STARTF_TITLEISLINKNAME  = 0x00000800
STARTF_TITLEISAPPID     = 0x00001000
STARTF_PREVENTPINNING   = 0x00002000

SW_HIDE            = 0
SW_SHOWNORMAL      = 1
SW_SHOWMINIMIZED   = 2
SW_SHOWMAXIMIZED   = 3
SW_SHOWNOACTIVATE  = 4
SW_SHOW            = 5
SW_MINIMIZE        = 6
SW_SHOWMINNOACTIVE = 7
SW_SHOWNA          = 8
SW_RESTORE         = 9
SW_SHOWDEFAULT     = 10 # ~STARTUPINFO
SW_FORCEMINIMIZE   = 11

LOGON_WITH_PROFILE        = 0x00000001
LOGON_NETCREDENTIALS_ONLY = 0x00000002

STD_INPUT_HANDLE  = wintypes.DWORD(-10).value
STD_OUTPUT_HANDLE = wintypes.DWORD(-11).value
STD_ERROR_HANDLE  = wintypes.DWORD(-12).value

class HANDLE(wintypes.HANDLE):
    __slots__ = 'closed',

    def __int__(self):
        return self.value or 0

    def Detach(self):
        if not getattr(self, 'closed', False):
            self.closed = True
            value = int(self)
            self.value = None
            return value
        raise ValueError("already closed")

    def Close(self, CloseHandle=kernel32.CloseHandle):
        if self and not getattr(self, 'closed', False):
            CloseHandle(self.Detach())

    __del__ = Close

    def __repr__(self):
        return "%s(%d)" % (self.__class__.__name__, int(self))

class PROCESS_INFORMATION(ctypes.Structure):
    """https://msdn.microsoft.com/en-us/library/ms684873"""
    __slots__ = '_cached_hProcess', '_cached_hThread'

    _fields_ = (('_hProcess',   HANDLE),
                ('_hThread',    HANDLE),
                ('dwProcessId', wintypes.DWORD),
                ('dwThreadId',  wintypes.DWORD))

    @property
    def hProcess(self):
        if not hasattr(self, '_cached_hProcess'):
            self._cached_hProcess = self._hProcess
        return self._cached_hProcess

    @property
    def hThread(self):
        if not hasattr(self, '_cached_hThread'):
            self._cached_hThread = self._hThread
        return self._cached_hThread

    def __del__(self):
        try:
            self.hProcess.Close()
        finally:
            self.hThread.Close()

LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)

LPBYTE = ctypes.POINTER(wintypes.BYTE)

class STARTUPINFO(ctypes.Structure):
    """https://msdn.microsoft.com/en-us/library/ms686331"""
    _fields_ = (('cb',              wintypes.DWORD),
                ('lpReserved',      wintypes.LPWSTR),
                ('lpDesktop',       wintypes.LPWSTR),
                ('lpTitle',         wintypes.LPWSTR),
                ('dwX',             wintypes.DWORD),
                ('dwY',             wintypes.DWORD),
                ('dwXSize',         wintypes.DWORD),
                ('dwYSize',         wintypes.DWORD),
                ('dwXCountChars',   wintypes.DWORD),
                ('dwYCountChars',   wintypes.DWORD),
                ('dwFillAttribute', wintypes.DWORD),
                ('dwFlags',         wintypes.DWORD),
                ('wShowWindow',     wintypes.WORD),
                ('cbReserved2',     wintypes.WORD),
                ('lpReserved2',     LPBYTE),
                ('hStdInput',       wintypes.HANDLE),
                ('hStdOutput',      wintypes.HANDLE),
                ('hStdError',       wintypes.HANDLE))

    def __init__(self, **kwds):
        self.cb = ctypes.sizeof(self)
        super(STARTUPINFO, self).__init__(**kwds)

class PROC_THREAD_ATTRIBUTE_LIST(ctypes.Structure):
    pass

PPROC_THREAD_ATTRIBUTE_LIST = ctypes.POINTER(PROC_THREAD_ATTRIBUTE_LIST)

class STARTUPINFOEX(STARTUPINFO):
    _fields_ = (('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),)

LPSTARTUPINFO = ctypes.POINTER(STARTUPINFO)
LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX)

class SECURITY_ATTRIBUTES(ctypes.Structure):
    _fields_ = (('nLength',              wintypes.DWORD),
                ('lpSecurityDescriptor', wintypes.LPVOID),
                ('bInheritHandle',       wintypes.BOOL))
    def __init__(self, **kwds):
        self.nLength = ctypes.sizeof(self)
        super(SECURITY_ATTRIBUTES, self).__init__(**kwds)

LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES)

class HANDLE_IHV(HANDLE):
    pass

class DWORD_IDV(wintypes.DWORD):
    pass

def _check_ihv(result, func, args):
    if result.value == INVALID_HANDLE_VALUE:
        raise ctypes.WinError(ctypes.get_last_error())
    return result.value

def _check_idv(result, func, args):
    if result.value == INVALID_DWORD_VALUE:
        raise ctypes.WinError(ctypes.get_last_error())
    return result.value

def _check_bool(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

def WIN(func, restype, *argtypes):
    func.restype = restype
    func.argtypes = argtypes
    if issubclass(restype, HANDLE_IHV):
        func.errcheck = _check_ihv
    elif issubclass(restype, DWORD_IDV):
        func.errcheck = _check_idv
    else:
        func.errcheck = _check_bool

# https://msdn.microsoft.com/en-us/library/ms724211
WIN(kernel32.CloseHandle, wintypes.BOOL,
    wintypes.HANDLE,) # _In_ HANDLE hObject

# https://msdn.microsoft.com/en-us/library/ms685086
WIN(kernel32.ResumeThread, DWORD_IDV,
    wintypes.HANDLE,) # _In_ hThread

# https://msdn.microsoft.com/en-us/library/ms682425
WIN(kernel32.CreateProcessW, wintypes.BOOL,
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpProcessAttributes
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpThreadAttributes
    wintypes.BOOL,          # _In_        bInheritHandles
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation

# https://msdn.microsoft.com/en-us/library/ms682429
WIN(advapi32.CreateProcessAsUserW, wintypes.BOOL,
    wintypes.HANDLE,        # _In_opt_    hToken
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpProcessAttributes
    LPSECURITY_ATTRIBUTES,  # _In_opt_    lpThreadAttributes
    wintypes.BOOL,          # _In_        bInheritHandles
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation

# https://msdn.microsoft.com/en-us/library/ms682434
WIN(advapi32.CreateProcessWithTokenW, wintypes.BOOL,
    wintypes.HANDLE,        # _In_        hToken
    wintypes.DWORD,         # _In_        dwLogonFlags
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation

# https://msdn.microsoft.com/en-us/library/ms682431
WIN(advapi32.CreateProcessWithLogonW, wintypes.BOOL,
    wintypes.LPCWSTR,       # _In_        lpUsername
    wintypes.LPCWSTR,       # _In_opt_    lpDomain
    wintypes.LPCWSTR,       # _In_        lpPassword
    wintypes.DWORD,         # _In_        dwLogonFlags
    wintypes.LPCWSTR,       # _In_opt_    lpApplicationName
    wintypes.LPWSTR,        # _Inout_opt_ lpCommandLine
    wintypes.DWORD,         # _In_        dwCreationFlags
    wintypes.LPCWSTR,       # _In_opt_    lpEnvironment
    wintypes.LPCWSTR,       # _In_opt_    lpCurrentDirectory
    LPSTARTUPINFO,          # _In_        lpStartupInfo
    LPPROCESS_INFORMATION)  # _Out_       lpProcessInformation


CREATION_TYPE_NORMAL = 0
CREATION_TYPE_LOGON  = 1
CREATION_TYPE_TOKEN  = 2
CREATION_TYPE_USER   = 3

class CREATIONINFO(object):
    __slots__ = ('dwCreationType',
        'lpApplicationName', 'lpCommandLine', 'bUseShell',
        'lpProcessAttributes', 'lpThreadAttributes', 'bInheritHandles',
        'dwCreationFlags', 'lpEnvironment', 'lpCurrentDirectory',
        'hToken', 'lpUsername', 'lpDomain', 'lpPassword', 'dwLogonFlags')

    def __init__(self, dwCreationType=CREATION_TYPE_NORMAL,
                 lpApplicationName=None, lpCommandLine=None, bUseShell=False,
                 lpProcessAttributes=None, lpThreadAttributes=None,
                 bInheritHandles=False, dwCreationFlags=0, lpEnvironment=None,
                 lpCurrentDirectory=None, hToken=None, dwLogonFlags=0,
                 lpUsername=None, lpDomain=None, lpPassword=None):
        self.dwCreationType = dwCreationType
        self.lpApplicationName = lpApplicationName
        self.lpCommandLine = lpCommandLine
        self.bUseShell = bUseShell
        self.lpProcessAttributes = lpProcessAttributes
        self.lpThreadAttributes = lpThreadAttributes
        self.bInheritHandles = bInheritHandles
        self.dwCreationFlags = dwCreationFlags
        self.lpEnvironment = lpEnvironment
        self.lpCurrentDirectory = lpCurrentDirectory
        self.hToken = hToken
        self.lpUsername = lpUsername
        self.lpDomain = lpDomain
        self.lpPassword = lpPassword
        self.dwLogonFlags = dwLogonFlags

def create_environment(environ):
    if environ is not None:
        items = ['%s=%s' % (k, environ[k]) for k in sorted(environ)]
        buf = '\x00'.join(items)
        length = len(buf) + 2 if buf else 1
        return ctypes.create_unicode_buffer(buf, length)

def create_process(commandline=None, creationinfo=None, startupinfo=None):
    if creationinfo is None:
        creationinfo = CREATIONINFO()

    if startupinfo is None:
        startupinfo = STARTUPINFO()
    elif isinstance(startupinfo, subprocess.STARTUPINFO):
        startupinfo = STARTUPINFO(dwFlags=startupinfo.dwFlags,
                        hStdInput=startupinfo.hStdInput,
                        hStdOutput=startupinfo.hStdOutput,
                        hStdError=startupinfo.hStdError,
                        wShowWindow=startupinfo.wShowWindow)

    si, ci, pi = startupinfo, creationinfo, PROCESS_INFORMATION()

    if commandline is None:
        commandline = ci.lpCommandLine

    if commandline is not None:
        if ci.bUseShell:
            si.dwFlags |= STARTF_USESHOWWINDOW
            si.wShowWindow = SW_HIDE
            comspec = os.environ.get("ComSpec", os.path.join(
                        os.environ["SystemRoot"], "System32", "cmd.exe"))
            commandline = '"{}" /c "{}"'.format(comspec, commandline)
        commandline = ctypes.create_unicode_buffer(commandline)

    dwCreationFlags = ci.dwCreationFlags | CREATE_UNICODE_ENVIRONMENT
    lpEnvironment = create_environment(ci.lpEnvironment)

    if (dwCreationFlags & DETACHED_PROCESS and
       ((dwCreationFlags & CREATE_NEW_CONSOLE) or
        (ci.dwCreationType == CREATION_TYPE_LOGON) or
        (ci.dwCreationType == CREATION_TYPE_TOKEN))):
        raise RuntimeError('DETACHED_PROCESS is incompatible with '
                           'CREATE_NEW_CONSOLE, which is implied for '
                           'the logon and token creation types')

    if ci.dwCreationType == CREATION_TYPE_NORMAL:

        kernel32.CreateProcessW(
            ci.lpApplicationName, commandline,
            ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    elif ci.dwCreationType == CREATION_TYPE_LOGON:

        advapi32.CreateProcessWithLogonW(
            ci.lpUsername, ci.lpDomain, ci.lpPassword, ci.dwLogonFlags,
            ci.lpApplicationName, commandline,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    elif ci.dwCreationType == CREATION_TYPE_TOKEN:

        advapi32.CreateProcessWithTokenW(
            ci.hToken, ci.dwLogonFlags,
            ci.lpApplicationName, commandline,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    elif ci.dwCreationType == CREATION_TYPE_USER:

        advapi32.CreateProcessAsUserW(
            ci.hToken,
            ci.lpApplicationName, commandline,
            ci.lpProcessAttributes, ci.lpThreadAttributes, ci.bInheritHandles,
            dwCreationFlags, lpEnvironment, ci.lpCurrentDirectory,
            ctypes.byref(si), ctypes.byref(pi))

    else:
        raise ValueError('invalid process creation type')

    return pi

class Popen(subprocess.Popen):
    def __init__(self, *args, **kwds):
        ci = self._creationinfo = kwds.pop('creationinfo', CREATIONINFO())
        if kwds.pop('suspended', False):
            ci.dwCreationFlags |= CREATE_SUSPENDED
        self._child_started = False
        super(Popen, self).__init__(*args, **kwds)

    if sys.version_info[0] == 2:

        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           cwd, env, universal_newlines, startupinfo,
                           creationflags, shell, to_close, p2cread, p2cwrite,
                           c2pread, c2pwrite, errread, errwrite):
            """Execute program (MS Windows version)"""
            commandline = (args if isinstance(args, types.StringTypes) else
                           subprocess.list2cmdline(args))
            self._common_execute_child(executable, commandline, shell, 
                    close_fds, creationflags, env, cwd,
                    startupinfo, p2cread, c2pwrite, errwrite, to_close)
    else:

        def _execute_child(self, args, executable, preexec_fn, close_fds,
                           pass_fds, cwd, env, startupinfo, creationflags,
                           shell, p2cread, p2cwrite, c2pread, c2pwrite, errread,
                           errwrite, restore_signals, start_new_session):
            """Execute program (MS Windows version)"""
            assert not pass_fds, "pass_fds not supported on Windows."
            commandline = (args if isinstance(args, str) else
                           subprocess.list2cmdline(args))
            self._common_execute_child(executable, commandline, shell, 
                    close_fds, creationflags, env, cwd,
                    startupinfo, p2cread, c2pwrite, errwrite)

    def _common_execute_child(self, executable, commandline, shell,
                              close_fds, creationflags, env, cwd,
                              startupinfo, p2cread, c2pwrite, errwrite,
                              to_close=()):

        ci = self._creationinfo
        if executable is not None:
            ci.lpApplicationName = executable
        if commandline:
            ci.lpCommandLine = commandline
        if shell:
            ci.bUseShell = shell
        if not close_fds:
            ci.bInheritHandles = int(not close_fds)
        if creationflags:
            ci.dwCreationFlags |= creationflags
        if env is not None:
            ci.lpEnvironment = env
        if cwd is not None:
            ci.lpCurrentDirectory = cwd

        if startupinfo is None:
            startupinfo = STARTUPINFO()
        si = self._startupinfo = startupinfo

        default = None if sys.version_info[0] == 2 else -1
        if default not in (p2cread, c2pwrite, errwrite):
            si.dwFlags |= STARTF_USESTDHANDLES
            si.hStdInput  = int( p2cread)
            si.hStdOutput = int(c2pwrite)
            si.hStdError  = int(errwrite)

        try:
            pi = create_process(creationinfo=ci, startupinfo=si)
        finally:
            if sys.version_info[0] == 2:
                if p2cread is not None:
                    p2cread.Close()
                    to_close.remove(p2cread)
                if c2pwrite is not None:
                    c2pwrite.Close()
                    to_close.remove(c2pwrite)
                if errwrite is not None:
                    errwrite.Close()
                    to_close.remove(errwrite)
            else:
                if p2cread != -1:
                    p2cread.Close()
                if c2pwrite != -1:
                    c2pwrite.Close()
                if errwrite != -1:
                    errwrite.Close()
                if hasattr(self, '_devnull'):
                    os.close(self._devnull)

        if not ci.dwCreationFlags & CREATE_SUSPENDED:
            self._child_started = True

        # Retain the process handle, but close the thread handle
        # if it's no longer needed.
        self._processinfo = pi
        self._handle = pi.hProcess.Detach()
        self.pid = pi.dwProcessId
        if self._child_started:
            pi.hThread.Close()

    def start(self):
        if self._child_started:
            raise RuntimeError("processes can only be started once")
        hThread = self._processinfo.hThread
        prev_count = kernel32.ResumeThread(hThread)
        if prev_count > 1:
            for i in range(1, prev_count):
                if kernel32.ResumeThread(hThread) <= 1:
                    break
            else:
                raise RuntimeError('cannot start the main thread')
        # The thread's previous suspend count was 0 or 1, 
        # so it should be running now.
        self._child_started = True
        hThread.Close()

    def __del__(self):
        if not self._child_started:
            try:
                if hasattr(self, '_processinfo'):
                    self._processinfo.hThread.Close()
            finally:
                if hasattr(self, '_handle'):
                    self.terminate()
        super(Popen, self).__del__()

....

cmd = "rundll32.exe user32.dll,LockWorkStation" #lock_file
ci = CREATIONINFO(CREATION_TYPE_LOGON,
                 lpUsername=user,
                 lpPassword=pword)

p = Popen(cmd, suspended=True, creationinfo=ci,
          stdout=PIPE, universal_newlines=True)
p.start()

завершается с той же ошибкой.

И затем мы достигаем окончательного решения, которое на самом деле работает, которое заключается в использовании WTSGetActiveConsoleSessionId, WTSGetActiveConsoleSessionId и CreateEnvironmentBlock до вызова CreateProcessAsUser. Самая важная часть imho - это CreateEnvironmentBlock, которая необходима для этого. Первые два метода позволяют нам не использовать предопределенный пользователь / пароль.

Полезные ссылки:

@ EugeneMayevski'Callback предоставил две ссылки, которые также обсуждают это:
1. call-createprocessasuser-from-service
2. call-createprocessasuser-from-a-user-process-launch-from-a-service

Важное примечание:

В режиме отладки при использовании фрагмента I при условии, что вы можете столкнуться с ошибкой 1314 для WTSGetActiveConsoleSessionId, указанной в спецификации, и работающая служба не столкнется с этой ошибкой и будет работать нормально.

1 голос
/ 08 марта 2020

Проблема не в том, что службы могут или не могут взаимодействовать с рабочим столом, а в том, что они запускаются в отдельном Windows сеансе . Помимо подробного объяснения вашей проблемы, в этой статье также предлагается возможное решение:

Для более сложного пользовательского интерфейса используйте функцию CreateProcessAsUser для создания процесса в сеансе пользователя.

Вы можете даже повторно запустить python со своим собственным сценарием, но в сеансе пользователя.

Сложность здесь состоит в том, чтобы определить, , какую сессию вы должны заблокировать и какого пользователя вы должны выдать за себя. На самом деле может быть несколько пользователей, одновременно подключенных даже локально. Но, скорее всего (если вы создаете сервис для себя), вы можете просто выдать себя за пользователя в сеансе 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...