Blutkoete
Blutkoete

Reputation: 418

kernel32.CreateProcessW: Python crashes after successful launch when trying to access PROCESS_INFORMATION structure

After quite a long time, I'm trying to run some code that explains how debugging works. The book ("Grey Hat Python") is quite old and was written for 32bit systems and Python 2.7.

I'm trying it on Windows 8 64bit with Python 3.4. The process launches successful, but the moment I try to access the PROCESS_INFORMATION structure, Python crashes. I tried it on Windows 7 64bit, it crashes, too.

In Eclipse, I don't get an error message (neither do I get one on my Windows 7 machine, there Python.exe just stops working), but on Windows 8 I do (from within a Powershell):

[*] Field 0: ('hProcess', <class 'ctypes.c_void_p'>)
[*] Field 1: ('hThread', <class 'ctypes.c_void_p'>)
[*] Field 2: ('dwProcessId', <class 'ctypes.c_ulong'>)
[*] Field 3: ('dwThreadId', <class 'ctypes.c_ulong'>)
Traceback (most recent call last):
  File "my_test.py", line 11, in <module>
    debugger.load("C:\\Windows\\System32\\calc.exe")
  File "C:\Workspace\my_debugger\my_debugger.py", line 57, in lo
    byref(process_information)):
OSError: exception: access violation reading 0xFFFFFFFFFFFFFFFF

Looks like the pointers are pointing nowhere. It stops with the CreateProcessW() call!

Within Eclipse:

[*] Field 0: ('hProcess', <class 'ctypes.c_void_p'>)
[*] Field 1: ('hThread', <class 'ctypes.c_void_p'>)
[*] Field 2: ('dwProcessId', <class 'ctypes.c_ulong'>)
[*] Field 3: ('dwThreadId', <class 'ctypes.c_ulong'>)
[*] We have successfully launched the process!
[*] PROCESS_INFORMATION object: <my_debugger_defines.PROCESS_INFORMATION object at  0x00000000031623C8>

It stops AFTER the call!

I already applied changes from this question to the code below, to no avail.

These are my defines:

from ctypes import *
from ctypes.wintypes import *

# Let's map the Microsoft types to ctypes for clarity
LPBYTE  = POINTER(BYTE)

# Constants
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010

# Structures for CreateProcessA() function
class STARTUPINFOW(Structure):
    _fields = [
               ("cb",               DWORD),
               ("lpReserved",       LPWSTR),
               ("lpDesktop",        LPWSTR),
               ("lpTitle",          LPWSTR),
               ("dwX",              DWORD),
               ("dwY",              DWORD),
               ("dwXSize",          DWORD),
               ("dwYSize",          DWORD),
               ("dwXCountChars",    DWORD),
               ("dwYCountChars",    DWORD),
               ("dwFillAtrribute",  DWORD),
               ("dwFlags",          DWORD),
               ("wShowWindow",      WORD),
               ("cbReserved2",      WORD),
               ("lpReserved2",      LPBYTE),
               ("hStdInput",        HANDLE),
               ("hStdOutput",       HANDLE),
               ("hStdError",        HANDLE),
              ]
LPSTARTUPINFOW = POINTER(STARTUPINFOW)


class PROCESS_INFORMATION(Structure):
    _fields = [
               ("hProcess",         HANDLE),
               ("hThread",          HANDLE),
               ("dwProcessId",      DWORD),
               ("dwThreadId",       DWORD),
              ]
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)

And here's the main code:

kernel32 = windll.kernel32

class Debugger():

    def __init__(self):
        '''
        Constructor
        '''
        pass

    def load(self, path_to_exe):

        # dwCreation flag determines how to create the process
        # set creation_flags = CREATE_NEW_CONSOLE if you want
        # to see the calculator GUI
        creation_flags = DEBUG_PROCESS

        # instantiate the structs
        startupinfo         = STARTUPINFOW()
        process_information = PROCESS_INFORMATION()

        # The following two optiions allow the started process
        # to be shown as a seperate window. This also illustrates
        # how different settings in the STARTUPINFO struct can affect
        # the debuggee.
        startupinfo.dwFlags     = 0x1
        startupinfo.wShowWindow = 0x0

        # We then initialize the cb variable in the STARTUPINFO struct
        # which is just the size of the struct itself
        startupinfo.cb = sizeof(startupinfo)

        print("[*] PROCESS_INFORMATION object: %s" % process_information)
        for count, field in enumerate(process_information._fields):
            print("[*] Field %d: %s" % (count, field))
        if kernel32.CreateProcessW(path_to_exe,
                                   None,
                                   None,
                                   None,
                                   None,
                                   creation_flags,
                                   None,
                                   None,
                                   byref(startupinfo),
                                   byref(process_information)):
            print("[*] We have successfully launched the process!")
            print("[*] PROCESS_INFORMATION object: %s" % process_information)
            for count, field in enumerate(process_information._fields):
                print("[*] Field %d: %s" % (count, field))
        else:
            print("[*] Error: 0x%08x." % kernel32.GetLastError())

        print("[*] Debugger finished.")

It's simply called by:

import my_debugger

debugger = my_debugger.Debugger()

debugger.load("C:\\Windows\\System32\\calc.exe")

I admit I'm out of my league here, but you have so start somewhere. As you can see from the output, before CreateProcessW(), I can access the struct just fine, but after the successful launch of the process, the struct appears to be broken.

Why is my process_information structure broken?

I fear I'm just blind to spot a typo after looking on that for hours.

Thank you very much for your support!

Upvotes: 0

Views: 914

Answers (1)

Eryk Sun
Eryk Sun

Reputation: 34260

Your struct definitions assign to _fields instead of the correct attribute name _fields_. To help catch a typo like this, define __slots__ = '__weakref__'. This prevents instances from getting a __dict__, but it retains the ability to create weak references. Of course, it's still a problem if you have a typo in the __slots__ definition itself, so a larger project should use a factory function to minimize errors due to typos that pass silently until the process mysteriously crashes.

The metaclass _ctypes.PyCStructType adds descriptors for the names in _fields_ when it creates a Structure subclass, so generally an instance has no need of a dict. If you accidentally use _fields or some other typo in the attribute name, then no descriptors will be added. In this case, accessing a field will raise an AttributeError. Using __slots__ also prevents a typo in a field name from erroneously creating an instance attribute.

from ctypes import *

class Good(Structure):
    __slots__ = '__weakref__'
    _fields_ = [('a', c_int), 
                ('b', c_int)]

class Bad(Structure):
    __slots__ = '__weakref__'
    _fields = [('a', c_int), 
               ('b', c_int)]

>>> g = Good()
>>> g.a = 1
>>> g.c = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Good' object has no attribute 'c'

>>> b = Bad()
>>> b.a = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Bad' object has no attribute 'a'

NB:

  • bInheritHandles should be 0 instead of None. If you had defined CreateProcessW.argtypes, then passing None would be an ArgumentError because BOOL is not a pointer.
  • CreationFlags should include CREATE_UNICODE_ENVIRONMENT. You're passing NULL for lpEnvironment, so the new process inherits Python's unicode environment.
  • Instead of printing an error code, you can raise WinError(). This raises an OSError with the formatted error message from FormatError(GetLastError()).

Upvotes: 1

Related Questions