2013-09-21 56 views
0

使用找到的代码here可以成功启动作为备用用户的应用程序。但是,在应用程序启动后Python崩溃,并且Windows显示“python.exe已停止工作”。它似乎只发生在函数完成调用后,但似乎不是由函数内的任何内容引起的。调用CreateProcessWithLogonW后Python崩溃

import ctypes, sys 
from ctypes import Structure, sizeof 

NULL = 0 
TRUE = 1 
FALSE = 0 

INVALID_HANDLE_VALUE = -1 

WORD = ctypes.c_ushort 
DWORD = ctypes.c_uint 
LPSTR = ctypes.c_char_p 
LPBYTE = LPSTR 
HANDLE = DWORD 

# typedef struct _PROCESS_INFORMATION { 
#  HANDLE hProcess; 
#  HANDLE hThread; 
#  DWORD dwProcessId; 
#  DWORD dwThreadId; 
# } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION; 
class PROCESS_INFORMATION(Structure): 
    _pack_ = 1 
    _fields_ = [ 
     ('hProcess', HANDLE), 
     ('hThread',  HANDLE), 
     ('dwProcessId', DWORD), 
     ('dwThreadId', DWORD), 
    ] 

# typedef struct _STARTUPINFO { 
#  DWORD cb; 
#  LPSTR lpReserved; 
#  LPSTR lpDesktop; 
#  LPSTR lpTitle; 
#  DWORD dwX; 
#  DWORD dwY; 
#  DWORD dwXSize; 
#  DWORD dwYSize; 
#  DWORD dwXCountChars; 
#  DWORD dwYCountChars; 
#  DWORD dwFillAttribute; 
#  DWORD dwFlags; 
#  WORD wShowWindow; 
#  WORD cbReserved2; 
#  LPBYTE lpReserved2; 
#  HANDLE hStdInput; 
#  HANDLE hStdOutput; 
#  HANDLE hStdError; 
# } STARTUPINFO, *LPSTARTUPINFO; 
class STARTUPINFO(Structure): 
    _pack_ = 1 
    _fields_ = [ 
     ('cb',    DWORD), 
     ('lpReserved',  DWORD),  # LPSTR 
     ('lpDesktop',  LPSTR), 
     ('lpTitle',   LPSTR), 
     ('dwX',    DWORD), 
     ('dwY',    DWORD), 
     ('dwXSize',   DWORD), 
     ('dwYSize',   DWORD), 
     ('dwXCountChars', DWORD), 
     ('dwYCountChars', DWORD), 
     ('dwFillAttribute', DWORD), 
     ('dwFlags',   DWORD), 
     ('wShowWindow',  WORD), 
     ('cbReserved2',  WORD), 
     ('lpReserved2',  DWORD),  # LPBYTE 
     ('hStdInput',  DWORD), 
     ('hStdOutput',  DWORD), 
     ('hStdError',  DWORD), 
    ] 

# BOOL WINAPI CreateProcessWithLogonW(
# __in   LPCWSTR lpUsername, 
# __in_opt  LPCWSTR lpDomain, 
# __in   LPCWSTR lpPassword, 
# __in   DWORD dwLogonFlags, 
# __in_opt  LPCWSTR lpApplicationName, 
# __inout_opt LPWSTR lpCommandLine, 
# __in   DWORD dwCreationFlags, 
# __in_opt  LPVOID lpEnvironment, 
# __in_opt  LPCWSTR lpCurrentDirectory, 
# __in   LPSTARTUPINFOW lpStartupInfo, 
# __out  LPPROCESS_INFORMATION lpProcessInfo 
#); 
def CreateProcessWithLogonW(lpUsername = None, lpDomain = None, lpPassword = 
None, dwLogonFlags = 0, lpApplicationName = None, lpCommandLine = None, 
dwCreationFlags = 0, lpEnvironment = None, lpCurrentDirectory = None, 
lpStartupInfo = None): 
    if not lpUsername: 
     lpUsername   = NULL 
    else: 
     lpUsername   = ctypes.c_wchar_p(lpUsername) 
    if not lpDomain: 
     lpDomain   = NULL 
    else: 
     lpDomain   = ctypes.c_wchar_p(lpDomain) 
    if not lpPassword: 
     lpPassword   = NULL 
    else: 
     lpPassword   = ctypes.c_wchar_p(lpPassword) 
    if not lpApplicationName: 
     lpApplicationName = NULL 
    else: 
     lpApplicationName = ctypes.c_wchar_p(lpApplicationName) 
    if not lpCommandLine: 
     lpCommandLine  = NULL 
    else: 
     lpCommandLine  = ctypes.create_unicode_buffer(lpCommandLine) 
    if not lpEnvironment: 
     lpEnvironment  = NULL 
    else: 
     lpEnvironment  = ctypes.c_wchar_p(lpEnvironment) 
    if not lpCurrentDirectory: 
     lpCurrentDirectory = NULL 
    else: 
     lpCurrentDirectory = ctypes.c_wchar_p(lpCurrentDirectory) 
    if not lpStartupInfo: 
     lpStartupInfo    = STARTUPINFO() 
     lpStartupInfo.cb   = sizeof(STARTUPINFO) 
     lpStartupInfo.lpReserved = 0 
     lpStartupInfo.lpDesktop = 0 
     lpStartupInfo.lpTitle  = 0 
     lpStartupInfo.dwFlags  = 0 
     lpStartupInfo.cbReserved2 = 0 
     lpStartupInfo.lpReserved2 = 0 
    lpProcessInformation    = PROCESS_INFORMATION() 
    lpProcessInformation.hProcess  = INVALID_HANDLE_VALUE 
    lpProcessInformation.hThread  = INVALID_HANDLE_VALUE 
    lpProcessInformation.dwProcessId = 0 
    lpProcessInformation.dwThreadId = 0 
    success = ctypes.windll.advapi32.CreateProcessWithLogonW(lpUsername, 
lpDomain, lpPassword, dwLogonFlags, lpApplicationName, 
ctypes.byref(lpCommandLine), dwCreationFlags, lpEnvironment, 
lpCurrentDirectory, ctypes.byref(lpStartupInfo), 
ctypes.byref(lpProcessInformation)) 
    if success == FALSE: 
     raise ctypes.WinError() 
    #A raw_input or other blocking function here will prevent python from crashing until continuing 
    return lpProcessInformation #Happens whether or not this is returned 

CreateProcessWithLogonW("User", "Domain", "Password", 0, None, "C:\\Windows\\notepad.exe") 
print("Test") #This will never be reached 

正如我在代码中所评论的,如果您阻止达到函数的结尾,则不会发生崩溃。范围返回到函数外部之后的任何内容都将无法到达,python.exe会崩溃。

我试过的解决方法是在函数的末尾使用taskkill来通过其PID来杀死python.exe进程。这确实阻止了错误信息按预期发生,但并不理想,因为它也会杀死任何子进程(包括成功启动的子进程)。我无法弄清楚为什么完成函数调用会导致Python崩溃。这发生在Python 2.7和3.x中。任何建议,非常感谢。

回答

3

对于HANDLE或任何其他指针类型,使用32位DWORD在64位Windows上不正确。 ctypes.wintypes模块定义了可在32位和64位Windows上工作的类型。如果缺少特定类型,则可以在Windows Data Types中找到定义。

设置_pack_ = 1错误地使用1字节对齐而不是使用本地对齐填充。另外,STARTUPINFOW应该使用LPWSTR而不是LPSTR

试试这个改写:

import ctypes 
from ctypes import wintypes 

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

CREATE_NEW_CONSOLE   = 0x00000010 
CREATE_NO_WINDOW   = 0x08000000 
DETACHED_PROCESS   = 0x00000008 
CREATE_NEW_PROCESS_GROUP = 0x00000200 
CREATE_UNICODE_ENVIRONMENT = 0x00000400 

if not hasattr(wintypes, 'LPBYTE'): 
    wintypes.LPBYTE = ctypes.POINTER(wintypes.BYTE) 

class HANDLE(wintypes.HANDLE): 

    def detach(self): 
     handle, self.value = self.value, None 
     return wintypes.HANDLE(handle) 

    def close(self, CloseHandle=kernel32.CloseHandle): 
     if self: 
      CloseHandle(self.detach()) 

    def __del__(self): 
     self.close() 

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

LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) 

class STARTUPINFOW(ctypes.Structure): 
    """http://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',  wintypes.LPBYTE), 
       ('hStdInput',  wintypes.HANDLE), 
       ('hStdOutput',  wintypes.HANDLE), 
       ('hStdError',  wintypes.HANDLE)) 

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

LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW) 

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

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

def CreateProcessWithLogonW(username, password, domain=None, logonflags=0, 
          executable=None, commandline=None, creationflags=0, 
          env=None, cwd=None, startupinfo=None): 
    if commandline is not None: 
     commandline = ctypes.create_unicode_buffer(commandline) 
    creationflags |= CREATE_UNICODE_ENVIRONMENT 
    if startupinfo is None: 
     startupinfo = STARTUPINFOW() 
    pi = PROCESS_INFORMATION() 
    advapi32.CreateProcessWithLogonW(username, domain, password, logonflags, 
            executable, commandline, creationflags, 
            env, cwd, ctypes.byref(startupinfo), 
            ctypes.byref(pi)) 
    return pi.hProcess, pi.hThread, pi.dwProcessId, pi.dwThreadId 

if __name__ == '__main__': 
    import os 
    import getpass 
    username = input('username: ') 
    password = getpass.getpass('password: ') 
    exe = os.environ['ComSpec'] 
    cflags = CREATE_NEW_CONSOLE 
    hProcess, hThread, pid, tid = CreateProcessWithLogonW(
      username, password, executable=exe, creationflags=cflags) 
    print('PID: %d' % pid)