kernel32.CreateProcessW:访问PROCESS_INFORMATION结构时Python崩溃
经过很长一段时间,我正在尝试运行一些代码,来解释调试是怎么回事。这本书(《灰帽Python》)相当老旧,是为32位系统和Python 2.7写的。
我在64位的Windows 8上用Python 3.4来试这个代码。程序启动得很成功,但一旦我尝试访问PROCESS_INFORMATION
这个结构,Python就崩溃了。我在64位的Windows 7上也试过,同样崩溃。
在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>
它在调用之后停止了!
我已经根据这个问题对下面的代码进行了修改,但没有效果。
这是我的定义:
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 个回答
你的结构体定义中使用了 _fields
,而不是正确的属性名 _fields_
。为了帮助你发现这样的拼写错误,可以定义 __slots__ = '__weakref__'
。这样做可以防止实例生成一个 __dict__
,但仍然可以创建弱引用。当然,如果你在 __slots__
的定义中也拼错了,那还是个问题,所以在较大的项目中,应该使用工厂函数来减少由于拼写错误而导致的隐性错误,这些错误可能在程序神秘崩溃之前不会被发现。
元类 _ctypes.PyCStructType
在创建一个 Structure
子类时,会为 _fields_
中的名字添加描述符,所以一般情况下,实例不需要一个 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'
注意:
bInheritHandles
应该是0
而不是None
。如果你定义了CreateProcessW.argtypes
,那么传递None
会导致ArgumentError
,因为BOOL
不是一个指针。CreationFlags
应该包含CREATE_UNICODE_ENVIRONMENT
。你传递了NULL
给lpEnvironment
,所以新进程会继承 Python 的 Unicode 环境。- 与其打印错误代码,不如使用
raise WinError()
。这样会引发一个OSError
,并附上来自FormatError(GetLastError())
的格式化错误信息。