I'm reading 'Gray Hat Python.'
There's an example where we get the thread of the process and dump all the register values.
I copied down the source from the book, and it won't work.
Here's a part of the source that I think is the trouble.
def run(self):
# Now we have to poll the debuggee for debugging events
while self.debugger_active == True:
self.get_debug_event()
def get_debug_event(self):
debug_event = DEBUG_EVENT()
continue_status = DBG_CONTINUE
if kernel32.WaitForDebugEvent(byref(debug_event), INFINITE):
# We aren't going to build any event handlers
# just yet. Let's just resume the process for now.
# raw_input("Press a key to continue...")
# self.debugger_active = False
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
These two lines were used for previous examples and were commented out in this one.
# raw_input("Press a key to continue...")
# self.debugger_active = False
These two lines were commented out The problem is when self.debugger_active is True, it runs through the WaitForDebugEvent and ContinueDebugEvent.
But do not open thread or anything. It just runs 39 times which I have no idea why.
Here is the full source.
from ctypes import *
from my_debugger_defines import *
kernel32 = windll.kernel32
class debugger():
def __init__(self):
self.h_process = None
self.pid = None
self.debugger_active = False
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 = STARTUPINFO()
process_information = PROCESS_INFORMATION()
# The following two options allow the started process
# to be shown as a separate 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)
if kernel32.CreateProcessA(path_to_exe,
None,
None,
None,
None,
creation_flags,
None,
None,
byref(startupinfo),
byref(process_information)):
print "[*] We have successfully launched the process!"
print "[*] PID: %d" % process_information.dwProcessId
# Obtain a valid handle to the newly created process
# and store it for future access
self.h_process = self.open_process(process_information.dwProcessId)
else:
print "[*] Error: 0x%08x." % kernel32.GetLastError()
def open_process(self, pid):
h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, pid, False)
return h_process
def attach(self, pid):
self.h_process = self.open_process(pid)
# We attempt to attach to the process
# if this fails we exit the call
if kernel32.DebugActiveProcess(pid):
self.debugger_active = True
self.pid = int(pid)
self.run()
else:
print "[*] Unable to attach to the process. Error: 0x%08x." % kernel32.GetLastError()
def run(self):
# Now we have to poll the debuggee for debugging events
self.count = 1;
while self.debugger_active == True:
self.get_debug_event()
def get_debug_event(self):
debug_event = DEBUG_EVENT()
continue_status = DBG_CONTINUE
if kernel32.WaitForDebugEvent(byref(debug_event), INFINITE):
# We aren't going to build any event handlers
# just yet. Let's just resume the process for now.
# raw_input("Press a key to continue...")
# self.debugger_active = False
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
print "Just finished ContinueDebugEvent %d" % self.count
self.count += 1
def detach(self):
if kernel32.DebugActiveProcessStop(self.pid):
print "[*] Finished debugging. Exiting..."
return True
else:
print "There was an error finishing debugging"
return False
def open_thread(self, thread_id):
print "open_thread"
h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)
if h_thread is not None:
return h_thread
else:
print "[*] Could not obtain a valid thread handle."
return False
def enumerate_threads(self):
print "enumerate_threads"
thread_entry = THREADENTRY32()
thread_list = []
snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)
if snapshot is not None:
# You have to set the size of the struct
# or the call will fail
thread_entry.dwSize = sizeof(thread_entry)
success = kernel32.Thread32First(snapshot, byref(thread_entry))
while success:
if thread_entry.th32OwnerProcessID == self.pid:
thread_list.append(thread_entry.th32ThreadID)
success = kernel32.Thread32Next(snapshot, byref(thread_entry))
kernel32.CloseHandle(snapshot)
return thread_list
else:
return False
def get_thread_context(self, thread_id):
print "get_thread_context"
context = CONTEXT()
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
# Obtain a handle to the thread
h_thread = self.open_thread(thread_id)
if kernel32.GetThreadContext(h_thread, byref(context)):
kernel32.CloseHandle(h_thread)
return context
else:
return False
ADDED
I debugged this a little bit, and found out that when get_thread_context
is called, it always returns false.
Also, at the end of the ContinueDebugEvent
, it does not call EXIT_THREAD_DEBUG_EVENT
. It just terminates the program right after calling EXEPTION_DEBUG_EVENT
.
I'm not sure if these two are related, but just as an update.
Thank you very much.
PART SOLUTION
I found one huge error in the code.
I don't know if the book has some kind of edited version or not.
Anyway, one of my problems was that get_thread_context
didn't work.
The source should change to
def get_thread_context(self, h_thread):
context = CONTEXT()
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
if kernel32.GetThreadContext(h_thread, byref(context)):
kernel32.CloseHandle(h_thread)
return context
else:
return False
For some reason, the source in the book gave the thread handle as the parameter of open_thread
. You already had got the thread handle before and gave that as the parameter of get_thread_context
. So no need for that again.
===============
Still haven't found any solution for the other error.
Which the ContinueDebugEvent
won't finish with EXIT_THREAD_DEBUG_EVENT
.
It's confirmed that the code for this book only works on a 32 bit platform. Also, there are a few bugs in the source which are noted on the books website which will stop the programs from working. If you download the source from the site, these bugs have been removed.
If you want to get the code to run on your machine and you run x64, you can download "Windows XP mode" which is a virtual 32 bit windows XP environment made available for free by microsoft. http://www.microsoft.com/en-us/download/details.aspx?id=3702. Install your Python IDE there and the code should run.