| Home | Trees | Indices | Help |
|
|---|
|
|
1 # Copyright (c) 2009, Mario Vilas
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 #
7 # * Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above copyright
10 # notice,this list of conditions and the following disclaimer in the
11 # documentation and/or other materials provided with the distribution.
12 # * Neither the name of the copyright holder nor the names of its
13 # contributors may be used to endorse or promote products derived from
14 # this software without specific prior written permission.
15 #
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 # POSSIBILITY OF SUCH DAMAGE.
27
28 """
29 Event handling module.
30
31 @see: U{http://apps.sourceforge.net/trac/winappdbg/wiki/Debugging}
32
33 @group Event objects:
34 Event,
35 NoEvent,
36 CreateProcessEvent,
37 CreateThreadEvent,
38 ExitProcessEvent,
39 ExitThreadEvent,
40 LoadDLLEvent,
41 UnloadDLLEvent,
42 OutputDebugStringEvent,
43 RIPEvent,
44 ExceptionEvent
45 """
46
47 __revision__ = "$Id: event.py 415 2009-09-29 15:38:12Z QvasiModo $"
48
49 __all__ = [
50 # Factory of Event objects and all of it's subclasses.
51 # Users should not need to instance Event objects directly.
52 'EventFactory',
53
54 # Base class for user-defined event handlers.
55 'EventHandler',
56
57 # Dummy event object that can be used as a placeholder.
58 # It's never returned by the EventFactory.
59 'NoEvent',
60 ]
61
62 import win32
63 from win32 import FileHandle, ProcessHandle, ThreadHandle
64 from breakpoint import ApiHook
65 from system import Module, Thread, Process, PathOperations
66 from textio import HexDump
67
68 import ctypes
69
70 #==============================================================================
71
72 -class Event (object):
73 """
74 Event object.
75
76 @type eventMethod: str
77 @cvar eventMethod:
78 Method name to call when using L{EventHandler} subclasses.
79
80 @type eventName: str
81 @cvar eventName:
82 User-friendly name of the event.
83
84 @type eventDescription: str
85 @cvar eventDescription:
86 User-friendly description of the event.
87
88 @type debug: L{Debug}
89 @ivar debug:
90 Debug object that received the event.
91
92 @type raw: L{DEBUG_EVENT}
93 @ivar raw:
94 Raw DEBUG_EVENT structure as used by the Win32 API.
95
96 @type continueStatus: int
97 @ivar continueStatus:
98 Continue status to pass to L{win32.ContinueDebugEvent}.
99 """
100
101 eventMethod = 'unknown_event'
102 eventName = 'Unknown event'
103 eventDescription = 'A debug event of an unknown type has occured.'
104
106 """
107 @type debug: L{Debug}
108 @param debug: Debug object that received the event.
109
110 @type raw: L{DEBUG_EVENT}
111 @param raw: Raw DEBUG_EVENT structure as used by the Win32 API.
112 """
113 self.debug = debug
114 self.raw = raw
115 self.continueStatus = win32.DBG_EXCEPTION_NOT_HANDLED
116
118 """
119 @rtype: str
120 @return: User-friendly name of the event.
121 """
122 return self.eventName
123
125 """
126 @rtype: str
127 @return: User-friendly description of the event.
128 """
129 return self.eventDescription
130
132 """
133 @rtype: int
134 @return: Debug event code as defined in the Win32 API.
135 """
136 return self.raw.dwDebugEventCode
137
138 # Compatibility with version 1.0
139 # XXX to be removed in version 1.3
141 """
142 Alias of L{get_event_code} for backwards compatibility
143 with WinAppDbg version 1.0.
144 Will be phased out in the next version.
145
146 @rtype: int
147 @return: Debug event code as defined in the Win32 API.
148 """
149 return self.get_event_code()
150
152 """
153 @see: L{get_process}
154
155 @rtype: int
156 @return: Process global ID where the event occured.
157 """
158 return self.raw.dwProcessId
159
161 """
162 @see: L{get_thread}
163
164 @rtype: int
165 @return: Thread global ID where the event occured.
166 """
167 return self.raw.dwThreadId
168
170 """
171 @see: L{get_pid}
172
173 @rtype: L{Process}
174 @return: Process where the event occured.
175 """
176 # We can't assume the Process object will be in the System snapshot.
177 # The user may have cleared or otherwise modified the snapshot.
178 # Also some process creation events are missed on Wine.
179 pid = self.get_pid()
180 system = self.debug.system
181 if system.has_process(pid):
182 process = system.get_process(pid)
183 else:
184 ## print "Process notification missed! ID: %x" % pid # XXX
185 process = Process(pid)
186 system._ProcessContainer__add_process(process)
187 ## process.scan_threads() # not needed
188 process.scan_modules()
189 return process
190
192 """
193 @see: L{get_tid}
194
195 @rtype: L{Thread}
196 @return: Thread where the event occured.
197 """
198 # We can't assume the Thread object will be in the Process snapshot.
199 # See the comments of get_process.
200 tid = self.get_tid()
201 process = self.get_process()
202 if process.has_thread(tid):
203 thread = process.get_thread(tid)
204 else:
205 ## print "Thread notification missed! ID: %x" % tid # XXX
206 thread = Thread(tid)
207 process._ThreadContainer__add_thread(thread)
208 return thread
209
210 #==============================================================================
211
212 -class NoEvent (Event):
213 """
214 No event.
215
216 Dummy L{Event} object that can be used as a placeholder when no debug
217 event has occured yet. It's never returned by the L{EventFactory}.
218 """
219
220 eventMethod = 'no_event'
221 eventName = 'No event'
222 eventDescription = 'No debug event has occured.'
223
226
228 """
229 Always returns C{0}, so when evaluating the object as a boolean it's
230 always C{False}. This prevents L{Debug.cont} from trying to continue
231 a dummy event.
232 """
233 return 0
234
237
240
243
246
249
250 #==============================================================================
251
252 -class ExceptionEvent (Event):
253 """
254 Exception event.
255
256 @type exceptionName: dict( int S{->} str )
257 @cvar exceptionName:
258 Mapping of exception constants to their names.
259
260 @type exceptionDescription: dict( int S{->} str )
261 @cvar exceptionDescription:
262 Mapping of exception constants to user-friendly strings.
263
264 @type breakpoint: L{Breakpoint}
265 @ivar breakpoint:
266 If the exception was caused by one of our breakpoints, this member
267 contains a reference to the breakpoint object. Otherwise it's not
268 defined. It should only be used from the condition or action callback
269 routines, instead of the event handler.
270
271 @type hook: L{Hook}
272 @ivar hook:
273 If the exception was caused by a function hook, this member contains a
274 reference to the hook object. Otherwise it's not defined. It should
275 only be used from the hook callback routines, instead of the event
276 handler.
277 """
278
279 eventName = 'Exception event'
280 eventDescription = 'An exception was raised by the debugee.'
281
282 __exceptionMethod = {
283 win32.EXCEPTION_ACCESS_VIOLATION : 'access_violation',
284 win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED : 'array_bounds_exceeded',
285 win32.EXCEPTION_BREAKPOINT : 'breakpoint',
286 win32.EXCEPTION_DATATYPE_MISALIGNMENT : 'datatype_misalignment',
287 win32.EXCEPTION_FLT_DENORMAL_OPERAND : 'float_denormal_operand',
288 win32.EXCEPTION_FLT_DIVIDE_BY_ZERO : 'float_divide_by_zero',
289 win32.EXCEPTION_FLT_INEXACT_RESULT : 'float_inexact_result',
290 win32.EXCEPTION_FLT_INVALID_OPERATION : 'float_invalid_operation',
291 win32.EXCEPTION_FLT_OVERFLOW : 'float_overflow',
292 win32.EXCEPTION_FLT_STACK_CHECK : 'float_stack_check',
293 win32.EXCEPTION_FLT_UNDERFLOW : 'float_underflow',
294 win32.EXCEPTION_ILLEGAL_INSTRUCTION : 'illegal_instruction',
295 win32.EXCEPTION_IN_PAGE_ERROR : 'in_page_error',
296 win32.EXCEPTION_INT_DIVIDE_BY_ZERO : 'integer_divide_by_zero',
297 win32.EXCEPTION_INT_OVERFLOW : 'integer_overflow',
298 win32.EXCEPTION_INVALID_DISPOSITION : 'invalid_disposition',
299 win32.EXCEPTION_NONCONTINUABLE_EXCEPTION : 'noncontinuable_exception',
300 win32.EXCEPTION_PRIV_INSTRUCTION : 'privileged_instruction',
301 win32.EXCEPTION_SINGLE_STEP : 'single_step',
302 win32.EXCEPTION_STACK_OVERFLOW : 'stack_overflow',
303 win32.EXCEPTION_GUARD_PAGE : 'guard_page',
304 win32.EXCEPTION_INVALID_HANDLE : 'invalid_handle',
305 win32.EXCEPTION_POSSIBLE_DEADLOCK : 'possible_deadlock',
306 win32.EXCEPTION_WX86_BREAKPOINT : 'wow64_breakpoint',
307 win32.CONTROL_C_EXIT : 'control_c_exit',
308 win32.DBG_CONTROL_C : 'debug_control_c',
309 win32.MS_VC_EXCEPTION : 'ms_vc_exception',
310 }
311
312 __exceptionName = {
313 win32.EXCEPTION_ACCESS_VIOLATION : 'EXCEPTION_ACCESS_VIOLATION',
314 win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED : 'EXCEPTION_ARRAY_BOUNDS_EXCEEDED',
315 win32.EXCEPTION_BREAKPOINT : 'EXCEPTION_BREAKPOINT',
316 win32.EXCEPTION_DATATYPE_MISALIGNMENT : 'EXCEPTION_DATATYPE_MISALIGNMENT',
317 win32.EXCEPTION_FLT_DENORMAL_OPERAND : 'EXCEPTION_FLT_DENORMAL_OPERAND',
318 win32.EXCEPTION_FLT_DIVIDE_BY_ZERO : 'EXCEPTION_FLT_DIVIDE_BY_ZERO',
319 win32.EXCEPTION_FLT_INEXACT_RESULT : 'EXCEPTION_FLT_INEXACT_RESULT',
320 win32.EXCEPTION_FLT_INVALID_OPERATION : 'EXCEPTION_FLT_INVALID_OPERATION',
321 win32.EXCEPTION_FLT_OVERFLOW : 'EXCEPTION_FLT_OVERFLOW',
322 win32.EXCEPTION_FLT_STACK_CHECK : 'EXCEPTION_FLT_STACK_CHECK',
323 win32.EXCEPTION_FLT_UNDERFLOW : 'EXCEPTION_FLT_UNDERFLOW',
324 win32.EXCEPTION_ILLEGAL_INSTRUCTION : 'EXCEPTION_ILLEGAL_INSTRUCTION',
325 win32.EXCEPTION_IN_PAGE_ERROR : 'EXCEPTION_IN_PAGE_ERROR',
326 win32.EXCEPTION_INT_DIVIDE_BY_ZERO : 'EXCEPTION_INT_DIVIDE_BY_ZERO',
327 win32.EXCEPTION_INT_OVERFLOW : 'EXCEPTION_INT_OVERFLOW',
328 win32.EXCEPTION_INVALID_DISPOSITION : 'EXCEPTION_INVALID_DISPOSITION',
329 win32.EXCEPTION_NONCONTINUABLE_EXCEPTION : 'EXCEPTION_NONCONTINUABLE_EXCEPTION',
330 win32.EXCEPTION_PRIV_INSTRUCTION : 'EXCEPTION_PRIV_INSTRUCTION',
331 win32.EXCEPTION_SINGLE_STEP : 'EXCEPTION_SINGLE_STEP',
332 win32.EXCEPTION_STACK_OVERFLOW : 'EXCEPTION_STACK_OVERFLOW',
333 win32.EXCEPTION_GUARD_PAGE : 'EXCEPTION_GUARD_PAGE',
334 win32.EXCEPTION_INVALID_HANDLE : 'EXCEPTION_INVALID_HANDLE',
335 win32.EXCEPTION_POSSIBLE_DEADLOCK : 'EXCEPTION_POSSIBLE_DEADLOCK',
336 win32.EXCEPTION_WX86_BREAKPOINT : 'EXCEPTION_WX86_BREAKPOINT',
337 win32.CONTROL_C_EXIT : 'CONTROL_C_EXIT',
338 win32.DBG_CONTROL_C : 'DBG_CONTROL_C',
339 win32.MS_VC_EXCEPTION : 'MS_VC_EXCEPTION',
340 }
341
342 __exceptionDescription = {
343 win32.EXCEPTION_ACCESS_VIOLATION : 'Access violation',
344 win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED : 'Array bounds exceeded',
345 win32.EXCEPTION_BREAKPOINT : 'Breakpoint',
346 win32.EXCEPTION_DATATYPE_MISALIGNMENT : 'Datatype misalignment',
347 win32.EXCEPTION_FLT_DENORMAL_OPERAND : 'Float denormal operand',
348 win32.EXCEPTION_FLT_DIVIDE_BY_ZERO : 'Float divide by zero',
349 win32.EXCEPTION_FLT_INEXACT_RESULT : 'Float inexact result',
350 win32.EXCEPTION_FLT_INVALID_OPERATION : 'Float invalid operation',
351 win32.EXCEPTION_FLT_OVERFLOW : 'Float overflow',
352 win32.EXCEPTION_FLT_STACK_CHECK : 'Float stack check',
353 win32.EXCEPTION_FLT_UNDERFLOW : 'Float underflow',
354 win32.EXCEPTION_ILLEGAL_INSTRUCTION : 'Illegal instruction',
355 win32.EXCEPTION_IN_PAGE_ERROR : 'In-page error',
356 win32.EXCEPTION_INT_DIVIDE_BY_ZERO : 'Integer divide by zero',
357 win32.EXCEPTION_INT_OVERFLOW : 'Integer overflow',
358 win32.EXCEPTION_INVALID_DISPOSITION : 'Invalid disposition',
359 win32.EXCEPTION_NONCONTINUABLE_EXCEPTION : 'Noncontinuable exception',
360 win32.EXCEPTION_PRIV_INSTRUCTION : 'Privileged instruction',
361 win32.EXCEPTION_SINGLE_STEP : 'Single step event',
362 win32.EXCEPTION_STACK_OVERFLOW : 'Stack limits overflow',
363 win32.EXCEPTION_GUARD_PAGE : 'Guard page hit',
364 win32.EXCEPTION_INVALID_HANDLE : 'Invalid handle',
365 win32.EXCEPTION_POSSIBLE_DEADLOCK : 'Possible deadlock',
366 win32.EXCEPTION_WX86_BREAKPOINT : 'WOW64 breakpoint',
367 win32.CONTROL_C_EXIT : 'Control-C exit',
368 win32.DBG_CONTROL_C : 'Debug Control-C',
369 win32.MS_VC_EXCEPTION : 'Microsoft Visual C exception',
370 }
371
372 @property
376
378 """
379 @rtype: str
380 @return: Name of the exception as defined by the Win32 API.
381 """
382 code = self.get_exception_code()
383 unk = HexDump.integer(code)
384 return self.__exceptionName.get(code, unk)
385
387 """
388 @rtype: str
389 @return: User-friendly name of the exception.
390 """
391 code = self.get_exception_code()
392 unk = 'C++ exception %s' % HexDump.integer(code)
393 return self.__exceptionDescription.get(code, unk)
394
396 """
397 @rtype: bool
398 @return: True for first chance exceptions, False for last chance.
399 """
400 return self.raw.u.Exception.dwFirstChance != win32.FALSE
401
403 """
404 @rtype: bool
405 @return: The opposite of L{is_first_chance}.
406 """
407 return not self.is_first_chance()
408
410 """
411 @see: U{http://msdn.microsoft.com/en-us/library/aa363082(VS.85).aspx}
412
413 @rtype: bool
414 @return: True if the exception is noncontinuable.
415
416 Attempting to continue a noncontinuable exception results in an
417 EXCEPTION_NONCONTINUABLE_EXCEPTION exception to be raised.
418 """
419 return bool( self.raw.u.Exception.ExceptionRecord.ExceptionFlags & \
420 win32.EXCEPTION_NONCONTINUABLE )
421
423 """
424 @rtype: bool
425 @return: The opposite of L{is_noncontinuable}.
426 """
427 return not self.is_noncontinuable()
428
430 """
431 @rtype: int
432 @return: Exception code as defined by the Win32 API.
433 """
434 return self.raw.u.Exception.ExceptionRecord.ExceptionCode
435
437 """
438 @rtype: int
439 @return: Memory address where the exception occured.
440 """
441 return self.raw.u.Exception.ExceptionRecord.ExceptionAddress
442
444 """
445 @type index: int
446 @param index: Index into the exception information block.
447
448 @rtype: int
449 @return: Exception information DWORD.
450 """
451 if index < 0 or index > win32.EXCEPTION_MAXIMUM_PARAMETERS:
452 raise IndexError, "Array index out of range: %s" % repr(index)
453 info = self.raw.u.Exception.ExceptionRecord.ExceptionInformation
454 return info[index]
455
457 """
458 @rtype: list( int )
459 @return: Exception information block.
460 """
461 info = self.raw.u.Exception.ExceptionRecord.ExceptionInformation
462 return [ info[i] \
463 for i in xrange(0, win32.EXCEPTION_MAXIMUM_PARAMETERS) ]
464
466 """
467 @rtype: int
468 @return: Access violation type.
469 Should be one of the following constants:
470
471 - L{win32.EXCEPTION_READ_FAULT}
472 - L{win32.EXCEPTION_WRITE_FAULT}
473 - L{win32.EXCEPTION_EXECUTE_FAULT}
474
475 @note: This method is only meaningful for access violation exceptions,
476 in-page memory error exceptions and guard page exceptions.
477
478 @raise NotImplementedError: Wrong kind of exception.
479 """
480 if self.get_exception_code() not in (win32.EXCEPTION_ACCESS_VIOLATION,
481 win32.EXCEPTION_IN_PAGE_ERROR, win32.EXCEPTION_GUARD_PAGE):
482 msg = "This method is not meaningful for %s."
483 raise NotImplementedError, msg % self.get_exception_name()
484 return self.get_exception_information(0)
485
487 """
488 @rtype: int
489 @return: Access violation memory address.
490
491 @note: This method is only meaningful for access violation exceptions,
492 in-page memory error exceptions and guard page exceptions.
493
494 @raise NotImplementedError: Wrong kind of exception.
495 """
496 if self.get_exception_code() not in (win32.EXCEPTION_ACCESS_VIOLATION,
497 win32.EXCEPTION_IN_PAGE_ERROR, win32.EXCEPTION_GUARD_PAGE):
498 msg = "This method is not meaningful for %s."
499 raise NotImplementedError, msg % self.get_exception_name()
500 return self.get_exception_information(1)
501
503 """
504 @rtype: int
505 @return: NTSTATUS status code that caused the exception.
506
507 @note: This method is only meaningful for in-page memory error
508 exceptions.
509
510 @raise NotImplementedError: Not an in-page memory error.
511 """
512 if self.get_exception_code() != win32.EXCEPTION_IN_PAGE_ERROR:
513 msg = "This method is only meaningful "\
514 "for in-page memory error exceptions."
515 raise NotImplementedError, msg
516 return self.get_exception_information(2)
517
519 """
520 Traverses the exception record linked list and builds a Python list.
521
522 Nested exception records are received for nested exceptions. This
523 happens when an exception is raised in the debugee while trying to
524 handle a previous exception.
525
526 @rtype: list( L{win32.EXCEPTION_RECORD} )
527 @return:
528 List of raw exception record structures as used by the Win32 API.
529
530 There is always at least one exception record, so the list is
531 never empty. All other methods of this class read from the first
532 exception record only, that is, the most recent exception.
533 """
534
535 # The first EXCEPTION_RECORD is contained in EXCEPTION_DEBUG_INFO.
536 record = self.raw.u.Exception.ExceptionRecord
537 nested = [ record ]
538
539 # The remaining EXCEPTION_RECORD structures are linked by pointers.
540 while record.ExceptionRecord:
541 record = record.ExceptionRecord.contents
542 nested.append(record)
543
544 # Return the list of nested exceptions.
545 return nested
546
547 # TODO
548 # Return the nested exceptions as a list of ExceptionEvent objects.
549 # The first element is always "self".
550 # Each element contains a pointer to the next exception record.
551 # New raw structures may have to be allocated for each object,
552 # but it's OK to reuse pointers since they're supposed to be read only.
553 # (Perhaps a custom DEBUG_EVENT has to be defined, where the first record
554 # is a pointer instead of an embedded structure).
555 ## def get_nested_exceptions(self):
556
557 #==============================================================================
558
559 -class CreateThreadEvent (Event):
560 """
561 Thread creation event.
562 """
563
564 eventMethod = 'create_thread'
565 eventName = 'Thread creation event'
566 eventDescription = 'A new thread has started.'
567
569 """
570 @rtype: L{ThreadHandle}
571 @return: Thread handle received from the system.
572 If it's a valid handle, a new L{ThreadHandle} object is created.
573 Otherwise, the method returns C{INVALID_HANDLE_VALUE}.
574 @note: This method never returns C{NULL}.
575 """
576 # The handle doesn't need to be closed.
577 # See http://msdn.microsoft.com/en-us/library/ms681423(VS.85).aspx
578 hThread = self.raw.u.CreateThread.hThread
579 if hThread == win32.NULL:
580 hThread = win32.INVALID_HANDLE_VALUE
581 elif hThread != win32.INVALID_HANDLE_VALUE:
582 hThread = ThreadHandle(hThread, False)
583 return hThread
584
586 """
587 @rtype: int
588 @return: Pointer to the TEB.
589 """
590 return self.raw.u.CreateThread.lpThreadLocalBase
591
593 """
594 @rtype: int
595 @return: Pointer to the first instruction to execute in this thread.
596
597 Returns C{NULL} when the debugger attached to a process
598 and the thread already existed.
599
600 See U{http://msdn.microsoft.com/en-us/library/ms679295(VS.85).aspx}
601 """
602 return self.raw.u.CreateThread.lpStartAddress
603
604 #==============================================================================
605
606 -class CreateProcessEvent (Event):
607 """
608 Process creation event.
609 """
610
611 eventMethod = 'create_process'
612 eventName = 'Process creation event'
613 eventDescription = 'A new process has started.'
614
616 """
617 @rtype: L{FileHandle}
618 @return: File handle to the main module.
619 If it's a valid handle, a new L{FileHandle} object is created.
620 Otherwise, the method returns C{INVALID_HANDLE_VALUE}.
621 @note: This method never returns C{NULL}.
622 """
623 # This handle DOES need to be closed.
624 # Therefore we must cache it so it doesn't
625 # get closed after the first call.
626 if hasattr(self, '_CreateProcessEvent__hFile'):
627 hFile = self.__hFile
628 else:
629 hFile = self.raw.u.CreateProcessInfo.hFile
630 if hFile == win32.NULL:
631 hFile = win32.INVALID_HANDLE_VALUE
632 elif hFile != win32.INVALID_HANDLE_VALUE:
633 hFile = FileHandle(hFile, True)
634 self.__hFile = hFile
635 return hFile
636
638 """
639 @rtype: L{ProcessHandle}
640 @return: Process handle received from the system.
641 If it's a valid handle, a new L{ProcessHandle} object is created.
642 Otherwise, the method returns C{INVALID_HANDLE_VALUE}.
643 @note: This method never returns C{NULL}.
644 """
645 # The handle doesn't need to be closed.
646 # See http://msdn.microsoft.com/en-us/library/ms681423(VS.85).aspx
647 hProcess = self.raw.u.CreateProcessInfo.hProcess
648 if hProcess == win32.NULL:
649 hProcess = win32.INVALID_HANDLE_VALUE
650 elif hProcess != win32.INVALID_HANDLE_VALUE:
651 hProcess = ProcessHandle(hProcess, False)
652 return hProcess
653
655 """
656 @rtype: L{ThreadHandle}
657 @return: Thread handle received from the system.
658 If it's a valid handle, a new L{ThreadHandle} object is created.
659 Otherwise, the method returns C{INVALID_HANDLE_VALUE}.
660 @note: This method never returns C{NULL}.
661 """
662 # The handle doesn't need to be closed.
663 # See http://msdn.microsoft.com/en-us/library/ms681423(VS.85).aspx
664 hThread = self.raw.u.CreateProcessInfo.hThread
665 if hThread == win32.NULL:
666 hThread = win32.INVALID_HANDLE_VALUE
667 elif hThread != win32.INVALID_HANDLE_VALUE:
668 hThread = ThreadHandle(hThread, False)
669 return hThread
670
672 """
673 @rtype: int
674 @return: Pointer to the first instruction to execute in this process.
675
676 Returns C{NULL} when the debugger attaches to a process.
677
678 See U{http://msdn.microsoft.com/en-us/library/ms679295(VS.85).aspx}
679 """
680 return self.raw.u.CreateProcessInfo.lpStartAddress
681
683 """
684 @rtype: int
685 @return: Base address of the main module.
686 """
687 return self.raw.u.CreateProcessInfo.lpBaseOfImage
688
690 """
691 @rtype: int
692 @return: Pointer to the TEB.
693 """
694 return self.raw.u.CreateProcessInfo.lpThreadLocalBase
695
697 """
698 @rtype: str
699 @return: Debugging information.
700 """
701 raw = self.raw.u.CreateProcessInfo
702 ptr = raw.lpBaseOfImage + raw.dwDebugInfoFileOffset
703 size = raw.nDebugInfoSize
704 data = self.get_process().peek(ptr, size)
705 if len(data) == size:
706 return data
707 return None
708
710 """
711 @rtype: str, None
712 @return: This method does it's best to retrieve the filename to
713 the main module of the process. However, sometimes that's not
714 possible, and C{None} is returned instead.
715 """
716
717 # Try to get the filename from the file handle.
718 szFilename = self.get_file_handle().get_filename()
719 if not szFilename:
720
721 # Try to get it from CREATE_PROCESS_DEBUG_INFO.lpImageName
722 # It's NULL or *NULL most of the times, see MSDN:
723 # http://msdn.microsoft.com/en-us/library/ms679286(VS.85).aspx
724 aProcess = self.get_process()
725 lpRemoteFilenamePtr = self.raw.u.CreateProcessInfo.lpImageName
726 if lpRemoteFilenamePtr:
727 lpFilename = aProcess.peek_uint(lpRemoteFilenamePtr)
728 fUnicode = bool( self.raw.u.CreateProcessInfo.fUnicode )
729 szFilename = aProcess.peek_string(lpFilename, fUnicode)
730
731 # Try to get it from Process.get_image_name().
732 if not szFilename:
733 szFilename = aProcess.get_image_name()
734
735 # Return the filename, or None on error.
736 return szFilename
737
739 """
740 @rtype: int
741 @return: Base address of the main module.
742 """
743 return self.get_image_base()
744
746 """
747 @rtype: L{Module}
748 @return: Main module of the process.
749 """
750 return self.get_process().get_module( self.get_module_base() )
751
752 #==============================================================================
753
754 -class ExitThreadEvent (Event):
755 """
756 Thread termination event.
757 """
758
759 eventMethod = 'exit_thread'
760 eventName = 'Thread termination event'
761 eventDescription = 'A thread has finished executing.'
762
764 """
765 @rtype: int
766 @return: Exit code of the thread.
767 """
768 return self.raw.u.ExitThread.dwExitCode
769
770 #==============================================================================
771
772 -class ExitProcessEvent (Event):
773 """
774 Process termination event.
775 """
776
777 eventMethod = 'exit_process'
778 eventName = 'Process termination event'
779 eventDescription = 'A process has finished executing.'
780
782 """
783 @rtype: int
784 @return: Exit code of the process.
785 """
786 return self.raw.u.ExitProcess.dwExitCode
787
789 """
790 @rtype: None or str
791 @return: Filename of the main module.
792 C{None} if the filename is unknown.
793 """
794 return self.get_module().get_filename()
795
797 """
798 @rtype: int
799 @return: Base address of the main module.
800 """
801 return self.get_module_base()
802
804 """
805 @rtype: int
806 @return: Base address of the main module.
807 """
808 return self.get_module().get_base()
809
811 """
812 @rtype: L{Module}
813 @return: Main module of the process.
814 """
815 return self.get_process().get_main_module()
816
817 #==============================================================================
818
819 -class LoadDLLEvent (Event):
820 """
821 Module load event.
822 """
823
824 eventMethod = 'load_dll'
825 eventName = 'Module load event'
826 eventDescription = 'A new DLL library was loaded by the debugee.'
827
829 """
830 @rtype: int
831 @return: Base address for the newly loaded DLL.
832 """
833 return self.raw.u.LoadDll.lpBaseOfDll
834
836 """
837 @rtype: L{Module}
838 @return: Module object for the newly loaded DLL.
839 """
840 lpBaseOfDll = self.get_module_base()
841 aProcess = self.get_process()
842 if aProcess.has_module(lpBaseOfDll):
843 aModule = aProcess.get_module(lpBaseOfDll)
844 else:
845 aModule = Module(lpBaseOfDll,
846 hFile = self.get_file_handle(),
847 fileName = get_filename(),
848 process = aProcess)
849 aProcess.__ModuleContainer_add_module(aModule)
850 return aModule
851
853 """
854 @rtype: L{FileHandle}
855 @return: File handle to the newly loaded DLL.
856 If it's a valid handle, a new L{FileHandle} object is created.
857 Otherwise, the method returns C{INVALID_HANDLE_VALUE}.
858 @note: This method never returns C{NULL}.
859 """
860 # This handle DOES need to be closed.
861 # Therefore we must cache it so it doesn't
862 # get closed after the first call.
863 try:
864 hFile = self.__hFile
865 except AttributeError:
866 hFile = self.raw.u.LoadDll.hFile
867 if hFile == win32.NULL:
868 hFile = win32.INVALID_HANDLE_VALUE
869 elif hFile != win32.INVALID_HANDLE_VALUE:
870 hFile = FileHandle(hFile, True)
871 self.__hFile = hFile
872 return hFile
873
875 """
876 @rtype: str, None
877 @return: This method does it's best to retrieve the filename to
878 the newly loaded module. However, sometimes that's not
879 possible, and C{None} is returned instead.
880 """
881
882 # Try to get the filename from the file handle.
883 szFilename = self.get_file_handle().get_filename()
884 if not szFilename:
885
886 # Try to get it from LOAD_DLL_DEBUG_INFO.lpImageName
887 # It's NULL or *NULL most of the times, see MSDN:
888 # http://msdn.microsoft.com/en-us/library/ms679286(VS.85).aspx
889 aProcess = self.get_process()
890 lpRemoteFilenamePtr = self.raw.u.LoadDll.lpImageName
891 if lpRemoteFilenamePtr:
892 lpFilename = aProcess.peek_uint(lpRemoteFilenamePtr)
893 fUnicode = bool( self.raw.u.LoadDll.fUnicode )
894 szFilename = aProcess.peek_string(lpFilename, fUnicode)
895 if not szFilename:
896 szFilename = None
897
898 # Return the filename, or None on error.
899 return szFilename
900
901 #==============================================================================
902
903 -class UnloadDLLEvent (Event):
904 """
905 Module unload event.
906 """
907
908 eventMethod = 'unload_dll'
909 eventName = 'Module unload event'
910 eventDescription = 'A DLL library was unloaded by the debugee.'
911
913 """
914 @rtype: int
915 @return: Base address for the recently unloaded DLL.
916 """
917 return self.raw.u.UnloadDll.lpBaseOfDll
918
920 """
921 @rtype: L{Module}
922 @return: Module object for the recently unloaded DLL.
923 """
924 lpBaseOfDll = self.get_module_base()
925 aProcess = self.get_process()
926 if aProcess.has_module(lpBaseOfDll):
927 aModule = aProcess.get_module(lpBaseOfDll)
928 else:
929 aModule = Module(lpBaseOfDll, process = aProcess)
930 aProcess._ModuleContainer__add_module(aModule)
931 return aModule
932
934 """
935 @rtype: None or L{FileHandle}
936 @return: File handle to the recently unloaded DLL.
937 Returns C{None} if the handle is not available.
938 """
939 hFile = self.get_module().hFile
940 if hFile == win32.INVALID_HANDLE_VALUE:
941 hFile = None
942 return hFile
943
945 """
946 @rtype: None or str
947 @return: Filename of the recently unloaded DLL.
948 C{None} if the filename is unknown.
949 """
950 return self.get_module().get_filename()
951
952 #==============================================================================
953
954 -class OutputDebugStringEvent (Event):
955 """
956 Debug string output event.
957 """
958
959 eventMethod = 'output_string'
960 eventName = 'Debug string output event'
961 eventDescription = 'The debugee sent a message to the debugger.'
962
964 """
965 @rtype: str, unicode
966 @return: String sent by the debugee.
967 It may be ANSI or Unicode and may end with a null character.
968 """
969 return self.get_process().peek_string(
970 self.raw.u.DebugString.lpDebugStringData,
971 bool( self.raw.u.DebugString.fUnicode ),
972 self.raw.u.DebugString.nDebugStringLength)
973
974 #==============================================================================
975
976 -class RIPEvent (Event):
977 """
978 RIP event.
979 """
980
981 eventMethod = 'rip'
982 eventName = 'RIP event'
983 eventDescription = 'An error has occured and the process ' \
984 'can no longer be debugged.'
985
987 """
988 @rtype: int
989 @return: RIP error code as defined by the Win32 API.
990 """
991 return self.raw.u.RipInfo.dwError
992
999
1000 #==============================================================================
1001
1002 -class EventFactory (object):
1003 """
1004 Factory of L{Event} objects.
1005
1006 @type baseEvent: L{Event}
1007 @cvar baseEvent:
1008 Base class for Event objects.
1009 It's used for unknown event codes.
1010
1011 @type eventClasses: dict( int S{->} L{Event} )
1012 @cvar eventClasses:
1013 Dictionary that maps event codes to L{Event} subclasses.
1014 """
1015
1016 baseEvent = Event
1017 eventClasses = {
1018 win32.EXCEPTION_DEBUG_EVENT : ExceptionEvent, # 1
1019 win32.CREATE_THREAD_DEBUG_EVENT : CreateThreadEvent, # 2
1020 win32.CREATE_PROCESS_DEBUG_EVENT : CreateProcessEvent, # 3
1021 win32.EXIT_THREAD_DEBUG_EVENT : ExitThreadEvent, # 4
1022 win32.EXIT_PROCESS_DEBUG_EVENT : ExitProcessEvent, # 5
1023 win32.LOAD_DLL_DEBUG_EVENT : LoadDLLEvent, # 6
1024 win32.UNLOAD_DLL_DEBUG_EVENT : UnloadDLLEvent, # 7
1025 win32.OUTPUT_DEBUG_STRING_EVENT : OutputDebugStringEvent, # 8
1026 win32.RIP_EVENT : RIPEvent, # 9
1027 }
1028
1029 @classmethod
1031 """
1032 @type debug: L{Debug}
1033 @param debug: Debug object that received the event.
1034
1035 @type raw: L{DEBUG_EVENT}
1036 @param raw: Raw DEBUG_EVENT structure as used by the Win32 API.
1037
1038 @rtype: L{Event}
1039 @returns: An Event object or one of it's subclasses,
1040 depending on the event type.
1041 """
1042 eventClass = cls.eventClasses.get(raw.dwDebugEventCode, cls.baseEvent)
1043 return eventClass(debug, raw)
1044
1046 """
1047 C{EventFactory} is a singleton, you can't really have multiple
1048 instances of it. To create this effect, the C{__new__} operator
1049 was overriden to return always the I{class} object instead
1050 of new I{instances}.
1051
1052 @rtype: L{EventFactory}
1053 @return: C{EventFactory} class (NOT an instance)
1054 """
1055 return EventFactory
1056
1057 #==============================================================================
1058
1059 -class EventHandler (object):
1060 """
1061 Base class for debug event handlers.
1062
1063 Your program should subclass it to implement it's own event handling.
1064
1065 The signature for event handlers is the following::
1066
1067 def event_handler(self, event):
1068
1069 Where B{event} is an L{Event} object.
1070
1071 Each event handler is named after the event they handle.
1072 This is the list of all valid event handler names:
1073
1074 - I{event}
1075
1076 Receives an L{Event} object or an object of any of it's subclasses,
1077 and handles any event for which no handler was defined.
1078
1079 - I{unknown_event}
1080
1081 Receives an L{Event} object or an object of any of it's subclasses,
1082 and handles any event unknown to the debugging engine. (This is not
1083 likely to happen unless the Win32 debugging API is changed in future
1084 versions of Windows).
1085
1086 - I{exception}
1087
1088 Receives an L{ExceptionEvent} object and handles any exception for
1089 which no handler was defined. See above for exception handlers.
1090
1091 - I{unknown_exception}
1092
1093 Receives an L{ExceptionEvent} object and handles any exception unknown
1094 to the debugging engine. This usually happens for C++ exceptions, which
1095 are not standardized and may change from one compiler to the next.
1096
1097 Currently we have partial support for C++ exceptions thrown by Microsoft
1098 compilers.
1099
1100 Also see: U{RaiseException()
1101 <http://msdn.microsoft.com/en-us/library/ms680552(VS.85).aspx>}
1102
1103 - I{create_thread}
1104
1105 Receives a L{CreateThreadEvent} object.
1106
1107 - I{create_process}
1108
1109 Receives a L{CreateProcessEvent} object.
1110
1111 - I{exit_thread}
1112
1113 Receives a L{ExitThreadEvent} object.
1114
1115 - I{exit_process}
1116
1117 Receives a L{ExitProcessEvent} object.
1118
1119 - I{load_dll}
1120
1121 Receives a L{LoadDLLEvent} object.
1122
1123 - I{unload_dll}
1124
1125 Receives an L{UnloadDLLEvent} object.
1126
1127 - I{output_string}
1128
1129 Receives an L{OutputDebugStringEvent} object.
1130
1131 - I{rip}
1132
1133 Receives a L{RIPEvent} object.
1134
1135 This is the list of all valid exception handler names
1136 (they all receive an L{ExceptionEvent} object):
1137
1138 - I{access_violation}
1139 - I{array_bounds_exceeded}
1140 - I{breakpoint}
1141 - I{control_c_exit}
1142 - I{datatype_misalignment}
1143 - I{debug_control_c}
1144 - I{float_denormal_operand}
1145 - I{float_divide_by_zero}
1146 - I{float_inexact_result}
1147 - I{float_invalid_operation}
1148 - I{float_overflow}
1149 - I{float_stack_check}
1150 - I{float_underflow}
1151 - I{guard_page}
1152 - I{illegal_instruction}
1153 - I{in_page_error}
1154 - I{integer_divide_by_zero}
1155 - I{integer_overflow}
1156 - I{invalid_disposition}
1157 - I{invalid_handle}
1158 - I{ms_vc_exception}
1159 - I{noncontinuable_exception}
1160 - I{possible_deadlock}
1161 - I{privileged_instruction}
1162 - I{single_step}
1163 - I{stack_overflow}
1164
1165
1166
1167 @type apiHooks: dict( str S{->} tuple( str, int ) )
1168 @cvar apiHooks:
1169 Dictionary that maps module names to
1170 tuples of ( procedure name, parameter count ).
1171
1172 All procedures listed here will be hooked for calls from the debuguee.
1173 When this happens, the corresponding event handler is notified both
1174 when the procedure is entered and when it's left by the debugee.
1175
1176 For example, if the procedure name is "LoadLibraryEx" the event handler
1177 routines must be defined as "pre_LoadLibraryEx" and "post_LoadLibraryEx"
1178 in your class.
1179
1180 The signature for the routines can be something like this::
1181
1182 def pre_LoadLibraryEx(event, *params):
1183 ra = params[0] # return address
1184 argv = params[1:] # function parameters
1185
1186 # (...)
1187
1188 def post_LoadLibrary(event, return_value):
1189
1190 # (...)
1191
1192 But since you can also specify the number of arguments, this signature
1193 works too (four arguments in this case)::
1194
1195 def pre_LoadLibraryEx(event, ra, lpFilename, hFile, dwFlags):
1196 szFilename = event.get_process().peek_string(lpFilename)
1197
1198 # (...)
1199
1200 Note that the number of parameters to pull from the stack includes the
1201 return address. The apiHooks dictionary for the example above would
1202 look like this::
1203
1204 apiHook = {
1205
1206 "kernel32.dll" : (
1207
1208 # Procedure name Parameter count
1209 ( "LoadLibraryEx", 4 ),
1210
1211 # (more procedures can go here...)
1212 ),
1213
1214 # (more libraries can go here...)
1215 }
1216
1217 For a more complete support of API hooking, you can also check out
1218 Universal Hooker at U{http://oss.coresecurity.com/projects/uhooker.htm}
1219 """
1220
1221 #------------------------------------------------------------------------------
1222
1223 # Default (empty) API hooks dictionary.
1224 apiHooks = {}
1225
1227 # XXX HACK
1228 # This will be removed when hooks are supported in AMD64.
1229 if self.apiHooks and win32.CONTEXT.arch != 'i386':
1230 raise NotImplementedError, "Hooks are not yet implemented in 64 bits"
1231
1232 # Convert the tuples into instances of the ApiHook class.
1233 # A new dictionary must be instanced, otherwise we could also be
1234 # affecting all other instances of the EventHandler.
1235 self.__apiHooks = dict()
1236 for lib, hooks in self.apiHooks.iteritems():
1237 self.__apiHooks[lib] = [ ApiHook(self, *h) for h in hooks ]
1238
1240 """
1241 Hook the requested API calls (in self.apiHooks).
1242
1243 This method is called automatically whenever a DLL is loaded.
1244 """
1245 if self.__apiHooks:
1246 fileName = event.get_module().get_filename()
1247 if fileName:
1248 lib_name = PathOperations.pathname_to_filename(fileName).lower()
1249 for hook_lib, hook_api_list in self.__apiHooks.iteritems():
1250 if hook_lib == lib_name:
1251 for hook_api_stub in hook_api_list:
1252 hook_api_stub.hook(event.debug, event.get_pid(),
1253 lib_name)
1254
1256 """
1257 Dispatch debug events.
1258
1259 @type event: L{Event}
1260 @param event: Event object.
1261 """
1262 eventCode = event.get_event_code()
1263 if eventCode == win32.EXCEPTION_DEBUG_EVENT:
1264 method = getattr(self, 'exception', self.event)
1265 method = getattr(self, event.eventMethod, method)
1266 else:
1267 method = getattr(self, event.eventMethod, self.event)
1268 try:
1269 if eventCode == win32.LOAD_DLL_DEBUG_EVENT:
1270 self.__setApiHooksForDll(event)
1271 finally:
1272 return method(event)
1273
1282
1283 #==============================================================================
1284
1285 -class EventDispatcher (object):
1286 """
1287 Implements debug event dispatching capabilities.
1288 """
1289
1290 # Maps event code constants to the names of the pre-notify routines.
1291 # These routines are called BEFORE the user-defined handlers.
1292 # Unknown codes are ignored.
1293 __preEventNotifyCallbackName = {
1294 win32.CREATE_THREAD_DEBUG_EVENT : 'notify_create_thread',
1295 win32.CREATE_PROCESS_DEBUG_EVENT : 'notify_create_process',
1296 win32.LOAD_DLL_DEBUG_EVENT : 'notify_load_dll',
1297 }
1298
1299 # Maps event code constants to the names of the post-notify routines.
1300 # These routines are called AFTER the user-defined handlers.
1301 # Unknown codes are ignored.
1302 __postEventNotifyCallbackName = {
1303 win32.EXIT_THREAD_DEBUG_EVENT : 'notify_exit_thread',
1304 win32.EXIT_PROCESS_DEBUG_EVENT : 'notify_exit_process',
1305 win32.UNLOAD_DLL_DEBUG_EVENT : 'notify_unload_dll',
1306 win32.RIP_EVENT : 'notify_rip',
1307 }
1308
1309 # Maps exception code constants to the names of the pre-notify routines.
1310 # These routines are called BEFORE the user-defined handlers.
1311 # Unknown codes are ignored.
1312 __preExceptionNotifyCallbackName = {
1313 win32.EXCEPTION_BREAKPOINT : 'notify_breakpoint',
1314 win32.EXCEPTION_SINGLE_STEP : 'notify_single_step',
1315 win32.EXCEPTION_GUARD_PAGE : 'notify_guard_page',
1316 win32.DBG_CONTROL_C : 'notify_debug_control_c',
1317 win32.MS_VC_EXCEPTION : 'notify_ms_vc_exception',
1318 }
1319
1320 # Maps exception code constants to the names of the post-notify routines.
1321 # These routines are called AFTER the user-defined handlers.
1322 # Unknown codes are ignored.
1323 __postExceptionNotifyCallbackName = {
1324 }
1325
1327 """
1328 Event dispatcher.
1329
1330 @type eventHandler: L{EventHandler}
1331 @param eventHandler: (Optional) Event handler object.
1332
1333 @note: The L{eventHandler} parameter may be any callable Python object
1334 (for example a function, or an instance method).
1335 However you'll probably find it more convenient to use an instance
1336 of a subclass of L{EventHandler} here.
1337 """
1338 if eventHandler is not None and not callable(eventHandler):
1339 raise TypeError, "Invalid event handler"
1340 self.__eventHandler = eventHandler
1341
1343 """
1344 Sends event notifications to the L{Debug} object and
1345 the L{EventHandler} object provided by the user.
1346
1347 The L{Debug} object will forward the notifications to it's contained
1348 snapshot objects (L{System}, L{Process}, L{Thread} and L{Module}) when
1349 appropriate.
1350
1351 @warning: This method is called automatically from L{Debug.dispatch}.
1352
1353 @see: L{Debug.cont}, L{Debug.loop}, L{Debug.wait}
1354
1355 @type event: L{Event}
1356 @param event: Event object passed to L{Debug.dispatch}.
1357
1358 @raise WindowsError: Raises an exception on error.
1359 """
1360 returnValue = None
1361 bCallHandler = True
1362 pre_handler = None
1363 post_handler = None
1364 eventCode = event.get_code()
1365
1366 # Get the pre and post notification methods for exceptions.
1367 # If not found, the following steps take care of that.
1368 if eventCode == win32.EXCEPTION_DEBUG_EVENT:
1369 exceptionCode = event.get_exception_code()
1370 pre_name = self.__preExceptionNotifyCallbackName.get(
1371 exceptionCode, None)
1372 post_name = self.__postExceptionNotifyCallbackName.get(
1373 exceptionCode, None)
1374 if pre_name is not None:
1375 pre_handler = getattr(self, pre_name, None)
1376 if post_name is not None:
1377 post_handler = getattr(self, post_name, None)
1378
1379 # Get the pre notification method for all other events.
1380 # This includes the exception event if no notify method was found
1381 # for this exception code.
1382 if pre_handler is None:
1383 pre_name = self.__preEventNotifyCallbackName.get(eventCode, None)
1384 if pre_name is not None:
1385 pre_handler = getattr(self, pre_name, pre_handler)
1386
1387 # Get the post notification method for all other events.
1388 # This includes the exception event if no notify method was found
1389 # for this exception code.
1390 if post_handler is None:
1391 post_name = self.__postEventNotifyCallbackName.get(eventCode, None)
1392 if post_name is not None:
1393 post_handler = getattr(self, post_name, post_handler)
1394
1395 # Call the pre-notify method only if it was defined.
1396 # If an exception is raised don't call the other methods.
1397 if pre_handler is not None:
1398 bCallHandler = pre_handler(event)
1399
1400 # Call the user-defined event handler only if the pre-notify
1401 # method was not defined, or was and it returned True.
1402 try:
1403 if bCallHandler and self.__eventHandler is not None:
1404 returnValue = self.__eventHandler(event)
1405
1406 # Call the post-notify method if defined, even if an exception is
1407 # raised by the user-defined event handler.
1408 finally:
1409 if post_handler is not None:
1410 post_handler(event)
1411
1412 # Return the value from the call to the user-defined event handler.
1413 # If not defined return None.
1414 return returnValue
1415
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Feb 12 19:47:53 2010 | http://epydoc.sourceforge.net |