1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """
32 Thread instrumentation.
33
34 @group Instrumentation:
35 Thread
36 """
37
38 from __future__ import with_statement
39
40 __revision__ = "$Id: thread.py 1299 2013-12-20 09:30:55Z qvasimodo $"
41
42 __all__ = ['Thread']
43
44 import win32
45 from textio import HexDump
46 from util import DebugRegister
47 from window import Window
48
49 import struct
50 import warnings
51
52
53 Process = None
54
55
56
57
58
59
61 """
62 Interface to a thread in another process.
63
64 @group Properties:
65 get_tid, get_pid, get_process, set_process, get_exit_code, is_alive,
66 get_name, set_name, get_windows, get_teb, get_teb_address, is_wow64,
67 get_arch, get_bits, get_handle, open_handle, close_handle
68
69 @group Instrumentation:
70 suspend, resume, kill, wait
71
72 @group Debugging:
73 get_seh_chain_pointer, set_seh_chain_pointer,
74 get_seh_chain, get_wait_chain, is_hidden
75
76 @group Disassembly:
77 disassemble, disassemble_around, disassemble_around_pc,
78 disassemble_string, disassemble_instruction, disassemble_current
79
80 @group Stack:
81 get_stack_frame, get_stack_frame_range, get_stack_range,
82 get_stack_trace, get_stack_trace_with_labels,
83 read_stack_data, read_stack_dwords, read_stack_qwords,
84 peek_stack_data, peek_stack_dwords, peek_stack_qwords,
85 read_stack_structure, read_stack_frame
86
87 @group Registers:
88 get_context,
89 get_register,
90 get_flags, get_flag_value,
91 get_pc, get_sp, get_fp,
92 get_cf, get_df, get_sf, get_tf, get_zf,
93 set_context,
94 set_register,
95 set_flags, set_flag_value,
96 set_pc, set_sp, set_fp,
97 set_cf, set_df, set_sf, set_tf, set_zf,
98 clear_cf, clear_df, clear_sf, clear_tf, clear_zf,
99 Flags
100
101 @group Threads snapshot:
102 clear
103
104 @group Miscellaneous:
105 read_code_bytes, peek_code_bytes,
106 peek_pointers_in_data, peek_pointers_in_registers,
107 get_linear_address, get_label_at_pc
108
109 @type dwThreadId: int
110 @ivar dwThreadId: Global thread ID. Use L{get_tid} instead.
111
112 @type hThread: L{ThreadHandle}
113 @ivar hThread: Handle to the thread. Use L{get_handle} instead.
114
115 @type process: L{Process}
116 @ivar process: Parent process object. Use L{get_process} instead.
117
118 @type pInjectedMemory: int
119 @ivar pInjectedMemory: If the thread was created by L{Process.inject_code},
120 this member contains a pointer to the memory buffer for the injected
121 code. Otherwise it's C{None}.
122
123 The L{kill} method uses this member to free the buffer
124 when the injected thread is killed.
125 """
126
127 - def __init__(self, dwThreadId, hThread = None, process = None):
128 """
129 @type dwThreadId: int
130 @param dwThreadId: Global thread ID.
131
132 @type hThread: L{ThreadHandle}
133 @param hThread: (Optional) Handle to the thread.
134
135 @type process: L{Process}
136 @param process: (Optional) Parent Process object.
137 """
138 self.dwProcessId = None
139 self.dwThreadId = dwThreadId
140 self.hThread = hThread
141 self.pInjectedMemory = None
142 self.set_name(None)
143 self.set_process(process)
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
168
170 """
171 @rtype: L{Process}
172 @return: Parent Process object.
173 Returns C{None} if unknown.
174 """
175 if self.__process is not None:
176 return self.__process
177 self.__load_Process_class()
178 self.__process = Process(self.get_pid())
179 return self.__process
180
182 """
183 Manually set the parent Process object. Use with care!
184
185 @type process: L{Process}
186 @param process: (Optional) Process object. Use C{None} for no process.
187 """
188 if process is None:
189 self.dwProcessId = None
190 self.__process = None
191 else:
192 self.__load_Process_class()
193 if not isinstance(process, Process):
194 msg = "Parent process must be a Process instance, "
195 msg += "got %s instead" % type(process)
196 raise TypeError(msg)
197 self.dwProcessId = process.get_pid()
198 self.__process = process
199
200 process = property(get_process, set_process, doc="")
201
225
241
243 """
244 @rtype: int
245 @return: Thread global ID.
246 """
247 return self.dwThreadId
248
250 """
251 @rtype: str
252 @return: Thread name, or C{None} if the thread is nameless.
253 """
254 return self.name
255
257 """
258 Sets the thread's name.
259
260 @type name: str
261 @param name: Thread name, or C{None} if the thread is nameless.
262 """
263 self.name = name
264
265
266
268 """
269 Opens a new handle to the thread, closing the previous one.
270
271 The new handle is stored in the L{hThread} property.
272
273 @warn: Normally you should call L{get_handle} instead, since it's much
274 "smarter" and tries to reuse handles and merge access rights.
275
276 @type dwDesiredAccess: int
277 @param dwDesiredAccess: Desired access rights.
278 Defaults to L{win32.THREAD_ALL_ACCESS}.
279 See: U{http://msdn.microsoft.com/en-us/library/windows/desktop/ms686769(v=vs.85).aspx}
280
281 @raise WindowsError: It's not possible to open a handle to the thread
282 with the requested access rights. This tipically happens because
283 the target thread belongs to system process and the debugger is not
284 runnning with administrative rights.
285 """
286 hThread = win32.OpenThread(dwDesiredAccess, win32.FALSE, self.dwThreadId)
287
288
289
290 if not hasattr(self.hThread, '__del__'):
291 self.close_handle()
292
293 self.hThread = hThread
294
296 """
297 Closes the handle to the thread.
298
299 @note: Normally you don't need to call this method. All handles
300 created by I{WinAppDbg} are automatically closed when the garbage
301 collector claims them.
302 """
303 try:
304 if hasattr(self.hThread, 'close'):
305 self.hThread.close()
306 elif self.hThread not in (None, win32.INVALID_HANDLE_VALUE):
307 win32.CloseHandle(self.hThread)
308 finally:
309 self.hThread = None
310
312 """
313 Returns a handle to the thread with I{at least} the access rights
314 requested.
315
316 @note:
317 If a handle was previously opened and has the required access
318 rights, it's reused. If not, a new handle is opened with the
319 combination of the old and new access rights.
320
321 @type dwDesiredAccess: int
322 @param dwDesiredAccess: Desired access rights.
323 See: U{http://msdn.microsoft.com/en-us/library/windows/desktop/ms686769(v=vs.85).aspx}
324
325 @rtype: ThreadHandle
326 @return: Handle to the thread.
327
328 @raise WindowsError: It's not possible to open a handle to the thread
329 with the requested access rights. This tipically happens because
330 the target thread belongs to system process and the debugger is not
331 runnning with administrative rights.
332 """
333 if self.hThread in (None, win32.INVALID_HANDLE_VALUE):
334 self.open_handle(dwDesiredAccess)
335 else:
336 dwAccess = self.hThread.dwAccess
337 if (dwAccess | dwDesiredAccess) != dwAccess:
338 self.open_handle(dwAccess | dwDesiredAccess)
339 return self.hThread
340
342 """
343 Clears the resources held by this object.
344 """
345 try:
346 self.set_process(None)
347 finally:
348 self.close_handle()
349
350
351
352 - def wait(self, dwTimeout = None):
353 """
354 Waits for the thread to finish executing.
355
356 @type dwTimeout: int
357 @param dwTimeout: (Optional) Timeout value in milliseconds.
358 Use C{INFINITE} or C{None} for no timeout.
359 """
360 self.get_handle(win32.SYNCHRONIZE).wait(dwTimeout)
361
362 - def kill(self, dwExitCode = 0):
363 """
364 Terminates the thread execution.
365
366 @note: If the C{lpInjectedMemory} member contains a valid pointer,
367 the memory is freed.
368
369 @type dwExitCode: int
370 @param dwExitCode: (Optional) Thread exit code.
371 """
372 hThread = self.get_handle(win32.THREAD_TERMINATE)
373 win32.TerminateThread(hThread, dwExitCode)
374
375
376
377 if self.pInjectedMemory is not None:
378 try:
379 self.get_process().free(self.pInjectedMemory)
380 self.pInjectedMemory = None
381 except Exception:
382
383 pass
384
385
386
387
388
405
415
417 """
418 @rtype: bool
419 @return: C{True} if the thread if currently running.
420 @raise WindowsError:
421 The debugger doesn't have enough privileges to perform this action.
422 """
423 try:
424 self.wait(0)
425 except WindowsError, e:
426 error = e.winerror
427 if error == win32.ERROR_ACCESS_DENIED:
428 raise
429 return error == win32.WAIT_TIMEOUT
430 return True
431
442
443
444
445
446
447
461
462
463
464
465
466 - def get_context(self, ContextFlags = None, bSuspend = False):
467 """
468 Retrieves the execution context (i.e. the registers values) for this
469 thread.
470
471 @type ContextFlags: int
472 @param ContextFlags: Optional, specify which registers to retrieve.
473 Defaults to C{win32.CONTEXT_ALL} which retrieves all registes
474 for the current platform.
475
476 @type bSuspend: bool
477 @param bSuspend: C{True} to automatically suspend the thread before
478 getting its context, C{False} otherwise.
479
480 Defaults to C{False} because suspending the thread during some
481 debug events (like thread creation or destruction) may lead to
482 strange errors.
483
484 Note that WinAppDbg 1.4 used to suspend the thread automatically
485 always. This behavior was changed in version 1.5.
486
487 @rtype: dict( str S{->} int )
488 @return: Dictionary mapping register names to their values.
489
490 @see: L{set_context}
491 """
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507 dwAccess = win32.THREAD_GET_CONTEXT
508 if bSuspend:
509 dwAccess = dwAccess | win32.THREAD_SUSPEND_RESUME
510 hThread = self.get_handle(dwAccess)
511
512
513 if bSuspend:
514 try:
515 self.suspend()
516 except WindowsError:
517
518
519 bSuspend = False
520
521
522 try:
523
524 if win32.bits == self.get_bits():
525
526
527
528 ctx = win32.GetThreadContext(hThread,
529 ContextFlags = ContextFlags)
530
531 else:
532 if self.is_wow64():
533
534
535 if ContextFlags is not None:
536 ContextFlags &= ~win32.ContextArchMask
537 ContextFlags |= win32.WOW64_CONTEXT_i386
538 ctx = win32.Wow64GetThreadContext(hThread, ContextFlags)
539
540 else:
541
542
543
544 if win32.arch not in (win32.ARCH_I386, win32.ARCH_AMD64):
545 raise NotImplementedError()
546 if ContextFlags is not None:
547 ContextFlags &= ~win32.ContextArchMask
548 ContextFlags |= win32.context_amd64.CONTEXT_AMD64
549 ctx = win32.context_amd64.GetThreadContext(hThread,
550 ContextFlags = ContextFlags)
551
552 finally:
553
554
555 if bSuspend:
556 self.resume()
557
558
559 return ctx
560
561 - def set_context(self, context, bSuspend = False):
562 """
563 Sets the values of the registers.
564
565 @see: L{get_context}
566
567 @type context: dict( str S{->} int )
568 @param context: Dictionary mapping register names to their values.
569
570 @type bSuspend: bool
571 @param bSuspend: C{True} to automatically suspend the thread before
572 setting its context, C{False} otherwise.
573
574 Defaults to C{False} because suspending the thread during some
575 debug events (like thread creation or destruction) may lead to
576 strange errors.
577
578 Note that WinAppDbg 1.4 used to suspend the thread automatically
579 always. This behavior was changed in version 1.5.
580 """
581
582
583 dwAccess = win32.THREAD_SET_CONTEXT
584 if bSuspend:
585 dwAccess = dwAccess | win32.THREAD_SUSPEND_RESUME
586 hThread = self.get_handle(dwAccess)
587
588
589 if bSuspend:
590 self.suspend()
591
592
593
594
595 try:
596 if win32.bits == 64 and self.is_wow64():
597 win32.Wow64SetThreadContext(hThread, context)
598 else:
599 win32.SetThreadContext(hThread, context)
600
601
602 finally:
603 if bSuspend:
604 self.resume()
605
607 """
608 @type register: str
609 @param register: Register name.
610
611 @rtype: int
612 @return: Value of the requested register.
613 """
614 'Returns the value of a specific register.'
615 context = self.get_context()
616 return context[register]
617
619 """
620 Sets the value of a specific register.
621
622 @type register: str
623 @param register: Register name.
624
625 @rtype: int
626 @return: Register value.
627 """
628 context = self.get_context()
629 context[register] = value
630 self.set_context(context)
631
632
633
634
635
636
637 if win32.arch in (win32.ARCH_I386, win32.ARCH_AMD64):
638
646
648 """
649 Sets the value of the program counter register.
650
651 @type pc: int
652 @param pc: Value of the program counter register.
653 """
654 context = self.get_context(win32.CONTEXT_CONTROL)
655 context.pc = pc
656 self.set_context(context)
657
665
667 """
668 Sets the value of the stack pointer register.
669
670 @type sp: int
671 @param sp: Value of the stack pointer register.
672 """
673 context = self.get_context(win32.CONTEXT_CONTROL)
674 context.sp = sp
675 self.set_context(context)
676
685
697
698
699
700 if win32.arch in (win32.ARCH_I386, win32.ARCH_AMD64):
701
716
718 """
719 @type FlagMask: int
720 @param FlagMask: (Optional) Bitwise-AND mask.
721
722 @rtype: int
723 @return: Flags register contents, optionally masking out some bits.
724 """
725 context = self.get_context(win32.CONTEXT_CONTROL)
726 return context['EFlags'] & FlagMask
727
728 - def set_flags(self, eflags, FlagMask = 0xFFFFFFFF):
729 """
730 Sets the flags register, optionally masking some bits.
731
732 @type eflags: int
733 @param eflags: Flags register contents.
734
735 @type FlagMask: int
736 @param FlagMask: (Optional) Bitwise-AND mask.
737 """
738 context = self.get_context(win32.CONTEXT_CONTROL)
739 context['EFlags'] = (context['EFlags'] & FlagMask) | eflags
740 self.set_context(context)
741
743 """
744 @type FlagBit: int
745 @param FlagBit: One of the L{Flags}.
746
747 @rtype: bool
748 @return: Boolean value of the requested flag.
749 """
750 return bool( self.get_flags(FlagBit) )
751
753 """
754 Sets a single flag, leaving the others intact.
755
756 @type FlagBit: int
757 @param FlagBit: One of the L{Flags}.
758
759 @type FlagValue: bool
760 @param FlagValue: Boolean value of the flag.
761 """
762 if FlagValue:
763 eflags = FlagBit
764 else:
765 eflags = 0
766 FlagMask = 0xFFFFFFFF ^ FlagBit
767 self.set_flags(eflags, FlagMask)
768
770 """
771 @rtype: bool
772 @return: Boolean value of the Zero flag.
773 """
774 return self.get_flag_value(self.Flags.Zero)
775
777 """
778 @rtype: bool
779 @return: Boolean value of the Carry flag.
780 """
781 return self.get_flag_value(self.Flags.Carry)
782
784 """
785 @rtype: bool
786 @return: Boolean value of the Sign flag.
787 """
788 return self.get_flag_value(self.Flags.Sign)
789
796
798 """
799 @rtype: bool
800 @return: Boolean value of the Trap flag.
801 """
802 return self.get_flag_value(self.Flags.Trap)
803
807
811
815
819
823
827
831
835
839
843
844
845
847 """
848 Determines if the thread is running under WOW64.
849
850 @rtype: bool
851 @return:
852 C{True} if the thread is running under WOW64. That is, it belongs
853 to a 32-bit application running in a 64-bit Windows.
854
855 C{False} if the thread belongs to either a 32-bit application
856 running in a 32-bit Windows, or a 64-bit application running in a
857 64-bit Windows.
858
859 @raise WindowsError: On error an exception is raised.
860
861 @see: U{http://msdn.microsoft.com/en-us/library/aa384249(VS.85).aspx}
862 """
863 try:
864 wow64 = self.__wow64
865 except AttributeError:
866 if (win32.bits == 32 and not win32.wow64):
867 wow64 = False
868 else:
869 wow64 = self.get_process().is_wow64()
870 self.__wow64 = wow64
871 return wow64
872
874 """
875 @rtype: str
876 @return: The architecture in which this thread believes to be running.
877 For example, if running a 32 bit binary in a 64 bit machine, the
878 architecture returned by this method will be L{win32.ARCH_I386},
879 but the value of L{System.arch} will be L{win32.ARCH_AMD64}.
880 """
881 if win32.bits == 32 and not win32.wow64:
882 return win32.arch
883 return self.get_process().get_arch()
884
886 """
887 @rtype: str
888 @return: The number of bits in which this thread believes to be
889 running. For example, if running a 32 bit binary in a 64 bit
890 machine, the number of bits returned by this method will be C{32},
891 but the value of L{System.arch} will be C{64}.
892 """
893 if win32.bits == 32 and not win32.wow64:
894 return 32
895 return self.get_process().get_bits()
896
898 """
899 Determines if the thread has been hidden from debuggers.
900
901 Some binary packers hide their own threads to thwart debugging.
902
903 @rtype: bool
904 @return: C{True} if the thread is hidden from debuggers.
905 This means the thread's execution won't be stopped for debug
906 events, and thus said events won't be sent to the debugger.
907 """
908 return win32.NtQueryInformationThread(
909 self.get_handle(),
910 win32.ThreadHideFromDebugger)
911
913 """
914 Returns a copy of the TEB.
915 To dereference pointers in it call L{Process.read_structure}.
916
917 @rtype: L{TEB}
918 @return: TEB structure.
919 @raise WindowsError: An exception is raised on error.
920 """
921 return self.get_process().read_structure( self.get_teb_address(),
922 win32.TEB )
923
946
985
992
994 """
995 Get the pointer to the first structured exception handler block.
996
997 @rtype: int
998 @return: Remote pointer to the first block of the structured exception
999 handlers linked list. If the list is empty, the returned value is
1000 C{0xFFFFFFFF}.
1001
1002 @raise NotImplementedError:
1003 This method is only supported in 32 bits versions of Windows.
1004 """
1005 if win32.arch != win32.ARCH_I386:
1006 raise NotImplementedError(
1007 "SEH chain parsing is only supported in 32-bit Windows.")
1008
1009 process = self.get_process()
1010 address = self.get_linear_address( 'SegFs', 0 )
1011 return process.read_pointer( address )
1012
1014 """
1015 Change the pointer to the first structured exception handler block.
1016
1017 @type value: int
1018 @param value: Value of the remote pointer to the first block of the
1019 structured exception handlers linked list. To disable SEH set the
1020 value C{0xFFFFFFFF}.
1021
1022 @raise NotImplementedError:
1023 This method is only supported in 32 bits versions of Windows.
1024 """
1025 if win32.arch != win32.ARCH_I386:
1026 raise NotImplementedError(
1027 "SEH chain parsing is only supported in 32-bit Windows.")
1028
1029 process = self.get_process()
1030 address = self.get_linear_address( 'SegFs', 0 )
1031 process.write_pointer( address, value )
1032
1034 """
1035 @rtype: list of tuple( int, int )
1036 @return: List of structured exception handlers.
1037 Each SEH is represented as a tuple of two addresses:
1038 - Address of this SEH block
1039 - Address of the SEH callback function
1040 Do not confuse this with the contents of the SEH block itself,
1041 where the first member is a pointer to the B{next} block instead.
1042
1043 @raise NotImplementedError:
1044 This method is only supported in 32 bits versions of Windows.
1045 """
1046 seh_chain = list()
1047 try:
1048 process = self.get_process()
1049 seh = self.get_seh_chain_pointer()
1050 while seh != 0xFFFFFFFF:
1051 seh_func = process.read_pointer( seh + 4 )
1052 seh_chain.append( (seh, seh_func) )
1053 seh = process.read_pointer( seh )
1054 except WindowsError, e:
1055 seh_chain.append( (seh, None) )
1056 return seh_chain
1057
1059 """
1060 @rtype:
1061 tuple of (
1062 list of L{win32.WaitChainNodeInfo} structures,
1063 bool)
1064 @return:
1065 Wait chain for the thread.
1066 The boolean indicates if there's a cycle in the chain (a deadlock).
1067 @raise AttributeError:
1068 This method is only suppported in Windows Vista and above.
1069 @see:
1070 U{http://msdn.microsoft.com/en-us/library/ms681622%28VS.85%29.aspx}
1071 """
1072 with win32.OpenThreadWaitChainSession() as hWct:
1073 return win32.GetThreadWaitChain(hWct, ThreadId = self.get_tid())
1074
1076 """
1077 @rtype: tuple( int, int )
1078 @return: Stack beginning and end pointers, in memory addresses order.
1079 That is, the first pointer is the stack top, and the second pointer
1080 is the stack bottom, since the stack grows towards lower memory
1081 addresses.
1082 @raise WindowsError: Raises an exception on error.
1083 """
1084
1085 teb = self.get_teb()
1086 tib = teb.NtTib
1087 return ( tib.StackLimit, tib.StackBase )
1088
1089 - def __get_stack_trace(self, depth = 16, bUseLabels = True,
1090 bMakePretty = True):
1091 """
1092 Tries to get a stack trace for the current function using the debug
1093 helper API (dbghelp.dll).
1094
1095 @type depth: int
1096 @param depth: Maximum depth of stack trace.
1097
1098 @type bUseLabels: bool
1099 @param bUseLabels: C{True} to use labels, C{False} to use addresses.
1100
1101 @type bMakePretty: bool
1102 @param bMakePretty:
1103 C{True} for user readable labels,
1104 C{False} for labels that can be passed to L{Process.resolve_label}.
1105
1106 "Pretty" labels look better when producing output for the user to
1107 read, while pure labels are more useful programatically.
1108
1109 @rtype: tuple of tuple( int, int, str )
1110 @return: Stack trace of the thread as a tuple of
1111 ( return address, frame pointer address, module filename )
1112 when C{bUseLabels} is C{True}, or a tuple of
1113 ( return address, frame pointer label )
1114 when C{bUseLabels} is C{False}.
1115
1116 @raise WindowsError: Raises an exception on error.
1117 """
1118
1119 aProcess = self.get_process()
1120 arch = aProcess.get_arch()
1121 bits = aProcess.get_bits()
1122
1123 if arch == win32.ARCH_I386:
1124 MachineType = win32.IMAGE_FILE_MACHINE_I386
1125 elif arch == win32.ARCH_AMD64:
1126 MachineType = win32.IMAGE_FILE_MACHINE_AMD64
1127 elif arch == win32.ARCH_IA64:
1128 MachineType = win32.IMAGE_FILE_MACHINE_IA64
1129 else:
1130 msg = "Stack walking is not available for this architecture: %s"
1131 raise NotImplementedError(msg % arch)
1132
1133 hProcess = aProcess.get_handle( win32.PROCESS_VM_READ |
1134 win32.PROCESS_QUERY_INFORMATION )
1135 hThread = self.get_handle( win32.THREAD_GET_CONTEXT |
1136 win32.THREAD_QUERY_INFORMATION )
1137
1138 StackFrame = win32.STACKFRAME64()
1139 StackFrame.AddrPC = win32.ADDRESS64( self.get_pc() )
1140 StackFrame.AddrFrame = win32.ADDRESS64( self.get_fp() )
1141 StackFrame.AddrStack = win32.ADDRESS64( self.get_sp() )
1142
1143 trace = list()
1144 while win32.StackWalk64(MachineType, hProcess, hThread, StackFrame):
1145 if depth <= 0:
1146 break
1147 fp = StackFrame.AddrFrame.Offset
1148 ra = aProcess.peek_pointer(fp + 4)
1149 if ra == 0:
1150 break
1151 lib = aProcess.get_module_at_address(ra)
1152 if lib is None:
1153 lib = ""
1154 else:
1155 if lib.fileName:
1156 lib = lib.fileName
1157 else:
1158 lib = "%s" % HexDump.address(lib.lpBaseOfDll, bits)
1159 if bUseLabels:
1160 label = aProcess.get_label_at_address(ra)
1161 if bMakePretty:
1162 label = '%s (%s)' % (HexDump.address(ra, bits), label)
1163 trace.append( (fp, label) )
1164 else:
1165 trace.append( (fp, ra, lib) )
1166 fp = aProcess.peek_pointer(fp)
1167 return tuple(trace)
1168
1171 """
1172 Tries to get a stack trace for the current function.
1173 Only works for functions with standard prologue and epilogue.
1174
1175 @type depth: int
1176 @param depth: Maximum depth of stack trace.
1177
1178 @type bUseLabels: bool
1179 @param bUseLabels: C{True} to use labels, C{False} to use addresses.
1180
1181 @type bMakePretty: bool
1182 @param bMakePretty:
1183 C{True} for user readable labels,
1184 C{False} for labels that can be passed to L{Process.resolve_label}.
1185
1186 "Pretty" labels look better when producing output for the user to
1187 read, while pure labels are more useful programatically.
1188
1189 @rtype: tuple of tuple( int, int, str )
1190 @return: Stack trace of the thread as a tuple of
1191 ( return address, frame pointer address, module filename )
1192 when C{bUseLabels} is C{True}, or a tuple of
1193 ( return address, frame pointer label )
1194 when C{bUseLabels} is C{False}.
1195
1196 @raise WindowsError: Raises an exception on error.
1197 """
1198 aProcess = self.get_process()
1199 st, sb = self.get_stack_range()
1200 fp = self.get_fp()
1201 trace = list()
1202 if aProcess.get_module_count() == 0:
1203 aProcess.scan_modules()
1204 bits = aProcess.get_bits()
1205 while depth > 0:
1206 if fp == 0:
1207 break
1208 if not st <= fp < sb:
1209 break
1210 ra = aProcess.peek_pointer(fp + 4)
1211 if ra == 0:
1212 break
1213 lib = aProcess.get_module_at_address(ra)
1214 if lib is None:
1215 lib = ""
1216 else:
1217 if lib.fileName:
1218 lib = lib.fileName
1219 else:
1220 lib = "%s" % HexDump.address(lib.lpBaseOfDll, bits)
1221 if bUseLabels:
1222 label = aProcess.get_label_at_address(ra)
1223 if bMakePretty:
1224 label = '%s (%s)' % (HexDump.address(ra, bits), label)
1225 trace.append( (fp, label) )
1226 else:
1227 trace.append( (fp, ra, lib) )
1228 fp = aProcess.peek_pointer(fp)
1229 return tuple(trace)
1230
1232 """
1233 Tries to get a stack trace for the current function.
1234 Only works for functions with standard prologue and epilogue.
1235
1236 @type depth: int
1237 @param depth: Maximum depth of stack trace.
1238
1239 @rtype: tuple of tuple( int, int, str )
1240 @return: Stack trace of the thread as a tuple of
1241 ( return address, frame pointer address, module filename ).
1242
1243 @raise WindowsError: Raises an exception on error.
1244 """
1245 try:
1246 trace = self.__get_stack_trace(depth, False)
1247 except Exception, e:
1248 import traceback
1249 traceback.print_exc(e)
1250 trace = ()
1251 if not trace:
1252 trace = self.__get_stack_trace_manually(depth, False)
1253 return trace
1254
1256 """
1257 Tries to get a stack trace for the current function.
1258 Only works for functions with standard prologue and epilogue.
1259
1260 @type depth: int
1261 @param depth: Maximum depth of stack trace.
1262
1263 @type bMakePretty: bool
1264 @param bMakePretty:
1265 C{True} for user readable labels,
1266 C{False} for labels that can be passed to L{Process.resolve_label}.
1267
1268 "Pretty" labels look better when producing output for the user to
1269 read, while pure labels are more useful programatically.
1270
1271 @rtype: tuple of tuple( int, int, str )
1272 @return: Stack trace of the thread as a tuple of
1273 ( return address, frame pointer label ).
1274
1275 @raise WindowsError: Raises an exception on error.
1276 """
1277 try:
1278 trace = self.__get_stack_trace(depth, True, bMakePretty)
1279 except Exception, e:
1280 trace = ()
1281 if not trace:
1282 trace = self.__get_stack_trace_manually(depth, True, bMakePretty)
1283 return trace
1284
1286 """
1287 Returns the starting and ending addresses of the stack frame.
1288 Only works for functions with standard prologue and epilogue.
1289
1290 @rtype: tuple( int, int )
1291 @return: Stack frame range.
1292 May not be accurate, depending on the compiler used.
1293
1294 @raise RuntimeError: The stack frame is invalid,
1295 or the function doesn't have a standard prologue
1296 and epilogue.
1297
1298 @raise WindowsError: An error occured when getting the thread context.
1299 """
1300 st, sb = self.get_stack_range()
1301 sp = self.get_sp()
1302 fp = self.get_fp()
1303 size = fp - sp
1304 if not st <= sp < sb:
1305 raise RuntimeError('Stack pointer lies outside the stack')
1306 if not st <= fp < sb:
1307 raise RuntimeError('Frame pointer lies outside the stack')
1308 if sp > fp:
1309 raise RuntimeError('No valid stack frame found')
1310 return (sp, fp)
1311
1313 """
1314 Reads the contents of the current stack frame.
1315 Only works for functions with standard prologue and epilogue.
1316
1317 @type max_size: int
1318 @param max_size: (Optional) Maximum amount of bytes to read.
1319
1320 @rtype: str
1321 @return: Stack frame data.
1322 May not be accurate, depending on the compiler used.
1323 May return an empty string.
1324
1325 @raise RuntimeError: The stack frame is invalid,
1326 or the function doesn't have a standard prologue
1327 and epilogue.
1328
1329 @raise WindowsError: An error occured when getting the thread context
1330 or reading data from the process memory.
1331 """
1332 sp, fp = self.get_stack_frame_range()
1333 size = fp - sp
1334 if max_size and size > max_size:
1335 size = max_size
1336 return self.get_process().peek(sp, size)
1337
1339 """
1340 Reads the contents of the top of the stack.
1341
1342 @type size: int
1343 @param size: Number of bytes to read.
1344
1345 @type offset: int
1346 @param offset: Offset from the stack pointer to begin reading.
1347
1348 @rtype: str
1349 @return: Stack data.
1350
1351 @raise WindowsError: Could not read the requested data.
1352 """
1353 aProcess = self.get_process()
1354 return aProcess.read(self.get_sp() + offset, size)
1355
1357 """
1358 Tries to read the contents of the top of the stack.
1359
1360 @type size: int
1361 @param size: Number of bytes to read.
1362
1363 @type offset: int
1364 @param offset: Offset from the stack pointer to begin reading.
1365
1366 @rtype: str
1367 @return: Stack data.
1368 Returned data may be less than the requested size.
1369 """
1370 aProcess = self.get_process()
1371 return aProcess.peek(self.get_sp() + offset, size)
1372
1374 """
1375 Reads DWORDs from the top of the stack.
1376
1377 @type count: int
1378 @param count: Number of DWORDs to read.
1379
1380 @type offset: int
1381 @param offset: Offset from the stack pointer to begin reading.
1382
1383 @rtype: tuple( int... )
1384 @return: Tuple of integers read from the stack.
1385
1386 @raise WindowsError: Could not read the requested data.
1387 """
1388 if count > 0:
1389 stackData = self.read_stack_data(count * 4, offset)
1390 return struct.unpack('<'+('L'*count), stackData)
1391 return ()
1392
1394 """
1395 Tries to read DWORDs from the top of the stack.
1396
1397 @type count: int
1398 @param count: Number of DWORDs to read.
1399
1400 @type offset: int
1401 @param offset: Offset from the stack pointer to begin reading.
1402
1403 @rtype: tuple( int... )
1404 @return: Tuple of integers read from the stack.
1405 May be less than the requested number of DWORDs.
1406 """
1407 stackData = self.peek_stack_data(count * 4, offset)
1408 if len(stackData) & 3:
1409 stackData = stackData[:-len(stackData) & 3]
1410 if not stackData:
1411 return ()
1412 return struct.unpack('<'+('L'*count), stackData)
1413
1415 """
1416 Reads QWORDs from the top of the stack.
1417
1418 @type count: int
1419 @param count: Number of QWORDs to read.
1420
1421 @type offset: int
1422 @param offset: Offset from the stack pointer to begin reading.
1423
1424 @rtype: tuple( int... )
1425 @return: Tuple of integers read from the stack.
1426
1427 @raise WindowsError: Could not read the requested data.
1428 """
1429 stackData = self.read_stack_data(count * 8, offset)
1430 return struct.unpack('<'+('Q'*count), stackData)
1431
1433 """
1434 Tries to read QWORDs from the top of the stack.
1435
1436 @type count: int
1437 @param count: Number of QWORDs to read.
1438
1439 @type offset: int
1440 @param offset: Offset from the stack pointer to begin reading.
1441
1442 @rtype: tuple( int... )
1443 @return: Tuple of integers read from the stack.
1444 May be less than the requested number of QWORDs.
1445 """
1446 stackData = self.peek_stack_data(count * 8, offset)
1447 if len(stackData) & 7:
1448 stackData = stackData[:-len(stackData) & 7]
1449 if not stackData:
1450 return ()
1451 return struct.unpack('<'+('Q'*count), stackData)
1452
1454 """
1455 Reads the given structure at the top of the stack.
1456
1457 @type structure: ctypes.Structure
1458 @param structure: Structure of the data to read from the stack.
1459
1460 @type offset: int
1461 @param offset: Offset from the stack pointer to begin reading.
1462 The stack pointer is the same returned by the L{get_sp} method.
1463
1464 @rtype: tuple
1465 @return: Tuple of elements read from the stack. The type of each
1466 element matches the types in the stack frame structure.
1467 """
1468 aProcess = self.get_process()
1469 stackData = aProcess.read_structure(self.get_sp() + offset, structure)
1470 return tuple([ stackData.__getattribute__(name)
1471 for (name, type) in stackData._fields_ ])
1472
1474 """
1475 Reads the stack frame of the thread.
1476
1477 @type structure: ctypes.Structure
1478 @param structure: Structure of the stack frame.
1479
1480 @type offset: int
1481 @param offset: Offset from the frame pointer to begin reading.
1482 The frame pointer is the same returned by the L{get_fp} method.
1483
1484 @rtype: tuple
1485 @return: Tuple of elements read from the stack frame. The type of each
1486 element matches the types in the stack frame structure.
1487 """
1488 aProcess = self.get_process()
1489 stackData = aProcess.read_structure(self.get_fp() + offset, structure)
1490 return tuple([ stackData.__getattribute__(name)
1491 for (name, type) in stackData._fields_ ])
1492
1494 """
1495 Tries to read some bytes of the code currently being executed.
1496
1497 @type size: int
1498 @param size: Number of bytes to read.
1499
1500 @type offset: int
1501 @param offset: Offset from the program counter to begin reading.
1502
1503 @rtype: str
1504 @return: Bytes read from the process memory.
1505
1506 @raise WindowsError: Could not read the requested data.
1507 """
1508 return self.get_process().read(self.get_pc() + offset, size)
1509
1511 """
1512 Tries to read some bytes of the code currently being executed.
1513
1514 @type size: int
1515 @param size: Number of bytes to read.
1516
1517 @type offset: int
1518 @param offset: Offset from the program counter to begin reading.
1519
1520 @rtype: str
1521 @return: Bytes read from the process memory.
1522 May be less than the requested number of bytes.
1523 """
1524 return self.get_process().peek(self.get_pc() + offset, size)
1525
1527 """
1528 Tries to guess which values in the registers are valid pointers,
1529 and reads some data from them.
1530
1531 @type peekSize: int
1532 @param peekSize: Number of bytes to read from each pointer found.
1533
1534 @type context: dict( str S{->} int )
1535 @param context: (Optional)
1536 Dictionary mapping register names to their values.
1537 If not given, the current thread context will be used.
1538
1539 @rtype: dict( str S{->} str )
1540 @return: Dictionary mapping register names to the data they point to.
1541 """
1542 peekable_registers = (
1543 'Eax', 'Ebx', 'Ecx', 'Edx', 'Esi', 'Edi', 'Ebp'
1544 )
1545 if not context:
1546 context = self.get_context(win32.CONTEXT_CONTROL | \
1547 win32.CONTEXT_INTEGER)
1548 aProcess = self.get_process()
1549 data = dict()
1550 for (reg_name, reg_value) in context.iteritems():
1551 if reg_name not in peekable_registers:
1552 continue
1553
1554
1555
1556
1557
1558
1559 reg_data = aProcess.peek(reg_value, peekSize)
1560 if reg_data:
1561 data[reg_name] = reg_data
1562 return data
1563
1564
1565
1567 """
1568 Tries to guess which values in the given data are valid pointers,
1569 and reads some data from them.
1570
1571 @type data: str
1572 @param data: Binary data to find pointers in.
1573
1574 @type peekSize: int
1575 @param peekSize: Number of bytes to read from each pointer found.
1576
1577 @type peekStep: int
1578 @param peekStep: Expected data alignment.
1579 Tipically you specify 1 when data alignment is unknown,
1580 or 4 when you expect data to be DWORD aligned.
1581 Any other value may be specified.
1582
1583 @rtype: dict( str S{->} str )
1584 @return: Dictionary mapping stack offsets to the data they point to.
1585 """
1586 aProcess = self.get_process()
1587 return aProcess.peek_pointers_in_data(data, peekSize, peekStep)
1588
1589
1590
1591
1592
1593
1594
1596 """
1597 Disassemble instructions from a block of binary code.
1598
1599 @type lpAddress: int
1600 @param lpAddress: Memory address where the code was read from.
1601
1602 @type code: str
1603 @param code: Binary code to disassemble.
1604
1605 @rtype: list of tuple( long, int, str, str )
1606 @return: List of tuples. Each tuple represents an assembly instruction
1607 and contains:
1608 - Memory address of instruction.
1609 - Size of instruction in bytes.
1610 - Disassembly line of instruction.
1611 - Hexadecimal dump of instruction.
1612 """
1613 aProcess = self.get_process()
1614 return aProcess.disassemble_string(lpAddress, code)
1615
1617 """
1618 Disassemble instructions from the address space of the process.
1619
1620 @type lpAddress: int
1621 @param lpAddress: Memory address where to read the code from.
1622
1623 @type dwSize: int
1624 @param dwSize: Size of binary code to disassemble.
1625
1626 @rtype: list of tuple( long, int, str, str )
1627 @return: List of tuples. Each tuple represents an assembly instruction
1628 and contains:
1629 - Memory address of instruction.
1630 - Size of instruction in bytes.
1631 - Disassembly line of instruction.
1632 - Hexadecimal dump of instruction.
1633 """
1634 aProcess = self.get_process()
1635 return aProcess.disassemble(lpAddress, dwSize)
1636
1638 """
1639 Disassemble around the given address.
1640
1641 @type lpAddress: int
1642 @param lpAddress: Memory address where to read the code from.
1643
1644 @type dwSize: int
1645 @param dwSize: Delta offset.
1646 Code will be read from lpAddress - dwSize to lpAddress + dwSize.
1647
1648 @rtype: list of tuple( long, int, str, str )
1649 @return: List of tuples. Each tuple represents an assembly instruction
1650 and contains:
1651 - Memory address of instruction.
1652 - Size of instruction in bytes.
1653 - Disassembly line of instruction.
1654 - Hexadecimal dump of instruction.
1655 """
1656 aProcess = self.get_process()
1657 return aProcess.disassemble_around(lpAddress, dwSize)
1658
1660 """
1661 Disassemble around the program counter of the given thread.
1662
1663 @type dwSize: int
1664 @param dwSize: Delta offset.
1665 Code will be read from pc - dwSize to pc + dwSize.
1666
1667 @rtype: list of tuple( long, int, str, str )
1668 @return: List of tuples. Each tuple represents an assembly instruction
1669 and contains:
1670 - Memory address of instruction.
1671 - Size of instruction in bytes.
1672 - Disassembly line of instruction.
1673 - Hexadecimal dump of instruction.
1674 """
1675 aProcess = self.get_process()
1676 return aProcess.disassemble_around(self.get_pc(), dwSize)
1677
1679 """
1680 Disassemble the instruction at the given memory address.
1681
1682 @type lpAddress: int
1683 @param lpAddress: Memory address where to read the code from.
1684
1685 @rtype: tuple( long, int, str, str )
1686 @return: The tuple represents an assembly instruction
1687 and contains:
1688 - Memory address of instruction.
1689 - Size of instruction in bytes.
1690 - Disassembly line of instruction.
1691 - Hexadecimal dump of instruction.
1692 """
1693 aProcess = self.get_process()
1694 return aProcess.disassemble(lpAddress, 15)[0]
1695
1697 """
1698 Disassemble the instruction at the program counter of the given thread.
1699
1700 @rtype: tuple( long, int, str, str )
1701 @return: The tuple represents an assembly instruction
1702 and contains:
1703 - Memory address of instruction.
1704 - Size of instruction in bytes.
1705 - Disassembly line of instruction.
1706 - Hexadecimal dump of instruction.
1707 """
1708 return self.disassemble_instruction( self.get_pc() )
1709
1710
1711
1713 """
1714 Encapsulates the capability to contain Thread objects.
1715
1716 @group Instrumentation:
1717 start_thread
1718
1719 @group Threads snapshot:
1720 scan_threads,
1721 get_thread, get_thread_count, get_thread_ids,
1722 has_thread, iter_threads, iter_thread_ids,
1723 find_threads_by_name, get_windows,
1724 clear_threads, clear_dead_threads, close_thread_handles
1725 """
1726
1728 self.__threadDict = dict()
1729
1731 """
1732 Private method to automatically initialize the snapshot
1733 when you try to use it without calling any of the scan_*
1734 methods first. You don't need to call this yourself.
1735 """
1736 if not self.__threadDict:
1737 self.scan_threads()
1738
1740 """
1741 @type anObject: L{Thread}, int
1742 @param anObject:
1743 - C{int}: Global ID of the thread to look for.
1744 - C{Thread}: Thread object to look for.
1745
1746 @rtype: bool
1747 @return: C{True} if the snapshot contains
1748 a L{Thread} object with the same ID.
1749 """
1750 if isinstance(anObject, Thread):
1751 anObject = anObject.dwThreadId
1752 return self.has_thread(anObject)
1753
1755 """
1756 @see: L{iter_threads}
1757 @rtype: dictionary-valueiterator
1758 @return: Iterator of L{Thread} objects in this snapshot.
1759 """
1760 return self.iter_threads()
1761
1763 """
1764 @see: L{get_thread_count}
1765 @rtype: int
1766 @return: Count of L{Thread} objects in this snapshot.
1767 """
1768 return self.get_thread_count()
1769
1771 """
1772 @type dwThreadId: int
1773 @param dwThreadId: Global ID of the thread to look for.
1774
1775 @rtype: bool
1776 @return: C{True} if the snapshot contains a
1777 L{Thread} object with the given global ID.
1778 """
1779 self.__initialize_snapshot()
1780 return dwThreadId in self.__threadDict
1781
1783 """
1784 @type dwThreadId: int
1785 @param dwThreadId: Global ID of the thread to look for.
1786
1787 @rtype: L{Thread}
1788 @return: Thread object with the given global ID.
1789 """
1790 self.__initialize_snapshot()
1791 if dwThreadId not in self.__threadDict:
1792 msg = "Unknown thread ID: %d" % dwThreadId
1793 raise KeyError(msg)
1794 return self.__threadDict[dwThreadId]
1795
1797 """
1798 @see: L{iter_threads}
1799 @rtype: dictionary-keyiterator
1800 @return: Iterator of global thread IDs in this snapshot.
1801 """
1802 self.__initialize_snapshot()
1803 return self.__threadDict.iterkeys()
1804
1806 """
1807 @see: L{iter_thread_ids}
1808 @rtype: dictionary-valueiterator
1809 @return: Iterator of L{Thread} objects in this snapshot.
1810 """
1811 self.__initialize_snapshot()
1812 return self.__threadDict.itervalues()
1813
1815 """
1816 @rtype: list( int )
1817 @return: List of global thread IDs in this snapshot.
1818 """
1819 self.__initialize_snapshot()
1820 return self.__threadDict.keys()
1821
1823 """
1824 @rtype: int
1825 @return: Count of L{Thread} objects in this snapshot.
1826 """
1827 self.__initialize_snapshot()
1828 return len(self.__threadDict)
1829
1830
1831
1833 """
1834 Find threads by name, using different search methods.
1835
1836 @type name: str, None
1837 @param name: Name to look for. Use C{None} to find nameless threads.
1838
1839 @type bExactMatch: bool
1840 @param bExactMatch: C{True} if the name must be
1841 B{exactly} as given, C{False} if the name can be
1842 loosely matched.
1843
1844 This parameter is ignored when C{name} is C{None}.
1845
1846 @rtype: list( L{Thread} )
1847 @return: All threads matching the given name.
1848 """
1849 found_threads = list()
1850
1851
1852 if name is None:
1853 for aThread in self.iter_threads():
1854 if aThread.get_name() is None:
1855 found_threads.append(aThread)
1856
1857
1858 elif bExactMatch:
1859 for aThread in self.iter_threads():
1860 if aThread.get_name() == name:
1861 found_threads.append(aThread)
1862
1863
1864 else:
1865 for aThread in self.iter_threads():
1866 t_name = aThread.get_name()
1867 if t_name is not None and name in t_name:
1868 found_threads.append(aThread)
1869
1870 return found_threads
1871
1872
1873
1874
1875
1876
1878 """
1879 @rtype: list of L{Window}
1880 @return: Returns a list of windows handled by this process.
1881 """
1882 window_list = list()
1883 for thread in self.iter_threads():
1884 window_list.extend( thread.get_windows() )
1885 return window_list
1886
1887
1888
1889 - def start_thread(self, lpStartAddress, lpParameter=0, bSuspended = False):
1917
1918
1919
1920
1921
1922
1923
1963
1965 """
1966 Remove Thread objects from the snapshot
1967 referring to threads no longer running.
1968 """
1969 for tid in self.get_thread_ids():
1970 aThread = self.get_thread(tid)
1971 if not aThread.is_alive():
1972 self._del_thread(aThread)
1973
1975 """
1976 Clears the threads snapshot.
1977 """
1978 for aThread in self.__threadDict.itervalues():
1979 aThread.clear()
1980 self.__threadDict = dict()
1981
1983 """
1984 Closes all open handles to threads in the snapshot.
1985 """
1986 for aThread in self.iter_threads():
1987 try:
1988 aThread.close_handle()
1989 except Exception, e:
1990 try:
1991 msg = "Cannot close thread handle %s, reason: %s"
1992 msg %= (aThread.hThread.value, str(e))
1993 warnings.warn(msg)
1994 except Exception:
1995 pass
1996
1997
1998
1999
2000
2002 """
2003 Private method to add a thread object to the snapshot.
2004
2005 @type aThread: L{Thread}
2006 @param aThread: Thread object.
2007 """
2008
2009
2010
2011
2012
2013
2014
2015 dwThreadId = aThread.dwThreadId
2016
2017
2018
2019 aThread.set_process(self)
2020 self.__threadDict[dwThreadId] = aThread
2021
2023 """
2024 Private method to remove a thread object from the snapshot.
2025
2026 @type dwThreadId: int
2027 @param dwThreadId: Global thread ID.
2028 """
2029 try:
2030 aThread = self.__threadDict[dwThreadId]
2031 del self.__threadDict[dwThreadId]
2032 except KeyError:
2033 aThread = None
2034 msg = "Unknown thread ID %d" % dwThreadId
2035 warnings.warn(msg, RuntimeWarning)
2036 if aThread:
2037 aThread.clear()
2038
2040 """
2041 Private method to test for a thread in the snapshot without triggering
2042 an automatic scan.
2043 """
2044 return dwThreadId in self.__threadDict
2045
2047 """
2048 Private method to get the list of thread IDs currently in the snapshot
2049 without triggering an automatic scan.
2050 """
2051 return self.__threadDict.keys()
2052
2069
2070
2071
2072
2073
2075 """
2076 Notify the creation of the main thread of this process.
2077
2078 This is done automatically by the L{Debug} class, you shouldn't need
2079 to call it yourself.
2080
2081 @type event: L{CreateProcessEvent}
2082 @param event: Create process event.
2083
2084 @rtype: bool
2085 @return: C{True} to call the user-defined handle, C{False} otherwise.
2086 """
2087 self.__add_created_thread(event)
2088 return True
2089
2091 """
2092 Notify the creation of a new thread in this process.
2093
2094 This is done automatically by the L{Debug} class, you shouldn't need
2095 to call it yourself.
2096
2097 @type event: L{CreateThreadEvent}
2098 @param event: Create thread event.
2099
2100 @rtype: bool
2101 @return: C{True} to call the user-defined handle, C{False} otherwise.
2102 """
2103 self.__add_created_thread(event)
2104 return True
2105
2107 """
2108 Notify the termination of a thread.
2109
2110 This is done automatically by the L{Debug} class, you shouldn't need
2111 to call it yourself.
2112
2113 @type event: L{ExitThreadEvent}
2114 @param event: Exit thread event.
2115
2116 @rtype: bool
2117 @return: C{True} to call the user-defined handle, C{False} otherwise.
2118 """
2119 dwThreadId = event.get_tid()
2120
2121 if self._has_thread_id(dwThreadId):
2122 self._del_thread(dwThreadId)
2123 return True
2124