Search code examples
pythonmemoryctypes

How to call CreateProcessW correctly?


Here is my code:

from ctypes import *
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010
class STARTUPINFO(Structure):
    _fields_ = [
    ("cb", DWORD),
    ("lpReserved", LPTSTR),
    ("lpDesktop", LPTSTR),
    ("lpTitle", LPTSTR),
    ("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),
    ]
class PROCESS_INFORMATION(Structure):
    _fields_ = [
    ("hProcess", HANDLE),
    ("hThread", HANDLE),
    ("dwProcessId", DWORD),
    ("dwThreadId", DWORD),
    ]


kernel32 = windll.kernel32
class debugger():
    def __init__(self):
        pass

    def load(path_to_exe):
        creation_flags = DEBUG_PROCESS
        startupinfo = STARTUPINFO()
        processinfo = PROCESS_INFORMATION()
        startupinfo.dwFlags = 0x1
        startupinfo.wShowWindow = 0x0
        startupinfo.cb = sizeof(startupinfo)
        if kernel32.CreateProcessA(path_to_exe,None,None,None,None,creation_flags,None,None,byref(startupinfo),byref(processinfo)):
            print("[*] Process launched")
            print("[*] PID: %d" % (PROCESS_INFORMATION.dwProcessId))
        else:
            print("[*] Error: 0x%08x." % (kernel32.GetLastError()))

debugger.load("C:\\WINDOWS\\system32\\calc.exe")

I'm actually following along Gray hat python right now, and I'm converting this code to python2.7 as I read it.

Whenever I run it, it goes to the error: [*] Error: 0x000003e6.

but when my friend try this code in his computer, he can get: []We have successfully launched the process! []PID: 1208

and both our systems are 64-bit windows7.

Any help would be much appreciated!


Solution

  • Do you both have 64-bit Python installed? .argtypes and .restype should be set on your function or ctypes defaults to passing 32-bit parameters. On 64-bit Python that truncates your byref values, which are 64-bit pointers.

    As a reference, here's a fully tested version that works on Python 2 and 3, both 32- and 64-bit:

    from __future__ import print_function, unicode_literals
    
    from ctypes import *
    from ctypes.wintypes import BYTE, WORD, DWORD, LPWSTR, LPCWSTR, HANDLE, LPVOID, BOOL
    
    LPBYTE = POINTER(BYTE)
    
    DEBUG_PROCESS = 0x00000001
    CREATE_NEW_CONSOLE = 0x00000010
    STARTF_USESHOWWINDOW = 0x00000001
    SW_HIDE = 0
    
    class STARTUPINFOW(Structure):
        _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)]
    
    class PROCESS_INFORMATION(Structure):
        _fields_ = [('hProcess', HANDLE),
                    ('hThread', HANDLE),
                    ('dwProcessId', DWORD),
                    ('dwThreadId', DWORD)]
    
    class SECURITY_ATTRIBUTES(Structure):
        _fields_ = [('nLength', DWORD),
                    ('lpSecurityDescriptor', LPVOID),
                    ('bInheritHandle', BOOL)]
    
    LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
    LPSTARTUPINFOW = POINTER(STARTUPINFOW)
    LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
    
    def boolcheck(result, func, args):
        if not result:
            raise WinError(get_last_error())
        return None
    
    kernel32 = WinDLL('kernel32', use_last_error=True)
    CreateProcess = kernel32.CreateProcessW
    CreateProcess.argtypes = (LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES,
                              BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION)
    CreateProcess.restype = BOOL
    CreateProcess.errcheck = boolcheck
    
    def load(path_to_exe):
        creation_flags = DEBUG_PROCESS
        startupinfo = STARTUPINFOW()
        processinfo = PROCESS_INFORMATION()
        startupinfo.dwFlags = STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = SW_HIDE
        startupinfo.cb = sizeof(startupinfo)
        CreateProcess(path_to_exe, None, None, None, False, creation_flags, None, None, byref(startupinfo), byref(processinfo))
        print('[*] Process launched')
        print('[*] PID: {}'.format(processinfo.dwProcessId))
    
    load(r'C:\windows\system32\calc.exe')
    

    Note that the OP used SW_HIDE and DEBUG_PROCESS which will not show the process window and also terminate the debugged process when the Python process exits, so only the success message will be visible. Pause at the end of the script with some input() and the process will be viewable in Task Manager.