Add a sample for the debugger API

This commit is contained in:
Clement Rouault
2016-07-08 11:37:49 +02:00
parent 9a980d87b3
commit fb9cd75f40
6 changed files with 93 additions and 18 deletions
+76
View File
@@ -0,0 +1,76 @@
import sys
import os.path
import pprint
sys.path.append(os.path.abspath(__file__ + "\..\.."))
import windows
import windows.test
import windows.debug
from windows.generated_def.winstructs import *
class MyDebugger(windows.debug.Debugger):
def __init__(self, *args, **kwargs):
super(MyDebugger, self).__init__(*args, **kwargs)
self.struct_already_dump = set()
def dump_struct_once(self, struct, name):
if name in self.struct_already_dump:
return
windows.utils.print_ctypes_struct(struct, name, hexa=True)
self.struct_already_dump.add(name)
def on_exception(self, exception):
print("<on_exception> called with {0}".format(exception))
self.dump_struct_once(exception, " exception")
print("Single Stepping")
return self.single_step()
def on_single_step(self, exception):
print("<on_single_step> called with {0}".format(exception))
self.dump_struct_once(exception, " single_step")
def on_create_process(self, create_process):
print("<on_create_process> called with {0}".format(create_process))
self.dump_struct_once(create_process, " create_process")
pass
def on_exit_process(self, exit_process):
print("<on_exit_process> called with {0}".format(exit_process))
self.dump_struct_once(exit_process, " exit_process")
pass
def on_create_thread(self, create_thread):
print("<on_create_thread> called with {0}".format(create_thread))
self.dump_struct_once(create_thread, " create_thread")
pass
def on_exit_thread(self, exit_thread):
print("<on_exit_thread> called with {0}".format(exit_thread))
self.dump_struct_once(exit_thread, " exit_thread")
pass
def on_load_dll(self, load_dll):
print("<on_load_dll> called with {0} ({1})".format(load_dll, self._get_loaded_dll(load_dll)))
self.dump_struct_once(load_dll, " load_dll")
pass
def on_unload_dll(self, unload_dll):
print("<on_unload_dll> called with <{0}>".format(unload_dll))
self.dump_struct_once(unload_dll, " unload_dll")
pass
def on_output_debug_string(self, debug_string):
print("<on_output_debug_string> called with {0}".format(debug_string))
self.dump_struct_once(debug_string, " debug_string")
pass
def on_rip(self, rip_info):
print("<on_rip> called with {0}".format(rip_info))
self.dump_struct_once(rip_info, " rip_info")
pass
calc = windows.test.pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = MyDebugger(calc)
d.loop()
+15 -15
View File
@@ -71,6 +71,12 @@ class Debugger(object):
winproxy.DebugActiveProcess(target.pid)
return cls(target)
@classmethod
def debug(cls, path, args=None, dwCreationFlags=0, show_windows=False):
dwCreationFlags |= DEBUG_PROCESS
c = windows.utils.create_process(path, args=args, dwCreationFlags=dwCreationFlags, show_windows=show_windows)
return cls(c)
def _init_dispatch_handlers(self):
dbg_evt_dispatch = {}
dbg_evt_dispatch[EXCEPTION_DEBUG_EVENT] = self._handle_exception
@@ -337,16 +343,6 @@ class Debugger(object):
for bp, begin, end, original_prot in self._watched_memory:
if begin <= fault_addr < end:
## Reject bad EXCEPTION ?
#if fault_type == EXEC and bp.PROTECT not in [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE]:
# break
#if fault_type == READ and bp.PROTECT not in [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE]:
# break
#if fault_type == EXEC and bp.PROTECT not in [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE]:
# break
#print("BP MEM TRIGGER {0}".format(bp))
continue_flag = bp.trigger(self, exception)
self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF
self._pass_memory_breakpoint(bp, begin, end, original_prot)
@@ -542,7 +538,7 @@ class Debugger(object):
# Public callback
def on_exception(self, exception):
"""Called on exception event other that known breakpoint. ``exception`` is one of the following type:
"""Called on exception event other that known breakpoint or requested single step. ``exception`` is one of the following type:
* :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO32`
* :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO64`
@@ -555,6 +551,13 @@ class Debugger(object):
return DBG_CONTINUE
def on_single_step(self, exception):
"""Called on requested single step``exception`` is one of the following type:
* :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO32`
* :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO64`
There is no default implementation, if you use ``Debugger.single_step()`` you should implement ``on_single_step``
"""
raise NotImplementedError("Debugger that explicitly single step should implement <on_single_step>")
def on_create_process(self, create_process):
@@ -589,10 +592,7 @@ class Debugger(object):
"""Called on rip_info event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680587(v=vs.85).aspx)"""
pass
def debug(path, args=None, dwCreationFlags=0, show_windows=False):
dwCreationFlags |= DEBUG_PROCESS
c = windows.utils.create_process(path, args=args, dwCreationFlags=dwCreationFlags, show_windows=show_windows)
return Debugger(c)
class Breakpoint(object):
-1
View File
@@ -547,7 +547,6 @@ class NativeUtilsTestCase(unittest.TestCase):
# Put name in test to know which function caused the assert fails
self.assertEqual((name, hex(addr)), (name, hex(compute_addr)))
self.assertEqual(getprocaddr32("YOLO.DLL", "whatever"), 0xfffffffe)
self.assertEqual(getprocaddr32("KERNEL32.DLL", "YOLOAPI"), 0xffffffff)
-1
View File
@@ -372,7 +372,6 @@ class DebuggerTestCase(unittest.TestCase):
def test_memory_breakpoint_write(self):
"""Check MemoryBP WRITE"""
TEST_CASE = self
store_data = [0]
class TSTBP(windows.debug.MemoryBreakpoint):
+2 -1
View File
@@ -1,6 +1,7 @@
"""utils fonctions non windows-related"""
import ctypes
import _ctypes
from windows.generated_def import Flag
def fixedpropety(f):
@@ -40,7 +41,7 @@ def print_ctypes_struct(struct, name="", ident=0, hexa=False):
if isinstance(value, basestring):
value = repr(value)
if hexa:
if hexa and not isinstance(value, Flag):
try:
print("{0} -> {1}".format(name, hex(value)))
return