Reputation: 418
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
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.raise WinError()
. This raises an OSError
with the formatted error message from FormatError(GetLastError())
.Upvotes: 1