Search code examples
pythonpython-3.xwindowsctypeskernel32

Create Process in Debug Using Python ctypes


The following code is supposed to start a new process calc.exe in debug mode. However, it fails with the code 2 or ERROR_FILE_NOT_FOUND. However, this file calc.exe does exist on the system. What could be wrong with this code? Is there an issue with the path that can be improved so this works?

from ctypes import *
kernel32 = windll.kernel32

WORD      = c_ushort
DWORD     = c_ulong
LPBYTE    = POINTER(c_ubyte)
LPTSTR    = POINTER(c_char)
HANDLE    = c_void_p

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),
    ]

DEBUG_PROCESS = 0x00000001
creation_flags = DEBUG_PROCESS

startupinfo         = STARTUPINFO()
startupinfo.dwFlags     = 0x1
startupinfo.wShowWindow = 0x0
startupinfo.cb = sizeof(startupinfo)

process_information = PROCESS_INFORMATION()

result = kernel32.CreateProcessA("C:\\Windows\\System32\\calc.exe",
                        None,
                        None,
                        None,
                        None,
                        creation_flags,
                        None,
                        None,
                        byref(startupinfo),
                        byref(process_information)
)

print(result)
print(kernel32.GetLastError())

Solution

  • There are a number of things that are wrong:

    code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    from ctypes import wintypes as wts
    
    
    class STARTUPINFOA(cts.Structure):
        _fields_ = (
            ("cb", wts.DWORD),
            ("lpReserved", wts.LPSTR),
            ("lpDesktop", wts.LPSTR),
            ("lpTitle", wts.LPSTR),
            ("dwX", wts.DWORD),
            ("dwY", wts.DWORD),
            ("dwXSize", wts.DWORD),
            ("dwYSize", wts.DWORD),
            ("dwXCountChars", wts.DWORD),
            ("dwYCountChars", wts.DWORD),
            ("dwFillAttribute", wts.DWORD),
            ("dwFlags", wts.DWORD),
            ("wShowWindow", wts.WORD),
            ("cbReserved2", wts.WORD),
            ("lpReserved2", wts.LPBYTE),
            ("hStdInput", wts.HANDLE),
            ("hStdOutput", wts.HANDLE),
            ("hStdError", wts.HANDLE),
        )
    LPSTARTUPINFOA = cts.POINTER(STARTUPINFOA)
    
    class PROCESS_INFORMATION(cts.Structure):
        _fields_ = (
            ("hProcess", wts.HANDLE),
            ("hThread", wts.HANDLE),
            ("dwProcessId", wts.DWORD),
            ("dwThreadId", wts.DWORD),
        )
    LPPROCESS_INFORMATION = cts.POINTER(PROCESS_INFORMATION)
    
    LPSECURITY_ATTRIBUTES = cts.c_void_p
    
    DEBUG_PROCESS = 0x00000001
    
    
    def main(*argv):
        kernel32 = cts.windll.kernel32
    
        GetLastError = kernel32.GetLastError
        GetLastError.argtypes = ()
        GetLastError.restype = wts.DWORD
    
        CreateProcessA = kernel32.CreateProcessA
        CreateProcessA.argtypes = (
            wts.LPCSTR, wts.LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES,
            wts.BOOL, wts.DWORD, wts.LPVOID, wts.LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION
        )
        CreateProcessA.restype = wts.BOOL
    
    
        creation_flags = DEBUG_PROCESS
        creation_flags = 0  # Not automatically killed when Python terminates
    
        startupinfo = STARTUPINFOA()
        startupinfo.cb = cts.sizeof(STARTUPINFOA)
        startupinfo.dwFlags = 0x1
        startupinfo.wShowWindow = 0x0
    
        process_information = PROCESS_INFORMATION()
    
        result = kernel32.CreateProcessA(
            b"C:\\Windows\\System32\\calc.exe",
            None,
            None,
            None,
            False,
            creation_flags,
            None,
            None,
            cts.byref(startupinfo),
            cts.byref(process_information)
        )
    
        print(f"CreateProcessA returned {result}")
        if not result:
            print(f"  Error: {GetLastError()}")
        else:
            print(f"  Created process (PId: {process_information.dwProcessId},"
                  f" TId: {process_information.dwThreadId})")
    
    
    if __name__ == "__main__":
        print(
            "Python {:s} {:03d}bit on {:s}\n".format(
                " ".join(elem.strip() for elem in sys.version.split("\n")),
                64 if sys.maxsize > 0x100000000 else 32,
                sys.platform,
            )
        )
        rc = main(*sys.argv[1:])
        print("\nDone.\n")
        sys.exit(rc)
    

    Output:

    (py_pc064_03.10_test0) [cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q078680399]> sopr.bat
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [prompt]> python ./code00.py
    Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32
    
    CreateProcessA returned 1
      Created process (PId: 32444, TId: 22832)
    
    Done.
    
    
    [prompt]> :: Calculator window is pops up
    

    As a CTypes alternative, you could try [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions which is a Python wrapper over WinAPIs (and contains lots of boilerplate code that Python programmers would not have to write). Documentation (WiP) can be found at [GitHub.MHammond]: Python for Win32 Extensions Help (or [ME.TimGolden]: Python for Win32 Extensions Help).