kernel32.CreateProcessW:尝试访问PROCESS_INFORMATION结构后,Python在成功启动后崩溃

时间:2014-06-13 12:57:18

标签: python python-3.x ctypes

经过相当长的一段时间,我正在尝试运行一些解释调试工作原理的代码。这本书(“Gray Hat Python”)很老,是为32位系统和Python 2.7编写的。

我正在使用Python 3.4在Windows 8 64位上尝试它。该过程启动成功,但是当我尝试访问PROCESS_INFORMATION结构时,Python崩溃了。我在Windows 7 64bit上试过它,它也崩溃了。

在Eclipse中,我没有收到错误消息(在我的Windows 7计算机上也没有得到一个,但是Python.exe只是停止工作),但在Windows 8上我做了(来自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

看起来指针指向哪里。它会在CreateProcessW()来电停止!

在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>

在通话结束后停止!

我已将this问题的更改应用到下面的代码中,但无济于事。

这些是我的定义:

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)

这是主要代码:

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.")

简单地称之为:

import my_debugger

debugger = my_debugger.Debugger()

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

我承认我已离开了我的联盟,但你已经从某个地方开始了。从输出中可以看出,在CreateProcessW()之前,我可以很好地访问结构,但之后 成功启动该过程后,该结构似乎被打破了。

为什么我的process_information结构被破坏?

我担心在看了几个小时之后,我只是盲目发现错字。

非常感谢您的支持!

1 个答案:

答案 0 :(得分:1)

您的结构定义分配给_fields而不是正确的属性名称_fields_。要帮助捕获这样的拼写错误,请定义__slots__ = '__weakref__'。这可以防止实例获得__dict__,但它保留了创建弱引用的能力。当然,如果你在__slots__定义本身有一个拼写错误,它仍然是一个问题,所以一个更大的项目应该使用工厂函数来最小化由于错误传递的错误导致的错误,直到该过程神秘地崩溃。

元类_ctypes.PyCStructType在创建_fields_子类时为Structure中的名称添加描述符,因此通常实例不需要dict。如果您在属性名称中意外使用了_fields或其他拼写错误,则不会添加任何描述符。在这种情况下,访问字段将引发AttributeError。使用__slots__还可以防止字段名称中的拼写错误地创建实例属性。

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应该是0而不是None。如果您已定义CreateProcessW.argtypes,则传递None将为ArgumentError,因为BOOL不是指针。
  • CreationFlags应包含CREATE_UNICODE_ENVIRONMENT。您正在为NULL传递lpEnvironment,因此新进程会继承Python的unicode环境。
  • 您可以raise WinError()而不是打印错误代码。这会使用来自OSError
  • 的格式化错误消息引发FormatError(GetLastError())