Package winappdbg :: Module system
[hide private]
[frames] | no frames]

Source Code for Module winappdbg.system

   1  # Copyright (c) 2009-2010, 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  Instrumentation module. 
  30   
  31  @group Instrumentation: 
  32      System, Process, Thread, Module, Window 
  33   
  34  @group Capabilities (private): 
  35      ModuleContainer, ThreadContainer, ProcessContainer, SymbolContainer, 
  36      ThreadDebugOperations, ProcessDebugOperations, 
  37      MemoryOperations, SymbolOperations, SymbolEnumerator 
  38  """ 
  39   
  40  # FIXME 
  41  # I've been told the host process for the latest versions of VMWare 
  42  # can't be instrumented, because they try to stop code injection into the VMs. 
  43  # The solution appears to be to run the debugger from a user account that 
  44  # belongs to the VMware group. I haven't confirmed this yet. 
  45   
  46  __revision__ = "$Id: system.py 764 2010-07-20 10:59:45Z qvasimodo $" 
  47   
  48  __all__ =   [ 
  49                  # Instrumentation classes. 
  50                  'System', 
  51                  'Process', 
  52                  'Thread', 
  53                  'Module', 
  54                  'Window', 
  55              ] 
  56   
  57  import win32 
  58  import win32.version 
  59  from textio import HexInput, HexDump 
  60  from util import Regenerator, PathOperations, MemoryAddresses, DebugRegister 
  61   
  62  import re 
  63  import os 
  64  import sys 
  65  import ctypes 
  66  import struct 
  67  ##import weakref 
  68   
  69  try: 
  70      from distorm3 import Decode, Decode16Bits, Decode32Bits, Decode64Bits 
  71  except ImportError: 
  72      try: 
  73          from distorm import Decode, Decode16Bits, Decode32Bits, Decode64Bits 
  74      except ImportError: 
  75          Decode16Bits = None 
  76          Decode32Bits = None 
  77          Decode64Bits = None 
78 - def Decode(*argv, **argd):
79 "PLEASE INSTALL DISTORM BEFORE GENERATING THE DOCUMENTATION" 80 msg = ("diStorm is not installed or can't be found. Download it from: " 81 "http://code.google.com/p/distorm3") 82 raise NotImplementedError, msg
83
84 #============================================================================== 85 86 -class ModuleContainer (object):
87 """ 88 Encapsulates the capability to contain Module objects. 89 90 @group Modules snapshot: 91 scan_modules, 92 get_module, get_module_bases, get_module_count, 93 get_module_at_address, get_module_by_name, 94 has_module, iter_modules, iter_module_addresses, 95 clear_modules 96 97 @group Event notifications (private): 98 notify_load_dll, 99 notify_unload_dll 100 """ 101
102 - def __init__(self):
103 super(ModuleContainer, self).__init__() 104 self.__moduleDict = dict()
105
106 - def __initialize_snapshot(self):
107 """ 108 Private method to automatically initialize the snapshot 109 when you try to use it without calling any of the scan_* 110 methods first. You don't need to call this yourself. 111 """ 112 if not self.__moduleDict: 113 self.scan_modules()
114
115 - def __contains__(self, anObject):
116 """ 117 @type anObject: L{Module}, int 118 @param anObject: 119 - C{Module}: Module object to look for. 120 - C{int}: Base address of the DLL to look for. 121 122 @rtype: bool 123 @return: C{True} if the snapshot contains 124 a L{Module} object with the same base address. 125 """ 126 if isinstance(anObject, Module): 127 anObject = anObject.lpBaseOfDll 128 return self.has_module(anObject)
129
130 - def __iter__(self):
131 """ 132 @see: L{iter_modules} 133 @rtype: dictionary-valueiterator 134 @return: Iterator of L{Module} objects in this snapshot. 135 """ 136 return self.iter_modules()
137
138 - def __len__(self):
139 """ 140 @see: L{get_module_count} 141 @rtype: int 142 @return: Count of L{Module} objects in this snapshot. 143 """ 144 return self.get_module_count()
145
146 - def has_module(self, lpBaseOfDll):
147 """ 148 @type lpBaseOfDll: int 149 @param lpBaseOfDll: Base address of the DLL to look for. 150 151 @rtype: bool 152 @return: C{True} if the snapshot contains a 153 L{Module} object with the given base address. 154 """ 155 self.__initialize_snapshot() 156 return lpBaseOfDll in self.__moduleDict
157
158 - def get_module(self, lpBaseOfDll):
159 """ 160 @type lpBaseOfDll: int 161 @param lpBaseOfDll: Base address of the DLL to look for. 162 163 @rtype: L{Module} 164 @return: Module object with the given base address. 165 """ 166 self.__initialize_snapshot() 167 if lpBaseOfDll not in self.__moduleDict: 168 msg = "Unknown DLL base address %s" 169 msg = msg % HexDump.address(lpBaseOfDll) 170 raise KeyError, msg 171 return self.__moduleDict[lpBaseOfDll]
172
173 - def iter_module_addresses(self):
174 """ 175 @see: L{iter_modules} 176 @rtype: dictionary-keyiterator 177 @return: Iterator of DLL base addresses in this snapshot. 178 """ 179 self.__initialize_snapshot() 180 return self.__moduleDict.iterkeys()
181
182 - def iter_modules(self):
183 """ 184 @see: L{iter_module_addresses} 185 @rtype: dictionary-valueiterator 186 @return: Iterator of L{Module} objects in this snapshot. 187 """ 188 self.__initialize_snapshot() 189 return self.__moduleDict.itervalues()
190
191 - def get_module_bases(self):
192 """ 193 @see: L{iter_module_addresses} 194 @rtype: list( int... ) 195 @return: List of DLL base addresses in this snapshot. 196 """ 197 self.__initialize_snapshot() 198 return self.__moduleDict.keys()
199
200 - def get_module_count(self):
201 """ 202 @rtype: int 203 @return: Count of L{Module} objects in this snapshot. 204 """ 205 self.__initialize_snapshot() 206 return len(self.__moduleDict)
207 208 #------------------------------------------------------------------------------ 209
210 - def get_module_by_name(self, modName):
211 """ 212 @type modName: int 213 @param modName: 214 Name of the module to look for, as returned by L{Module.get_name}. 215 If two or more modules with the same name are loaded, only one 216 of the matching modules is returned. 217 218 You can also pass a full pathname to the DLL file. 219 This works correctly even if two modules with the same name 220 are loaded from different paths. 221 222 @rtype: L{Module} 223 @return: C{Module} object that best matches the given name. 224 Returns C{None} if no C{Module} can be found. 225 """ 226 227 # Convert modName to lowercase. 228 # This helps make case insensitive string comparisons. 229 modName = modName.lower() 230 231 # modName is an absolute pathname. 232 if PathOperations.path_is_absolute(modName): 233 for lib in self.iter_modules(): 234 if modName == lib.get_filename().lower(): 235 return lib 236 return None # Stop trying to match the name. 237 238 # Get all the module names. 239 # This prevents having to iterate through the module list 240 # more than once. 241 modDict = [ ( lib.get_name(), lib ) for lib in self.iter_modules() ] 242 modDict = dict(modDict) 243 244 # modName is a base filename. 245 if modName in modDict: 246 return modDict[modName] 247 248 # modName is a base filename without extension. 249 filepart, extpart = PathOperations.split_extension(modName) 250 if filepart and extpart: 251 if filepart in modDict: 252 return modDict[filepart] 253 254 # modName is a base address. 255 try: 256 baseAddress = HexInput.integer(modName) 257 except ValueError: 258 return None 259 if self.has_module(baseAddress): 260 return self.get_module(baseAddress) 261 262 # Module not found. 263 return None
264
265 - def get_module_at_address(self, address):
266 """ 267 @type address: int 268 @param address: Memory address to query. 269 270 @rtype: L{Module} 271 @return: C{Module} object that best matches the given address. 272 Returns C{None} if no C{Module} can be found. 273 """ 274 bases = self.get_module_bases() 275 bases.sort() 276 ## bases.append(0x100000000) # invalid, > 4 gb. address space 277 bases.append(0x1000000000000000) # invalid, > 64 bit address 278 if address >= bases[0]: 279 i = 0 280 max_i = len(bases) - 1 # -1 because last base is fake 281 while i < max_i: 282 begin, end = bases[i:i+2] 283 if begin <= address <= end: 284 module = self.get_module(begin) 285 here = module.is_address_here(address) 286 if here is False: 287 break 288 else: # True or None 289 return module 290 i = i + 1 291 return None
292 293 # XXX this method musn't end up calling __initialize_snapshot by accident!
294 - def scan_modules(self):
295 """ 296 Populates the snapshot with loaded modules. 297 """ 298 299 # The module filenames may be spoofed by malware, 300 # since this information resides in usermode space. 301 # See: http://www.ragestorm.net/blogs/?p=163 302 303 # Ignore special process IDs. 304 # PID 0: System Idle Process. Also has a special meaning to the 305 # toolhelp APIs (current process). 306 # PID 4: System Integrity Group. See this forum post for more info: 307 # http://tinyurl.com/ycza8jo 308 # (points to social.technet.microsoft.com) 309 # Only on XP and above 310 # PID 8: System (?) only in Windows 2000 and below AFAIK. 311 # It's probably the same as PID 4 in XP and above. 312 dwProcessId = self.get_pid() 313 if dwProcessId in (0, 4, 8): 314 return 315 316 # It would seem easier to clear the snapshot first. 317 # But then all open handles would be closed. 318 found_bases = set() 319 hSnapshot = win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPMODULE, \ 320 dwProcessId) 321 try: 322 me = win32.Module32First(hSnapshot) 323 while me is not None: 324 lpBaseAddress = me.modBaseAddr 325 fileName = me.szExePath # full pathname 326 if not fileName: 327 fileName = me.szModule # filename only 328 if not fileName: 329 fileName = None 330 else: 331 fileName = PathOperations.native_to_win32_pathname(fileName) 332 found_bases.add(lpBaseAddress) 333 ## if not self.has_module(lpBaseAddress): # XXX triggers a scan 334 if not self.__moduleDict.has_key(lpBaseAddress): 335 aModule = Module(lpBaseAddress, fileName = fileName, 336 SizeOfImage = me.modBaseSize, 337 process = self) 338 self.__add_module(aModule) 339 else: 340 aModule = self.get_module(lpBaseAddress) 341 if not aModule.fileName: 342 aModule.fileName = fileName 343 if not aModule.SizeOfImage: 344 aModule.SizeOfImage = me.modBaseSize 345 if not aModule.process: 346 aModule.process = self 347 me = win32.Module32Next(hSnapshot) 348 finally: 349 win32.CloseHandle(hSnapshot) 350 ## for base in self.get_module_bases(): # XXX triggers a scan 351 for base in self.__moduleDict.keys(): 352 if base not in found_bases: 353 self.__del_module(base)
354
355 - def clear_modules(self):
356 """ 357 Clears the modules snapshot. 358 """ 359 self.__moduleDict = dict()
360 361 #------------------------------------------------------------------------------ 362 363 # XXX notify_* methods should not trigger a scan 364
365 - def __add_module(self, aModule):
366 """ 367 Private method to add a module object to the snapshot. 368 369 @type aModule: L{Module} 370 @param aModule: Module object. 371 """ 372 ## if not isinstance(aModule, Module): 373 ## if hasattr(aModule, '__class__'): 374 ## typename = aModule.__class__.__name__ 375 ## else: 376 ## typename = str(type(aModule)) 377 ## msg = "Expected Module, got %s instead" % typename 378 ## raise TypeError, msg 379 lpBaseOfDll = aModule.get_base() 380 ## if lpBaseOfDll in self.__moduleDict: 381 ## msg = "Module already exists: %d" % lpBaseOfDll 382 ## raise KeyError, msg 383 self.__moduleDict[lpBaseOfDll] = aModule
384
385 - def __del_module(self, lpBaseOfDll):
386 """ 387 Private method to remove a module object from the snapshot. 388 389 @type lpBaseOfDll: int 390 @param lpBaseOfDll: Module base address. 391 """ 392 ## if lpBaseOfDll not in self.__moduleDict: 393 ## msg = "Unknown base address %d" % lpBaseOfDll 394 ## raise KeyError, msg 395 self.__moduleDict[lpBaseOfDll].hFile = None # handle 396 self.__moduleDict[lpBaseOfDll].process = None # circular reference 397 del self.__moduleDict[lpBaseOfDll]
398
399 - def __add_loaded_module(self, event):
400 """ 401 Private method to automatically add new module objects from debug events. 402 403 @type event: L{Event} 404 @param event: Event object. 405 """ 406 lpBaseOfDll = event.get_module_base() 407 hFile = event.get_file_handle() 408 ## if not self.has_module(lpBaseOfDll): # XXX this would trigger a scan 409 if not self.__moduleDict.has_key(lpBaseOfDll): 410 fileName = event.get_filename() 411 if not fileName: 412 fileName = None 413 if hasattr(event, 'get_start_address'): 414 EntryPoint = event.get_start_address() 415 else: 416 EntryPoint = None 417 aModule = Module(lpBaseOfDll, hFile, fileName = fileName, 418 EntryPoint = EntryPoint, 419 process = self) 420 self.__add_module(aModule) 421 else: 422 aModule = self.get_module(lpBaseOfDll) 423 if hFile != win32.INVALID_HANDLE_VALUE: 424 aModule.hFile = hFile 425 if not aModule.process: 426 aModule.process = self 427 if aModule.EntryPoint is None and \ 428 hasattr(event, 'get_start_address'): 429 aModule.EntryPoint = event.get_start_address() 430 if not aModule.fileName: 431 fileName = event.get_filename() 432 if fileName: 433 aModule.fileName = fileName
434
435 - def notify_create_process(self, event):
436 """ 437 Notify the load of the main module. 438 439 This is done automatically by the L{Debug} class, you shouldn't need 440 to call it yourself. 441 442 @type event: L{CreateProcessEvent} 443 @param event: Create process event. 444 445 @rtype: bool 446 @return: C{True} to call the user-defined handle, C{False} otherwise. 447 """ 448 self.__add_loaded_module(event) 449 return True
450
451 - def notify_load_dll(self, event):
452 """ 453 Notify the load of a new module. 454 455 This is done automatically by the L{Debug} class, you shouldn't need 456 to call it yourself. 457 458 @type event: L{LoadDLLEvent} 459 @param event: Load DLL event. 460 461 @rtype: bool 462 @return: C{True} to call the user-defined handle, C{False} otherwise. 463 """ 464 self.__add_loaded_module(event) 465 return True
466
467 - def notify_unload_dll(self, event):
468 """ 469 Notify the release of a loaded module. 470 471 This is done automatically by the L{Debug} class, you shouldn't need 472 to call it yourself. 473 474 @type event: L{UnloadDLLEvent} 475 @param event: Unload DLL event. 476 477 @rtype: bool 478 @return: C{True} to call the user-defined handle, C{False} otherwise. 479 """ 480 lpBaseOfDll = event.get_module_base() 481 ## if self.has_module(lpBaseOfDll): # XXX this would trigger a scan 482 if self.__moduleDict.has_key(lpBaseOfDll): 483 self.__del_module(lpBaseOfDll) 484 return True
485
486 #============================================================================== 487 488 -class ThreadContainer (object):
489 """ 490 Encapsulates the capability to contain Thread objects. 491 492 @group Instrumentation: 493 start_thread 494 495 @group Threads snapshot: 496 scan_threads, 497 get_thread, get_thread_count, get_thread_ids, 498 has_thread, iter_threads, iter_thread_ids, 499 find_threads_by_name, get_windows, 500 clear_threads, clear_dead_threads, close_thread_handles 501 502 @group Event notifications (private): 503 notify_create_process, 504 notify_create_thread, 505 notify_exit_thread 506 """ 507
508 - def __init__(self):
509 super(ThreadContainer, self).__init__() 510 self.__threadDict = dict()
511
512 - def __initialize_snapshot(self):
513 """ 514 Private method to automatically initialize the snapshot 515 when you try to use it without calling any of the scan_* 516 methods first. You don't need to call this yourself. 517 """ 518 if not self.__threadDict: 519 self.scan_threads()
520
521 - def __contains__(self, anObject):
522 """ 523 @type anObject: L{Thread}, int 524 @param anObject: 525 - C{int}: Global ID of the thread to look for. 526 - C{Thread}: Thread object to look for. 527 528 @rtype: bool 529 @return: C{True} if the snapshot contains 530 a L{Thread} object with the same ID. 531 """ 532 if isinstance(anObject, Thread): 533 anObject = anObject.dwThreadId 534 return self.has_thread(anObject)
535
536 - def __iter__(self):
537 """ 538 @see: L{iter_threads} 539 @rtype: dictionary-valueiterator 540 @return: Iterator of L{Thread} objects in this snapshot. 541 """ 542 return self.iter_threads()
543
544 - def __len__(self):
545 """ 546 @see: L{get_thread_count} 547 @rtype: int 548 @return: Count of L{Thread} objects in this snapshot. 549 """ 550 return self.get_thread_count()
551
552 - def has_thread(self, dwThreadId):
553 """ 554 @type dwThreadId: int 555 @param dwThreadId: Global ID of the thread to look for. 556 557 @rtype: bool 558 @return: C{True} if the snapshot contains a 559 L{Thread} object with the given global ID. 560 """ 561 self.__initialize_snapshot() 562 return dwThreadId in self.__threadDict
563
564 - def get_thread(self, dwThreadId):
565 """ 566 @type dwThreadId: int 567 @param dwThreadId: Global ID of the thread to look for. 568 569 @rtype: L{Thread} 570 @return: Thread object with the given global ID. 571 """ 572 self.__initialize_snapshot() 573 if dwThreadId not in self.__threadDict: 574 msg = "Unknown thread ID: %d" % dwThreadId 575 raise KeyError, msg 576 return self.__threadDict[dwThreadId]
577
578 - def iter_thread_ids(self):
579 """ 580 @see: L{iter_threads} 581 @rtype: dictionary-keyiterator 582 @return: Iterator of global thread IDs in this snapshot. 583 """ 584 self.__initialize_snapshot() 585 return self.__threadDict.iterkeys()
586
587 - def iter_threads(self):
588 """ 589 @see: L{iter_thread_ids} 590 @rtype: dictionary-valueiterator 591 @return: Iterator of L{Thread} objects in this snapshot. 592 """ 593 self.__initialize_snapshot() 594 return self.__threadDict.itervalues()
595
596 - def get_thread_ids(self):
597 """ 598 @rtype: list( int ) 599 @return: List of global thread IDs in this snapshot. 600 """ 601 self.__initialize_snapshot() 602 return self.__threadDict.keys()
603
604 - def get_thread_count(self):
605 """ 606 @rtype: int 607 @return: Count of L{Thread} objects in this snapshot. 608 """ 609 self.__initialize_snapshot() 610 return len(self.__threadDict)
611 612 #------------------------------------------------------------------------------ 613
614 - def find_threads_by_name(self, name, bExactMatch = True):
615 """ 616 Find threads by name, using different search methods. 617 618 @type name: str, None 619 @param name: Name to look for. Use C{None} to find nameless threads. 620 621 @type bExactMatch: bool 622 @param bExactMatch: C{True} if the name must be 623 B{exactly} as given, C{False} if the name can be 624 loosely matched. 625 626 This parameter is ignored when C{name} is C{None}. 627 628 @rtype: list( L{Thread} ) 629 @return: All threads matching the given name. 630 """ 631 found_threads = list() 632 633 # Find threads with no name. 634 if name is None: 635 for aThread in self.iter_threads(): 636 if aThread.get_name() is None: 637 found_threads.append(aThread) 638 639 # Find threads matching the given name exactly. 640 elif bExactMatch: 641 for aThread in self.iter_threads(): 642 if aThread.get_name() == name: 643 found_threads.append(aThread) 644 645 # Find threads whose names match the given substring. 646 else: 647 for aThread in self.iter_threads(): 648 t_name = aThread.get_name() 649 if t_name is not None and name in t_name: 650 found_threads.append(aThread) 651 652 return found_threads
653 654 #------------------------------------------------------------------------------ 655 656 # XXX TODO 657 # Support for string searches on the window captions. 658
659 - def get_windows(self):
660 """ 661 @rtype: list of L{Window} 662 @return: Returns a list of windows handled by this process. 663 """ 664 window_list = list() 665 for thread in self.iter_threads(): 666 window_list.extend( thread.get_windows() ) 667 return window_list
668 669 #------------------------------------------------------------------------------ 670
671 - def start_thread(self, lpStartAddress, lpParameter=0, bSuspended = False):
672 """ 673 Remotely creates a new thread in the process. 674 675 @type lpStartAddress: int 676 @param lpStartAddress: Start address for the new thread. 677 678 @type lpParameter: int 679 @param lpParameter: Optional argument for the new thread. 680 681 @type bSuspended: bool 682 @param bSuspended: C{True} if the new thread should be suspended. 683 In that case use L{Thread.resume} to start execution. 684 """ 685 if bSuspended: 686 dwCreationFlags = win32.CREATE_SUSPENDED 687 else: 688 dwCreationFlags = 0 689 hThread, dwThreadId = win32.CreateRemoteThread(self.get_handle(), 0, 0, 690 lpStartAddress, lpParameter, dwCreationFlags) 691 aThread = Thread(dwThreadId, hThread, self) 692 self.__add_thread(aThread) 693 return aThread
694 695 #------------------------------------------------------------------------------ 696 697 # TODO 698 # maybe put all the toolhelp code into their own set of classes? 699 # 700 # XXX this method musn't end up calling __initialize_snapshot by accident!
701 - def scan_threads(self):
702 """ 703 Populates the snapshot with running threads. 704 """ 705 706 # Ignore special process IDs. 707 # PID 0: System Idle Process. Also has a special meaning to the 708 # toolhelp APIs (current process). 709 # PID 4: System Integrity Group. See this forum post for more info: 710 # http://tinyurl.com/ycza8jo 711 # (points to social.technet.microsoft.com) 712 # Only on XP and above 713 # PID 8: System (?) only in Windows 2000 and below AFAIK. 714 # It's probably the same as PID 4 in XP and above. 715 dwProcessId = self.get_pid() 716 if dwProcessId in (0, 4, 8): 717 return 718 719 ## dead_tids = set( self.get_thread_ids() ) # XXX triggers a scan 720 dead_tids = self.__threadDict.keys() 721 dwProcessId = self.get_pid() 722 hSnapshot = win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPTHREAD, 723 dwProcessId) 724 try: 725 te = win32.Thread32First(hSnapshot) 726 while te is not None: 727 if te.th32OwnerProcessID == dwProcessId: 728 dwThreadId = te.th32ThreadID 729 if dwThreadId in dead_tids: 730 dead_tids.remove(dwThreadId) 731 ## if not self.has_thread(dwThreadId): # XXX triggers a scan 732 if not self.__threadDict.has_key(dwThreadId): 733 aThread = Thread(dwThreadId, process = self) 734 self.__add_thread(aThread) 735 te = win32.Thread32Next(hSnapshot) 736 finally: 737 win32.CloseHandle(hSnapshot) 738 for tid in dead_tids: 739 self.__del_thread(tid)
740
741 - def clear_dead_threads(self):
742 """ 743 Remove Thread objects from the snapshot 744 referring to threads no longer running. 745 """ 746 for tid in self.get_thread_ids(): 747 aThread = self.get_thread(tid) 748 if not aThread.is_alive(): 749 self.__del_thread(aThread)
750
751 - def clear_threads(self):
752 """ 753 Clears the threads snapshot. 754 """ 755 self.__threadDict = dict()
756
757 - def close_thread_handles(self):
758 """ 759 Closes all open handles to threads in the snapshot. 760 """ 761 for aThread in self.iter_threads(): 762 try: 763 aThread.close_handle() 764 except Exception, e: 765 pass
766 767 #------------------------------------------------------------------------------ 768 769 # XXX notify_* methods should not trigger a scan 770
771 - def __add_thread(self, aThread):
772 """ 773 Private method to add a thread object to the snapshot. 774 775 @type aThread: L{Thread} 776 @param aThread: Thread object. 777 """ 778 ## if not isinstance(aThread, Thread): 779 ## if hasattr(aThread, '__class__'): 780 ## typename = aThread.__class__.__name__ 781 ## else: 782 ## typename = str(type(aThread)) 783 ## msg = "Expected Thread, got %s instead" % typename 784 ## raise TypeError, msg 785 dwThreadId = aThread.dwThreadId 786 ## if dwThreadId in self.__threadDict: 787 ## msg = "Already have a Thread object with ID %d" % dwThreadId 788 ## raise KeyError, msg 789 aThread.dwProcessId = self.get_pid() 790 self.__threadDict[dwThreadId] = aThread
791
792 - def __del_thread(self, dwThreadId):
793 """ 794 Private method to remove a thread object from the snapshot. 795 796 @type dwThreadId: int 797 @param dwThreadId: Global thread ID. 798 """ 799 ## if dwThreadId not in self.__threadDict: 800 ## msg = "Unknown thread ID: %d" % dwThreadId 801 ## raise KeyError, msg 802 self.__threadDict[dwThreadId].hThread = None # handle 803 self.__threadDict[dwThreadId].process = None # circular reference 804 del self.__threadDict[dwThreadId]
805
806 - def __add_created_thread(self, event):
807 """ 808 Private method to automatically add new thread objects from debug events. 809 810 @type event: L{Event} 811 @param event: Event object. 812 """ 813 dwThreadId = event.get_tid() 814 hThread = event.get_thread_handle() 815 ## if not self.has_thread(dwThreadId): # XXX this would trigger a scan 816 if not self.__threadDict.has_key(dwThreadId): 817 aThread = Thread(dwThreadId, hThread, self) 818 self.__add_thread(aThread) 819 else: 820 aThread = self.get_thread(dwThreadId) 821 if hThread != win32.INVALID_HANDLE_VALUE: 822 aThread.hThread = hThread # may have more privileges
823
824 - def notify_create_process(self, event):
825 """ 826 Notify the creation of the main thread of this process. 827 828 This is done automatically by the L{Debug} class, you shouldn't need 829 to call it yourself. 830 831 @type event: L{CreateProcessEvent} 832 @param event: Create process event. 833 834 @rtype: bool 835 @return: C{True} to call the user-defined handle, C{False} otherwise. 836 """ 837 self.__add_created_thread(event) 838 return True
839
840 - def notify_create_thread(self, event):
841 """ 842 Notify the creation of a new thread in this process. 843 844 This is done automatically by the L{Debug} class, you shouldn't need 845 to call it yourself. 846 847 @type event: L{CreateThreadEvent} 848 @param event: Create thread event. 849 850 @rtype: bool 851 @return: C{True} to call the user-defined handle, C{False} otherwise. 852 """ 853 self.__add_created_thread(event) 854 return True
855
856 - def notify_exit_thread(self, event):
857 """ 858 Notify the termination of a thread. 859 860 This is done automatically by the L{Debug} class, you shouldn't need 861 to call it yourself. 862 863 @type event: L{ExitThreadEvent} 864 @param event: Exit thread event. 865 866 @rtype: bool 867 @return: C{True} to call the user-defined handle, C{False} otherwise. 868 """ 869 dwThreadId = event.get_tid() 870 ## if self.has_thread(dwThreadId): # XXX this would trigger a scan 871 if self.__threadDict.has_key(dwThreadId): 872 self.__del_thread(dwThreadId) 873 return True
874
875 #============================================================================== 876 877 -class ProcessContainer (object):
878 """ 879 Encapsulates the capability to contain Process objects. 880 881 @group Instrumentation: 882 start_process, argv_to_cmdline, cmdline_to_argv 883 884 @group Processes snapshot: 885 scan, scan_processes, scan_processes_fast, 886 get_process, get_process_count, get_process_ids, 887 has_process, iter_processes, iter_process_ids, 888 find_processes_by_filename, get_pid_from_tid, 889 get_windows, 890 clear, clear_processes, clear_dead_processes, 891 clear_unattached_processes, 892 close_process_handles, 893 close_process_and_thread_handles 894 895 @group Threads snapshots: 896 scan_processes_and_threads, 897 get_thread, get_thread_count, get_thread_ids, 898 has_thread 899 900 @group Modules snapshots: 901 scan_modules, find_modules_by_address, 902 find_modules_by_base, find_modules_by_name, 903 get_module_count 904 905 @group Event notifications (private): 906 notify_create_process, 907 notify_exit_process 908 """ 909
910 - def __init__(self):
911 super(ProcessContainer, self).__init__() 912 self.__processDict = dict()
913
914 - def __initialize_snapshot(self):
915 """ 916 Private method to automatically initialize the snapshot 917 when you try to use it without calling any of the scan_* 918 methods first. You don't need to call this yourself. 919 """ 920 if not self.__processDict: 921 ## self.scan() # recursive scan 922 try: 923 self.scan_processes() # normal scan 924 except Exception: 925 self.scan_processes_fast() # fast scan (no filenames)
926
927 - def __contains__(self, anObject):
928 """ 929 @type anObject: L{Process}, L{Thread}, int 930 @param anObject: 931 - C{int}: Global ID of the process to look for. 932 - C{int}: Global ID of the thread to look for. 933 - C{Process}: Process object to look for. 934 - C{Thread}: Thread object to look for. 935 936 @rtype: bool 937 @return: C{True} if the snapshot contains 938 a L{Process} or L{Thread} object with the same ID. 939 """ 940 if isinstance(anObject, Process): 941 anObject = anObject.dwProcessId 942 if self.has_process(anObject): 943 return True 944 for aProcess in self.iter_processes(): 945 if anObject in aProcess: 946 return True 947 return False
948
949 - def __iter__(self):
950 """ 951 @see: L{iter_processes} 952 @rtype: dictionary-valueiterator 953 @return: Iterator of L{Process} objects in this snapshot. 954 """ 955 return self.iter_processes()
956
957 - def __len__(self):
958 """ 959 @see: L{get_process_count} 960 @rtype: int 961 @return: Count of L{Process} objects in this snapshot. 962 """ 963 return self.get_process_count()
964
965 - def has_process(self, dwProcessId):
966 """ 967 @type dwProcessId: int 968 @param dwProcessId: Global ID of the process to look for. 969 970 @rtype: bool 971 @return: C{True} if the snapshot contains a 972 L{Process} object with the given global ID. 973 """ 974 self.__initialize_snapshot() 975 return dwProcessId in self.__processDict
976
977 - def get_process(self, dwProcessId):
978 """ 979 @type dwProcessId: int 980 @param dwProcessId: Global ID of the process to look for. 981 982 @rtype: L{Process} 983 @return: Process object with the given global ID. 984 """ 985 self.__initialize_snapshot() 986 if dwProcessId not in self.__processDict: 987 msg = "Unknown process ID %d" % dwProcessId 988 raise KeyError, msg 989 return self.__processDict[dwProcessId]
990
991 - def iter_process_ids(self):
992 """ 993 @see: L{iter_processes} 994 @rtype: dictionary-keyiterator 995 @return: Iterator of global process IDs in this snapshot. 996 """ 997 self.__initialize_snapshot() 998 return self.__processDict.iterkeys()
999
1000 - def iter_processes(self):
1001 """ 1002 @see: L{iter_process_ids} 1003 @rtype: dictionary-valueiterator 1004 @return: Iterator of L{Process} objects in this snapshot. 1005 """ 1006 self.__initialize_snapshot() 1007 return self.__processDict.itervalues()
1008
1009 - def get_process_ids(self):
1010 """ 1011 @see: L{iter_process_ids} 1012 @rtype: list( int ) 1013 @return: List of global process IDs in this snapshot. 1014 """ 1015 self.__initialize_snapshot() 1016 return self.__processDict.keys()
1017
1018 - def get_process_count(self):
1019 """ 1020 @rtype: int 1021 @return: Count of L{Process} objects in this snapshot. 1022 """ 1023 self.__initialize_snapshot() 1024 return len(self.__processDict)
1025 1026 #------------------------------------------------------------------------------ 1027 1028 # XXX TODO 1029 # Support for string searches on the window captions. 1030
1031 - def get_windows(self):
1032 """ 1033 @rtype: list of L{Window} 1034 @return: Returns a list of windows 1035 handled by all processes in this snapshot. 1036 """ 1037 window_list = list() 1038 for process in self.iter_processes(): 1039 window_list.extend( process.get_windows() ) 1040 return window_list
1041
1042 - def get_pid_from_tid(self, dwThreadId):
1043 """ 1044 Retrieves the global ID of the process that owns the thread. 1045 1046 @type dwThreadId: int 1047 @param dwThreadId: Thread global ID. 1048 1049 @rtype: int 1050 @return: Process global ID. 1051 1052 @raise KeyError: The thread does not exist. 1053 """ 1054 try: 1055 1056 # No good, because in XP and below it tries to get the PID 1057 # through the toolhelp API, and that's slow. We don't want 1058 # to scan for threads over and over for each call. 1059 ## dwProcessId = Thread(dwThreadId).get_pid() 1060 1061 # This API only exists in Vista and above. 1062 hThread = Thread(dwThreadId).get_handle() 1063 dwProcessId = win32.GetProcessIdOfThread(hThread) 1064 1065 # If all else fails, go through all processes in the snapshot 1066 # looking for the one that owns the thread we're looking for. 1067 # If the snapshot was empty the iteration should trigger an 1068 # automatic scan. Otherwise, it'll look for the thread in what 1069 # could possibly be an outdated snapshot. 1070 except Exception: 1071 for aProcess in self.iter_processes(): 1072 if aProcess.has_thread(dwThreadId): 1073 return aProcess.get_pid() 1074 1075 # The thread wasn't found, so let's refresh the snapshot and retry. 1076 # Normally this shouldn't happen since this function is only useful 1077 # for the debugger, so the thread should already exist in the snapshot. 1078 self.scan_processes_and_threads() 1079 for aProcess in self.iter_processes(): 1080 if aProcess.has_thread(dwThreadId): 1081 return aProcess.get_pid() 1082 1083 # No luck! It appears to be the thread doesn't exist after all. 1084 msg = "Unknown thread ID %d" % dwThreadId 1085 raise KeyError, msg
1086 1087 #------------------------------------------------------------------------------ 1088 1089 @staticmethod
1090 - def argv_to_cmdline(argv):
1091 """ 1092 Convert a list of arguments to a single command line string. 1093 1094 @type argv: list( str ) 1095 @param argv: List of argument strings. 1096 The first element is the program to execute. 1097 1098 @rtype: str 1099 @return: Command line string. 1100 """ 1101 cmdline = list() 1102 for token in argv: 1103 if not token: 1104 token = '""' 1105 else: 1106 if '"' in token: 1107 token = token.replace('"', '\\"') 1108 if ' ' in token or \ 1109 '\t' in token or \ 1110 '\n' in token or \ 1111 '\r' in token: 1112 token = '"%s"' % token 1113 cmdline.append(token) 1114 return ' '.join(cmdline)
1115 1116 @staticmethod
1117 - def cmdline_to_argv(lpCmdLine):
1118 """ 1119 Convert a single command line string to a list of arguments. 1120 1121 @type lpCmdLine: str 1122 @param lpCmdLine: Command line string. 1123 The first token is the program to execute. 1124 1125 @rtype: list( str ) 1126 @return: List of argument strings. 1127 """ 1128 if not lpCmdLine: 1129 return [] 1130 return win32.CommandLineToArgv(lpCmdLine)
1131
1132 - def start_process(self, lpCmdLine, 1133 bConsole = False, 1134 bDebug = False, 1135 bFollow = False, 1136 bSuspended = False, 1137 bInheritHandles = False, 1138 dwParentProcessId = None 1139 ):
1140 """ 1141 Starts a new process for instrumenting (or debugging). 1142 1143 @type lpCmdLine: str 1144 @param lpCmdLine: Command line to execute. Can't be an empty string. 1145 1146 @type bConsole: bool 1147 @param bConsole: C{True} if the new process should inherit the console. 1148 Defaults to C{False}. 1149 1150 @type bDebug: bool 1151 @param bDebug: C{True} to attach to the new process. 1152 To debug a process it's best to use the L{Debug} class instead. 1153 Defaults to C{False}. 1154 1155 @type bFollow: bool 1156 @param bFollow: C{True} to automatically attach to the child processes 1157 of the newly created process. Ignored unless C{bDebug} is C{True}. 1158 Defaults to C{False}. 1159 1160 @type bSuspended: bool 1161 @param bSuspended: C{True} if the new process should be suspended. 1162 Defaults to C{False}. 1163 1164 @type bInheritHandles: bool 1165 @param bInheritHandles: C{True} if the new process should inherit it's 1166 parent process' handles. Defaults to C{False}. 1167 1168 @type dwParentProcessId: int or None 1169 @param dwParentProcessId: C{None} if the debugger process should be the 1170 parent process (default), or a process ID to forcefully set as the 1171 debugee's parent (only available for Windows Vista and above). 1172 1173 @rtype: L{Process} 1174 @return: Process object. 1175 """ 1176 if not lpCmdLine: 1177 raise ValueError, "Missing command line to execute!" 1178 dwCreationFlags = 0 1179 dwCreationFlags |= win32.CREATE_DEFAULT_ERROR_MODE 1180 dwCreationFlags |= win32.CREATE_BREAKAWAY_FROM_JOB 1181 if not bConsole: 1182 dwCreationFlags |= win32.DETACHED_PROCESS 1183 if bSuspended: 1184 dwCreationFlags |= win32.CREATE_SUSPENDED 1185 if bDebug: 1186 dwCreationFlags |= win32.DEBUG_PROCESS 1187 if bDebug and not bFollow: 1188 dwCreationFlags |= win32.DEBUG_ONLY_THIS_PROCESS 1189 lpStartupInfo = None 1190 if dwParentProcessId is not None: 1191 myPID = win32.GetCurrentProcessId() 1192 if dwParentProcessId != myPID: 1193 if self.has_process(dwParentProcessId): 1194 ParentProcess = self.get_process(dwParentProcessId) 1195 else: 1196 ParentProcess = Process(dwParentProcessId) 1197 ParentProcessHandle = ParentProcess.get_handle()._as_parameter_ 1198 AttributeList = ( 1199 ( 1200 win32.PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, 1201 ParentProcessHandle 1202 ), 1203 ) 1204 AttributeList = win32.ProcThreadAttributeList(AttributeList) 1205 StartupInfoEx = win32.STARTUPINFOEX() 1206 StartupInfo = StartupInfoEx.StartupInfo 1207 StartupInfo.cb = win32.sizeof(win32.STARTUPINFOEX) 1208 StartupInfo.lpReserved = 0 1209 StartupInfo.lpDesktop = 0 1210 StartupInfo.lpTitle = 0 1211 StartupInfo.dwFlags = 0 1212 StartupInfo.cbReserved2 = 0 1213 StartupInfo.lpReserved2 = 0 1214 StartupInfoEx.lpAttributeList = AttributeList.value 1215 lpStartupInfo = StartupInfoEx 1216 dwCreationFlags |= win32.EXTENDED_STARTUPINFO_PRESENT 1217 pi = win32.CreateProcess(win32.NULL, lpCmdLine, 1218 bInheritHandles = bInheritHandles, 1219 dwCreationFlags = dwCreationFlags, 1220 lpStartupInfo = lpStartupInfo) 1221 aProcess = Process(pi.dwProcessId, pi.hProcess) 1222 aThread = Thread (pi.dwThreadId, pi.hThread) 1223 aProcess._ThreadContainer__add_thread(aThread) 1224 self.__add_process(aProcess) 1225 return aProcess
1226 1227 #------------------------------------------------------------------------------ 1228 1229 # XXX this methods musn't end up calling __initialize_snapshot by accident! 1230
1231 - def scan(self):
1232 """ 1233 Populates the snapshot with running processes and threads, 1234 and loaded modules. 1235 """ 1236 try: 1237 self.scan_processes_and_threads() 1238 except Exception: 1239 self.scan_processes_fast() 1240 raise 1241 self.scan_modules()
1242
1243 - def scan_processes_and_threads(self):
1244 """ 1245 Populates the snapshot with running processes and threads. 1246 """ 1247 1248 # The main module filename may be spoofed by malware, 1249 # since this information resides in usermode space. 1250 # See: http://www.ragestorm.net/blogs/?p=163 1251 1252 our_pid = win32.GetCurrentProcessId() 1253 ## dead_pids = set( self.get_process_ids() ) # XXX triggers a scan 1254 dead_pids = set( self.__processDict.keys() ) 1255 found_tids = set() 1256 1257 # Ignore our own process if it's in the snapshot for some reason 1258 if our_pid in dead_pids: 1259 dead_pids.remove(our_pid) 1260 1261 # Take a snapshot of all processes and threads 1262 # (excluding our own) 1263 dwFlags = win32.TH32CS_SNAPPROCESS | win32.TH32CS_SNAPTHREAD 1264 hSnapshot = win32.CreateToolhelp32Snapshot(dwFlags) 1265 try: 1266 1267 # Add all the processes 1268 pe = win32.Process32First(hSnapshot) 1269 while pe is not None: 1270 dwProcessId = pe.th32ProcessID 1271 if dwProcessId != our_pid: 1272 if dwProcessId in dead_pids: 1273 dead_pids.remove(dwProcessId) 1274 ## if not self.has_process(dwProcessId): # XXX triggers a scan 1275 if not self.__processDict.has_key(dwProcessId): 1276 aProcess = Process(dwProcessId) 1277 self.__add_process(aProcess) 1278 elif pe.szExeFile: 1279 aProcess = self.get_process(dwProcessId) 1280 if not aProcess.fileName: 1281 aProcess.fileName = pe.szExeFile 1282 pe = win32.Process32Next(hSnapshot) 1283 1284 # Add all the threads 1285 te = win32.Thread32First(hSnapshot) 1286 while te is not None: 1287 dwProcessId = te.th32OwnerProcessID 1288 if dwProcessId != our_pid: 1289 if dwProcessId in dead_pids: 1290 dead_pids.remove(dwProcessId) 1291 ## if self.has_process(dwProcessId): # XXX triggers a scan 1292 if self.__processDict.has_key(dwProcessId): 1293 aProcess = self.get_process(dwProcessId) 1294 else: 1295 aProcess = Process(dwProcessId) 1296 self.__add_process(aProcess) 1297 dwThreadId = te.th32ThreadID 1298 found_tids.add(dwThreadId) 1299 ## if not aProcess.has_thread(dwThreadId): # XXX triggers a scan 1300 if not aProcess._ThreadContainer__threadDict.has_key(dwThreadId): 1301 aThread = Thread(dwThreadId, process = aProcess) 1302 aProcess._ThreadContainer__add_thread(aThread) 1303 te = win32.Thread32Next(hSnapshot) 1304 1305 # Always close the snapshot handle before returning 1306 finally: 1307 win32.CloseHandle(hSnapshot) 1308 1309 # Remove dead processes 1310 for pid in dead_pids: 1311 self.__del_process(pid) 1312 1313 # Remove dead threads 1314 ## for aProcess in self.iter_processes(): # XXX triggers a scan 1315 for aProcess in self.__processDict.itervalues(): 1316 ## dead_tids = set( aProcess.get_thread_ids() ) # XXX triggers a scan 1317 dead_tids = set( aProcess._ThreadContainer__threadDict.keys() ) 1318 dead_tids.difference_update(found_tids) 1319 for tid in dead_tids: 1320 aProcess._ThreadContainer__del_thread(tid)
1321 1322
1323 - def scan_modules(self):
1324 """ 1325 Populates the snapshot with loaded modules. 1326 """ 1327 ## for aProcess in self.iter_processes(): # XXX triggers a scan 1328 for aProcess in self.__processDict.itervalues(): 1329 aProcess.scan_modules()
1330
1331 - def scan_processes(self):
1332 """ 1333 Populates the snapshot with running processes. 1334 """ 1335 1336 # The module filenames may be spoofed by malware, 1337 # since this information resides in usermode space. 1338 # See: http://www.ragestorm.net/blogs/?p=163 1339 1340 our_pid = win32.GetCurrentProcessId() 1341 ## dead_pids = set( self.get_process_ids() ) # XXX triggers a scan 1342 dead_pids = set( self.__processDict.keys() ) 1343 if our_pid in dead_pids: 1344 dead_pids.remove(our_pid) 1345 hSnapshot = win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPPROCESS) 1346 try: 1347 pe = win32.Process32First(hSnapshot) 1348 while pe is not None: 1349 dwProcessId = pe.th32ProcessID 1350 if dwProcessId != our_pid: 1351 if dwProcessId in dead_pids: 1352 dead_pids.remove(dwProcessId) 1353 ## if not self.has_process(dwProcessId): # XXX triggers a scan 1354 if not self.__processDict.has_key(dwProcessId): 1355 aProcess = Process(dwProcessId) 1356 self.__add_process(aProcess) 1357 elif pe.szExeFile: 1358 aProcess = self.get_process(dwProcessId) 1359 if not aProcess.fileName: 1360 aProcess.fileName = pe.szExeFile 1361 pe = win32.Process32Next(hSnapshot) 1362 finally: 1363 win32.CloseHandle(hSnapshot) 1364 for pid in dead_pids: 1365 self.__del_process(pid)
1366
1367 - def scan_processes_fast(self):
1368 """ 1369 Populates the snapshot with running processes. 1370 Only the PID is retrieved for each process. 1371 1372 Dead processes are removed. 1373 Threads and modules of living processes are ignored. 1374 1375 @note: This method may be faster for scanning, but some information 1376 may be missing, outdated or slower to obtain. This could be a good 1377 tradeoff under some circumstances. 1378 """ 1379 1380 # Get the new and old list of pids 1381 new_pids = set( win32.EnumProcesses() ) 1382 ## old_pids = set( self.get_process_ids() ) # XXX triggers a scan 1383 old_pids = set( self.__processDict.keys() ) 1384 1385 # Ignore our own pid 1386 our_pid = win32.GetCurrentProcessId() 1387 if our_pid in new_pids: 1388 new_pids.remove(our_pid) 1389 if our_pid in old_pids: 1390 old_pids.remove(our_pid) 1391 1392 # Add newly found pids 1393 for pid in new_pids.difference(old_pids): 1394 self.__add_process( Process(pid) ) 1395 1396 # Remove missing pids 1397 for pid in old_pids.difference(new_pids): 1398 self.__del_process(pid)
1399
1400 - def clear_dead_processes(self):
1401 """ 1402 Removes Process objects from the snapshot 1403 referring to processes no longer running. 1404 """ 1405 for pid in self.get_process_ids(): 1406 aProcess = self.get_process(pid) 1407 if not aProcess.is_alive(): 1408 self.__del_process(aProcess)
1409
1410 - def clear_unattached_processes(self):
1411 """ 1412 Removes Process objects from the snapshot 1413 referring to processes not being debugged. 1414 """ 1415 for pid in self.get_process_ids(): 1416 aProcess = self.get_process(pid) 1417 if not aProcess.is_being_debugged(): 1418 self.__del_process(aProcess)
1419
1420 - def close_process_handles(self):
1421 """ 1422 Closes all open handles to processes in this snapshot. 1423 """ 1424 for pid in self.get_process_ids(): 1425 aProcess = self.get_process(pid) 1426 try: 1427 aProcess.close_handle() 1428 except Exception, e: 1429 pass
1430
1432 """ 1433 Closes all open handles to processes and threads in this snapshot. 1434 """ 1435 for pid in self.get_process_ids(): 1436 aProcess = self.get_process(pid) 1437 aProcess.close_thread_handles() 1438 try: 1439 aProcess.close_handle() 1440 except Exception, e: 1441 pass
1442
1443 - def clear_processes(self):
1444 """ 1445 Removes all L{Process}, L{Thread} and L{Module} objects in this snapshot. 1446 """ 1447 self.__processDict = dict()
1448
1449 - def clear(self):
1450 """ 1451 Clears this snapshot. 1452 1453 @see: L{clear_processes} 1454 """ 1455 self.clear_processes()
1456 1457 #------------------------------------------------------------------------------ 1458 1459 # Docs for these methods are taken from the ThreadContainer class. 1460
1461 - def has_thread(self, dwThreadId):
1462 dwProcessId = self.get_pid_from_tid(dwThreadId) 1463 if dwProcessId is None: 1464 return False 1465 return self.has_process(dwProcessId)
1466
1467 - def get_thread(self, dwThreadId):
1468 dwProcessId = self.get_pid_from_tid(dwThreadId) 1469 if dwProcessId is None: 1470 msg = "Unknown thread ID %d" % dwThreadId 1471 raise KeyError, msg 1472 return self.get_process(dwProcessId).get_thread(dwThreadId)
1473
1474 - def get_thread_ids(self):
1475 ids = list() 1476 for aProcess in self.iter_processes(): 1477 ids += aProcess.get_thread_ids() 1478 return ids
1479
1480 - def get_thread_count(self):
1481 count = 0 1482 for aProcess in self.iter_processes(): 1483 count += aProcess.get_thread_count() 1484 return count
1485 1486 has_thread.__doc__ = ThreadContainer.has_thread.__doc__ 1487 get_thread.__doc__ = ThreadContainer.get_thread.__doc__ 1488 get_thread_ids.__doc__ = ThreadContainer.get_thread_ids.__doc__ 1489 get_thread_count.__doc__ = ThreadContainer.get_thread_count.__doc__ 1490 1491 #------------------------------------------------------------------------------ 1492 1493 # Docs for these methods are taken from the ModuleContainer class. 1494
1495 - def get_module_count(self):
1496 count = 0 1497 for aProcess in self.iter_processes(): 1498 count += aProcess.get_module_count() 1499 return count
1500 1501 get_module_count.__doc__ = ModuleContainer.get_module_count.__doc__ 1502 1503 #------------------------------------------------------------------------------ 1504
1505 - def find_modules_by_base(self, lpBaseOfDll):
1506 """ 1507 @rtype: list( L{Module}... ) 1508 @return: List of Module objects with the given base address. 1509 """ 1510 found = list() 1511 for aProcess in self.iter_processes(): 1512 if aProcess.has_module(lpBaseOfDll): 1513 aModule = aProcess.get_module(lpBaseOfDll) 1514 found.append( (aProcess, aModule) ) 1515 return found
1516
1517 - def find_modules_by_name(self, fileName):
1518 """ 1519 @rtype: list( L{Module}... ) 1520 @return: List of Module objects found. 1521 """ 1522 found = list() 1523 for aProcess in self.iter_processes(): 1524 aModule = aProcess.get_module_by_name(fileName) 1525 if aModule is not None: 1526 found.append( (aProcess, aModule) ) 1527 return found
1528
1529 - def find_modules_by_address(self, address):
1530 """ 1531 @rtype: list( L{Module}... ) 1532 @return: List of Module objects that best match the given address. 1533 """ 1534 found = list() 1535 for aProcess in self.iter_processes(): 1536 aModule = aProcess.get_module_at_address(address) 1537 if aModule is not None: 1538 found.append( (aProcess, aModule) ) 1539 return found
1540
1541 - def __find_processes_by_filename(self, filename):
1542 """ 1543 Internally used by L{find_processes_by_filename}. 1544 """ 1545 found = list() 1546 filename = filename.lower() 1547 if PathOperations.path_is_absolute(filename): 1548 for aProcess in self.iter_processes(): 1549 imagename = aProcess.get_filename() 1550 if imagename and imagename.lower() == filename: 1551 found.append( (aProcess, imagename) ) 1552 else: 1553 for aProcess in self.iter_processes(): 1554 imagename = aProcess.get_filename() 1555 if imagename: 1556 imagename = PathOperations.pathname_to_filename(imagename) 1557 if imagename.lower() == filename: 1558 found.append( (aProcess, imagename) ) 1559 return found
1560
1561 - def find_processes_by_filename(self, fileName):
1562 """ 1563 @type fileName: str 1564 @param fileName: Filename to search for. 1565 If it's a full pathname, the match must be exact. 1566 If it's a base filename only, the file part is matched, 1567 regardless of the directory where it's located. 1568 1569 @note: If the process is not found and the file extension is not 1570 given, this method will search again assuming a default 1571 extension (.exe). 1572 1573 @rtype: list of tuple( L{Process}, str ) 1574 @return: List of processes matching the given main module filename. 1575 Each tuple contains a Process object and it's filename. 1576 """ 1577 found = self.__find_processes_by_filename(fileName) 1578 if not found: 1579 fn, ext = PathOperations.split_extension(fileName) 1580 if not ext: 1581 fileName = '%s.exe' % fn 1582 found = self.__find_processes_by_filename(fileName) 1583 return found
1584 1585 #------------------------------------------------------------------------------ 1586 1587 # XXX notify_* methods should not trigger a scan 1588
1589 - def __add_process(self, aProcess):
1590 """ 1591 Private method to add a process object to the snapshot. 1592 1593 @type aProcess: L{Process} 1594 @param aProcess: Process object. 1595 """ 1596 ## if not isinstance(aProcess, Process): 1597 ## if hasattr(aProcess, '__class__'): 1598 ## typename = aProcess.__class__.__name__ 1599 ## else: 1600 ## typename = str(type(aProcess)) 1601 ## msg = "Expected Process, got %s instead" % typename 1602 ## raise TypeError, msg 1603 dwProcessId = aProcess.dwProcessId 1604 ## if dwProcessId in self.__processDict: 1605 ## msg = "Process already exists: %d" % dwProcessId 1606 ## raise KeyError, msg 1607 self.__processDict[dwProcessId] = aProcess
1608
1609 - def __del_process(self, dwProcessId):
1610 """ 1611 Private method to remove a process object from the snapshot. 1612 1613 @type dwProcessId: int 1614 @param dwProcessId: Global process ID. 1615 """ 1616 ## if dwProcessId not in self.__processDict: 1617 ## msg = "Unknown process ID %d" % dwProcessId 1618 ## raise KeyError, msg 1619 self.__processDict[dwProcessId].hProcess = None # handle 1620 self.__processDict[dwProcessId].clear() # circular reference 1621 del self.__processDict[dwProcessId]
1622 1623 # Notify the creation of a new process.
1624 - def notify_create_process(self, event):
1625 """ 1626 Notify the creation of a new process. 1627 1628 This is done automatically by the L{Debug} class, you shouldn't need 1629 to call it yourself. 1630 1631 @type event: L{CreateProcessEvent} 1632 @param event: Create process event. 1633 1634 @rtype: bool 1635 @return: C{True} to call the user-defined handle, C{False} otherwise. 1636 """ 1637 dwProcessId = event.get_pid() 1638 dwThreadId = event.get_tid() 1639 hProcess = event.get_process_handle() 1640 ## if not self.has_process(dwProcessId): # XXX this would trigger a scan 1641 if not self.__processDict.has_key(dwProcessId): 1642 aProcess = Process(dwProcessId, hProcess) 1643 self.__add_process(aProcess) 1644 aProcess.fileName = event.get_filename() 1645 else: 1646 aProcess = self.get_process(dwProcessId) 1647 if hProcess != win32.INVALID_HANDLE_VALUE: 1648 aProcess.hProcess = hProcess # may have more privileges 1649 if not aProcess.fileName: 1650 fileName = event.get_filename() 1651 if fileName: 1652 aProcess.fileName = fileName 1653 return aProcess.notify_create_process(event) # pass it to the process
1654
1655 - def notify_exit_process(self, event):
1656 """ 1657 Notify the termination of a process. 1658 1659 This is done automatically by the L{Debug} class, you shouldn't need 1660 to call it yourself. 1661 1662 @type event: L{ExitProcessEvent} 1663 @param event: Exit process event. 1664 1665 @rtype: bool 1666 @return: C{True} to call the user-defined handle, C{False} otherwise. 1667 """ 1668 dwProcessId = event.get_pid() 1669 ## if self.has_process(dwProcessId): # XXX this would trigger a scan 1670 if self.__processDict.has_key(dwProcessId): 1671 self.__del_process(dwProcessId) 1672 return True
1673
1674 #============================================================================== 1675 1676 # TODO 1677 # * This methods do not take into account that code breakpoints change the 1678 # memory. This object should talk to BreakpointContainer to retrieve the 1679 # original memory contents where code breakpoints are enabled. 1680 # * A memory cache could be implemented here. 1681 -class MemoryOperations (object):
1682 """ 1683 Encapsulates the capabilities to manipulate the memory of a process. 1684 1685 @group Instrumentation: 1686 malloc, free, mprotect, mquery, 1687 take_memory_snapshot, generate_memory_snapshot, restore_memory_snapshot 1688 1689 @group Memory mapping: 1690 get_memory_map, get_mapped_filenames, 1691 is_pointer, is_address_valid, is_address_free, is_address_reserved, 1692 is_address_commited, is_address_guard, is_address_readable, 1693 is_address_writeable, is_address_copy_on_write, is_address_executable, 1694 is_address_executable_and_writeable, 1695 is_buffer, 1696 is_buffer_readable, is_buffer_writeable, is_buffer_executable, 1697 is_buffer_executable_and_writeable 1698 1699 @group Memory read: 1700 read, read_char, read_uint, read_pointer, read_string, read_structure, 1701 peek, peek_char, peek_uint, peek_pointer, peek_string 1702 1703 @group Memory write: 1704 write, write_char, write_uint, write_pointer, 1705 poke, poke_char, poke_uint, poke_pointer 1706 """ 1707
1708 - def read(self, lpBaseAddress, nSize):
1709 """ 1710 Reads from the memory of the process. 1711 1712 @see: L{peek} 1713 1714 @type lpBaseAddress: int 1715 @param lpBaseAddress: Memory address to begin reading. 1716 1717 @type nSize: int 1718 @param nSize: Number of bytes to read. 1719 1720 @rtype: str 1721 @return: Bytes read from the process memory. 1722 1723 @raise WindowsError: On error an exception is raised. 1724 """ 1725 # XXX TODO 1726 # + Maybe change page permissions before trying to read? 1727 if not self.is_buffer(lpBaseAddress, nSize): 1728 raise ctypes.WinError(win32.ERROR_INVALID_ADDRESS) 1729 data = win32.ReadProcessMemory(self.get_handle(), lpBaseAddress, nSize) 1730 if len(data) != nSize: 1731 raise ctypes.WinError() 1732 return data
1733
1734 - def write(self, lpBaseAddress, lpBuffer):
1735 """ 1736 Writes to the memory of the process. 1737 1738 @note: Page permissions may be changed temporarily while writing. 1739 1740 @see: L{poke} 1741 1742 @type lpBaseAddress: int 1743 @param lpBaseAddress: Memory address to begin writing. 1744 1745 @type lpBuffer: str 1746 @param lpBuffer: Bytes to write. 1747 1748 @raise WindowsError: On error an exception is raised. 1749 """ 1750 r = self.poke(lpBaseAddress, lpBuffer) 1751 if r != len(lpBuffer): 1752 raise ctypes.WinError()
1753
1754 - def read_uint(self, lpBaseAddress):
1755 """ 1756 Reads a single unsigned integer from the memory of the process. 1757 1758 @see: L{peek} 1759 1760 @type lpBaseAddress: int 1761 @param lpBaseAddress: Memory address to begin reading. 1762 1763 @rtype: int 1764 @return: Integer value read from the process memory. 1765 1766 @raise WindowsError: On error an exception is raised. 1767 """ 1768 packedDword = self.read(lpBaseAddress, 4) 1769 if len(packedDword) != 4: 1770 raise ctypes.WinError() 1771 unpackedDword = struct.unpack('<L', packedDword)[0] 1772 return unpackedDword
1773
1774 - def write_uint(self, lpBaseAddress, unpackedDword):
1775 """ 1776 Writes a single unsigned integer to the memory of the process. 1777 1778 @note: Page permissions may be changed temporarily while writing. 1779 1780 @see: L{poke_uint} 1781 1782 @type lpBaseAddress: int 1783 @param lpBaseAddress: Memory address to begin writing. 1784 1785 @type unpackedDword: int, long 1786 @param unpackedDword: Value to write. 1787 1788 @raise WindowsError: On error an exception is raised. 1789 """ 1790 packedDword = struct.pack('<L', unpackedDword) 1791 self.write(lpBaseAddress, packedDword)
1792
1793 - def read_pointer(self, lpBaseAddress):
1794 """ 1795 Reads a single pointer value from the memory of the process. 1796 1797 @see: L{peek_pointer} 1798 1799 @type lpBaseAddress: int 1800 @param lpBaseAddress: Memory address to begin reading. 1801 1802 @rtype: int 1803 @return: Pointer value read from the process memory. 1804 1805 @raise WindowsError: On error an exception is raised. 1806 """ 1807 lpvoidLength = win32.sizeof(win32.LPVOID) 1808 packedValue = self.read(lpBaseAddress, lpvoidLength) 1809 if lpvoidLength == 4: 1810 lpvoidFmt = '<L' 1811 else: 1812 lpvoidFmt = '<Q' 1813 unpackedValue = struct.unpack(lpvoidFmt, packedValue)[0] 1814 return unpackedValue
1815
1816 - def write_pointer(self, lpBaseAddress, unpackedValue):
1817 """ 1818 Writes a single pointer value to the memory of the process. 1819 1820 @note: Page permissions may be changed temporarily while writing. 1821 1822 @see: L{poke_pointer} 1823 1824 @type lpBaseAddress: int 1825 @param lpBaseAddress: Memory address to begin writing. 1826 1827 @type unpackedValue: int, long 1828 @param unpackedValue: Value to write. 1829 1830 @raise WindowsError: On error an exception is raised. 1831 """ 1832 lpvoidLength = win32.sizeof(win32.LPVOID) 1833 if lpvoidLength == 4: 1834 lpvoidFmt = '<L' 1835 else: 1836 lpvoidFmt = '<Q' 1837 packedValue = struct.pack(lpvoidFmt, unpackedValue) 1838 self.write(lpBaseAddress, packedValue)
1839
1840 - def read_char(self, lpBaseAddress):
1841 """ 1842 Reads a single character to the memory of the process. 1843 1844 @see: L{write_char} 1845 1846 @type lpBaseAddress: int 1847 @param lpBaseAddress: Memory address to begin writing. 1848 1849 @rtype: int 1850 @return: Character value read from the process memory. 1851 1852 @raise WindowsError: On error an exception is raised. 1853 """ 1854 return ord( self.read(lpBaseAddress, 1) )
1855
1856 - def write_char(self, lpBaseAddress, char):
1857 """ 1858 Writes a single character to the memory of the process. 1859 1860 @note: Page permissions may be changed temporarily while writing. 1861 1862 @see: L{write_char} 1863 1864 @type lpBaseAddress: int 1865 @param lpBaseAddress: Memory address to begin writing. 1866 1867 @type char: int 1868 @param char: Character to write. 1869 1870 @raise WindowsError: On error an exception is raised. 1871 """ 1872 self.write(lpBaseAddress, chr(char))
1873
1874 - def read_structure(self, lpBaseAddress, stype):
1875 """ 1876 Reads a ctypes structure from the memory of the process. 1877 1878 @see: L{read} 1879 1880 @type lpBaseAddress: int 1881 @param lpBaseAddress: Memory address to begin reading. 1882 1883 @type stype: class ctypes.Structure or a subclass. 1884 @param stype: Structure definition. 1885 1886 @rtype: int 1887 @return: Structure instance filled in with data 1888 read from the process memory. 1889 1890 @raise WindowsError: On error an exception is raised. 1891 """ 1892 if type(lpBaseAddress) not in (type(0), type(0L)): 1893 lpBaseAddress = ctypes.cast(lpBaseAddress, ctypes.c_void_p) 1894 data = self.read(lpBaseAddress, ctypes.sizeof(stype)) 1895 buff = ctypes.create_string_buffer(data) 1896 ptr = ctypes.cast(ctypes.pointer(buff), ctypes.POINTER(stype)) 1897 return ptr.contents
1898 1899 # XXX TODO 1900 ## def write_structure(self, lpBaseAddress, sStructure): 1901 ## """ 1902 ## Writes a ctypes structure into the memory of the process. 1903 ## 1904 ## @note: Page permissions may be changed temporarily while writing. 1905 ## 1906 ## @see: L{write} 1907 ## 1908 ## @type lpBaseAddress: int 1909 ## @param lpBaseAddress: Memory address to begin writing. 1910 ## 1911 ## @type sStructure: ctypes.Structure or a subclass' instance. 1912 ## @param sStructure: Structure definition. 1913 ## 1914 ## @rtype: int 1915 ## @return: Structure instance filled in with data 1916 ## read from the process memory. 1917 ## 1918 ## @raise WindowsError: On error an exception is raised. 1919 ## """ 1920 ## size = ctypes.sizeof(sStructure) 1921 ## data = ctypes.create_string_buffer("", size = size) 1922 ## win32.CopyMemory(ctypes.byref(data), ctypes.byref(sStructure), size) 1923 ## self.write(lpBaseAddress, data.raw) 1924
1925 - def read_string(self, lpBaseAddress, nChars, fUnicode = False):
1926 """ 1927 Reads an ASCII or Unicode string 1928 from the address space of the process. 1929 1930 @see: L{read} 1931 1932 @type lpBaseAddress: int 1933 @param lpBaseAddress: Memory address to begin reading. 1934 1935 @type nChars: int 1936 @param nChars: String length to read, in characters. 1937 Remember that Unicode strings have two byte characters. 1938 1939 @type fUnicode: bool 1940 @param fUnicode: C{True} is the string is expected to be Unicode, 1941 C{False} if it's expected to be ANSI. 1942 1943 @rtype: str, unicode 1944 @return: String read from the process memory space. 1945 1946 @raise WindowsError: On error an exception is raised. 1947 """ 1948 if fUnicode: 1949 nChars = nChars * 2 1950 szString = self.read(lpBaseAddress, nChars) 1951 if fUnicode: 1952 szString = unicode(szString, 'U16', 'ignore') 1953 return szString
1954 1955 #------------------------------------------------------------------------------ 1956
1957 - def peek(self, lpBaseAddress, nSize):
1958 """ 1959 Reads the memory of the process. 1960 1961 @see: L{read} 1962 1963 @type lpBaseAddress: int 1964 @param lpBaseAddress: Memory address to begin reading. 1965 1966 @type nSize: int 1967 @param nSize: Number of bytes to read. 1968 1969 @rtype: str 1970 @return: Bytes read from the process memory. 1971 Returns an empty string on error. 1972 """ 1973 # XXX TODO 1974 # + Maybe change page permissions before trying to read? 1975 # + Maybe use mquery instead of get_memory_map? 1976 # (less syscalls if we break out of the loop earlier) 1977 data = '' 1978 if nSize > 0: 1979 try: 1980 for mbi in self.get_memory_map(lpBaseAddress, 1981 lpBaseAddress + nSize): 1982 if not mbi.is_readable(): 1983 nSize = mbi.BaseAddress - lpBaseAddress 1984 break 1985 if nSize > 0: 1986 data = win32.ReadProcessMemory(self.get_handle(), 1987 lpBaseAddress, nSize) 1988 except WindowsError: 1989 pass 1990 return data
1991
1992 - def poke(self, lpBaseAddress, lpBuffer):
1993 """ 1994 Writes to the memory of the process. 1995 1996 @note: Page permissions may be changed temporarily while writing. 1997 1998 @see: L{write} 1999 2000 @type lpBaseAddress: int 2001 @param lpBaseAddress: Memory address to begin writing. 2002 2003 @type lpBuffer: str 2004 @param lpBuffer: Bytes to write. 2005 2006 @rtype: int 2007 @return: Number of bytes written. 2008 May be less than the number of bytes to write. 2009 """ 2010 hProcess = self.get_handle() 2011 mbi = self.mquery(lpBaseAddress) 2012 if not mbi.has_content(): 2013 raise ctypes.WinError(win32.ERROR_INVALID_ADDRESS) 2014 if mbi.is_image() or mbi.is_mapped(): 2015 prot = win32.PAGE_WRITECOPY 2016 elif mbi.is_writeable(): 2017 prot = None 2018 elif mbi.is_executable(): 2019 prot = win32.PAGE_EXECUTE_READWRITE 2020 else: 2021 prot = win32.PAGE_READWRITE 2022 if prot is not None: 2023 self.mprotect(lpBaseAddress, len(lpBuffer), prot) 2024 try: 2025 r = win32.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer) 2026 finally: 2027 if prot is not None: 2028 self.mprotect(lpBaseAddress, len(lpBuffer), mbi.Protect) 2029 return r
2030
2031 - def peek_uint(self, lpBaseAddress):
2032 """ 2033 Reads a single unsigned integer from the memory of the process. 2034 2035 @see: L{read_uint} 2036 2037 @type lpBaseAddress: int 2038 @param lpBaseAddress: Memory address to begin reading. 2039 2040 @rtype: int 2041 @return: Integer value read from the process memory. 2042 Returns zero on error. 2043 """ 2044 dwordLength = win32.sizeof(win32.UINT) 2045 packedDword = self.peek(lpBaseAddress, dwordLength) 2046 if len(packedDword) < dwordLength: 2047 packedDword += '\x00' * (dwordLength - len(packedDword)) 2048 unpackedDword = struct.unpack('<L', packedDword)[0] 2049 return unpackedDword
2050
2051 - def poke_uint(self, lpBaseAddress, unpackedDword):
2052 """ 2053 Writes a single unsigned integer to the memory of the process. 2054 2055 @note: Page permissions may be changed temporarily while writing. 2056 2057 @see: L{write_uint} 2058 2059 @type lpBaseAddress: int 2060 @param lpBaseAddress: Memory address to begin writing. 2061 2062 @type unpackedDword: int, long 2063 @param unpackedDword: Value to write. 2064 2065 @rtype: int 2066 @return: Number of bytes written. 2067 May be less than the number of bytes to write. 2068 """ 2069 packedDword = struct.pack('<L', unpackedDword) 2070 dwBytesWritten = self.poke(lpBaseAddress, packedDword) 2071 return dwBytesWritten
2072
2073 - def peek_pointer(self, lpBaseAddress):
2074 """ 2075 Reads a single pointer value from the memory of the process. 2076 2077 @see: L{read_pointer} 2078 2079 @type lpBaseAddress: int 2080 @param lpBaseAddress: Memory address to begin reading. 2081 2082 @rtype: int 2083 @return: Pointer value read from the process memory. 2084 Returns zero on error. 2085 """ 2086 lpvoidLength = win32.sizeof(win32.LPVOID) 2087 packedValue = self.read(lpBaseAddress, lpvoidLength) 2088 if len(packedValue) < lpvoidLength: 2089 packedValue += '\x00' * (lpvoidLength - len(packedValue)) 2090 if lpvoidLength == 4: 2091 lpvoidFmt = '<L' 2092 else: 2093 lpvoidFmt = '<Q' 2094 unpackedValue = struct.unpack(lpvoidFmt, packedValue)[0] 2095 return unpackedValue
2096
2097 - def poke_pointer(self, lpBaseAddress, unpackedValue):
2098 """ 2099 Writes a single pointer value to the memory of the process. 2100 2101 @note: Page permissions may be changed temporarily while writing. 2102 2103 @see: L{write_pointer} 2104 2105 @type lpBaseAddress: int 2106 @param lpBaseAddress: Memory address to begin writing. 2107 2108 @type unpackedValue: int, long 2109 @param unpackedValue: Value to write. 2110 2111 @rtype: int 2112 @return: Number of bytes written. 2113 May be less than the number of bytes to write. 2114 """ 2115 lpvoidLength = win32.sizeof(win32.LPVOID) 2116 if lpvoidLength == 4: 2117 lpvoidFmt = '<L' 2118 else: 2119 lpvoidFmt = '<Q' 2120 packedValue = struct.pack(lpvoidFmt, unpackedValue) 2121 dwBytesWritten = self.poke(lpBaseAddress, packedValue) 2122 return dwBytesWritten
2123
2124 - def peek_char(self, lpBaseAddress):
2125 """ 2126 Reads a single character from the memory of the process. 2127 2128 @see: L{read_char} 2129 2130 @type lpBaseAddress: int 2131 @param lpBaseAddress: Memory address to begin reading. 2132 2133 @rtype: int 2134 @return: Character read from the process memory. 2135 Returns zero on error. 2136 """ 2137 char = self.peek(lpBaseAddress, 1) 2138 if char: 2139 return ord(char) 2140 return 0
2141
2142 - def poke_char(self, lpBaseAddress, char):
2143 """ 2144 Writes a single character to the memory of the process. 2145 2146 @note: Page permissions may be changed temporarily while writing. 2147 2148 @see: L{write_char} 2149 2150 @type lpBaseAddress: int 2151 @param lpBaseAddress: Memory address to begin writing. 2152 2153 @type char: str 2154 @param char: Character to write. 2155 2156 @rtype: int 2157 @return: Number of bytes written. 2158 May be less than the number of bytes to write. 2159 """ 2160 return self.poke(lpBaseAddress, chr(char))
2161
2162 - def peek_string(self, lpBaseAddress, fUnicode = False, dwMaxSize = 0x1000):
2163 """ 2164 Tries to read an ASCII or Unicode string 2165 from the address space of the process. 2166 2167 @see: L{peek} 2168 2169 @type lpBaseAddress: int 2170 @param lpBaseAddress: Memory address to begin reading. 2171 2172 @type fUnicode: bool 2173 @param fUnicode: C{True} is the string is expected to be Unicode, 2174 C{False} if it's expected to be ANSI. 2175 2176 @type dwMaxSize: int 2177 @param dwMaxSize: Maximum allowed string length to read, in bytes. 2178 2179 @rtype: str, unicode 2180 @return: String read from the process memory space. 2181 It B{doesn't} include the terminating null character. 2182 Returns an empty string on failure. 2183 """ 2184 2185 # Validate the parameters. 2186 if not lpBaseAddress or dwMaxSize == 0: 2187 if fUnicode: 2188 return u'' 2189 return '' 2190 if not dwMaxSize: 2191 dwMaxSize = 0x1000 2192 2193 # Read the string. 2194 szString = self.peek(lpBaseAddress, dwMaxSize) 2195 2196 # If the string is Unicode... 2197 if fUnicode: 2198 2199 # Decode the string. 2200 szString = unicode(szString, 'U16', 'replace') 2201 ## try: 2202 ## szString = unicode(szString, 'U16') 2203 ## except UnicodeDecodeError: 2204 ## szString = struct.unpack('H' * (len(szString) / 2), szString) 2205 ## szString = [ unichr(c) for c in szString ] 2206 ## szString = u''.join(szString) 2207 2208 # Truncate the string when the first null char is found. 2209 szString = szString[ : szString.find(u'\0') ] 2210 2211 # If the string is ANSI... 2212 else: 2213 2214 # Truncate the string when the first null char is found. 2215 szString = szString[ : szString.find('\0') ] 2216 2217 # Return the decoded string. 2218 return szString
2219 2220 #------------------------------------------------------------------------------ 2221
2222 - def malloc(self, dwSize, lpAddress = None):
2223 """ 2224 Allocates memory into the address space of the process. 2225 2226 @see: L{free} 2227 2228 @type dwSize: int 2229 @param dwSize: Number of bytes to allocate. 2230 2231 @type lpAddress: int 2232 @param lpAddress: (Optional) 2233 Desired address for the newly allocated memory. 2234 This is only a hint, the memory could still be allocated somewhere 2235 else. 2236 2237 @rtype: int 2238 @return: Address of the newly allocated memory. 2239 2240 @raise WindowsError: On error an exception is raised. 2241 """ 2242 return win32.VirtualAllocEx(self.get_handle(), lpAddress, dwSize)
2243
2244 - def mprotect(self, lpAddress, dwSize, flNewProtect):
2245 """ 2246 Set memory protection in the address space of the process. 2247 2248 @see: U{http://msdn.microsoft.com/en-us/library/aa366899.aspx} 2249 2250 @type lpAddress: int 2251 @param lpAddress: Address of memory to protect. 2252 2253 @type dwSize: int 2254 @param dwSize: Number of bytes to protect. 2255 2256 @type flNewProtect: int 2257 @param flNewProtect: New protect flags. 2258 2259 @rtype: int 2260 @return: Old protect flags. 2261 2262 @raise WindowsError: On error an exception is raised. 2263 """ 2264 return win32.VirtualProtectEx(self.get_handle(), lpAddress, dwSize, 2265 flNewProtect)
2266
2267 - def mquery(self, lpAddress):
2268 """ 2269 Query memory information from the address space of the process. 2270 Returns a L{win32.MemoryBasicInformation} object. 2271 2272 @see: U{http://msdn.microsoft.com/en-us/library/aa366907(VS.85).aspx} 2273 2274 @type lpAddress: int 2275 @param lpAddress: Address of memory to query. 2276 2277 @rtype: L{win32.MemoryBasicInformation} 2278 @return: Memory region information. 2279 2280 @raise WindowsError: On error an exception is raised. 2281 """ 2282 return win32.VirtualQueryEx(self.get_handle(), lpAddress)
2283
2284 - def free(self, lpAddress, dwSize = 0):
2285 """ 2286 Frees memory from the address space of the process. 2287 2288 @see: L{malloc} 2289 2290 @type lpAddress: int 2291 @param lpAddress: Address of memory to free. 2292 2293 @type dwSize: int 2294 @param dwSize: (Optional) Number of bytes to free. 2295 2296 @raise WindowsError: On error an exception is raised. 2297 """ 2298 win32.VirtualFreeEx(self.get_handle(), lpAddress, dwSize)
2299 2300 #------------------------------------------------------------------------------ 2301
2302 - def is_pointer(self, address):
2303 """ 2304 Determines if an address is a valid code or data pointer. 2305 2306 That is, the address must be valid and must point to code or data in 2307 the target process. 2308 2309 @type address: int 2310 @param address: Memory address to query. 2311 2312 @rtype: bool 2313 @return: C{True} if the address is a valid code or data pointer. 2314 2315 @raise WindowsError: An exception is raised on error. 2316 """ 2317 try: 2318 mbi = self.mquery(address) 2319 except WindowsError, e: 2320 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2321 return False 2322 raise 2323 return mbi.has_content()
2324
2325 - def is_address_valid(self, address):
2326 """ 2327 Determines if an address is a valid user mode address. 2328 2329 @type address: int 2330 @param address: Memory address to query. 2331 2332 @rtype: bool 2333 @return: C{True} if the address is a valid user mode address. 2334 2335 @raise WindowsError: An exception is raised on error. 2336 """ 2337 try: 2338 mbi = self.mquery(address) 2339 except WindowsError, e: 2340 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2341 return False 2342 raise 2343 return True
2344
2345 - def is_address_free(self, address):
2346 """ 2347 Determines if an address belongs to a free page. 2348 2349 @note: Returns always C{False} for kernel mode addresses. 2350 2351 @type address: int 2352 @param address: Memory address to query. 2353 2354 @rtype: bool 2355 @return: C{True} if the address belongs to a free page. 2356 2357 @raise WindowsError: An exception is raised on error. 2358 """ 2359 try: 2360 mbi = self.mquery(address) 2361 except WindowsError, e: 2362 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2363 return False 2364 raise 2365 return mbi.is_free()
2366
2367 - def is_address_reserved(self, address):
2368 """ 2369 Determines if an address belongs to a reserved page. 2370 2371 @note: Returns always C{False} for kernel mode addresses. 2372 2373 @type address: int 2374 @param address: Memory address to query. 2375 2376 @rtype: bool 2377 @return: C{True} if the address belongs to a reserved page. 2378 2379 @raise WindowsError: An exception is raised on error. 2380 """ 2381 try: 2382 mbi = self.mquery(address) 2383 except WindowsError, e: 2384 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2385 return False 2386 raise 2387 return mbi.is_reserved()
2388
2389 - def is_address_commited(self, address):
2390 """ 2391 Determines if an address belongs to a commited page. 2392 2393 @note: Returns always C{False} for kernel mode addresses. 2394 2395 @type address: int 2396 @param address: Memory address to query. 2397 2398 @rtype: bool 2399 @return: C{True} if the address belongs to a commited page. 2400 2401 @raise WindowsError: An exception is raised on error. 2402 """ 2403 try: 2404 mbi = self.mquery(address) 2405 except WindowsError, e: 2406 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2407 return False 2408 raise 2409 return mbi.is_commited()
2410
2411 - def is_address_guard(self, address):
2412 """ 2413 Determines if an address belongs to a guard page. 2414 2415 @note: Returns always C{False} for kernel mode addresses. 2416 2417 @type address: int 2418 @param address: Memory address to query. 2419 2420 @rtype: bool 2421 @return: C{True} if the address belongs to a guard page. 2422 2423 @raise WindowsError: An exception is raised on error. 2424 """ 2425 try: 2426 mbi = self.mquery(address) 2427 except WindowsError, e: 2428 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2429 return False 2430 raise 2431 return mbi.is_guard()
2432
2433 - def is_address_readable(self, address):
2434 """ 2435 Determines if an address belongs to a commited and readable page. 2436 The page may or may not have additional permissions. 2437 2438 @note: Returns always C{False} for kernel mode addresses. 2439 2440 @type address: int 2441 @param address: Memory address to query. 2442 2443 @rtype: bool 2444 @return: 2445 C{True} if the address belongs to a commited and readable page. 2446 2447 @raise WindowsError: An exception is raised on error. 2448 """ 2449 try: 2450 mbi = self.mquery(address) 2451 except WindowsError, e: 2452 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2453 return False 2454 raise 2455 return mbi.is_readable()
2456
2457 - def is_address_writeable(self, address):
2458 """ 2459 Determines if an address belongs to a commited and writeable page. 2460 The page may or may not have additional permissions. 2461 2462 @note: Returns always C{False} for kernel mode addresses. 2463 2464 @type address: int 2465 @param address: Memory address to query. 2466 2467 @rtype: bool 2468 @return: 2469 C{True} if the address belongs to a commited and writeable page. 2470 2471 @raise WindowsError: An exception is raised on error. 2472 """ 2473 try: 2474 mbi = self.mquery(address) 2475 except WindowsError, e: 2476 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2477 return False 2478 raise 2479 return mbi.is_writeable()
2480
2481 - def is_address_copy_on_write(self, address):
2482 """ 2483 Determines if an address belongs to a commited, copy-on-write page. 2484 The page may or may not have additional permissions. 2485 2486 @note: Returns always C{False} for kernel mode addresses. 2487 2488 @type address: int 2489 @param address: Memory address to query. 2490 2491 @rtype: bool 2492 @return: 2493 C{True} if the address belongs to a commited, copy-on-write page. 2494 2495 @raise WindowsError: An exception is raised on error. 2496 """ 2497 try: 2498 mbi = self.mquery(address) 2499 except WindowsError, e: 2500 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2501 return False 2502 raise 2503 return mbi.is_copy_on_write()
2504
2505 - def is_address_executable(self, address):
2506 """ 2507 Determines if an address belongs to a commited and executable page. 2508 The page may or may not have additional permissions. 2509 2510 @note: Returns always C{False} for kernel mode addresses. 2511 2512 @type address: int 2513 @param address: Memory address to query. 2514 2515 @rtype: bool 2516 @return: 2517 C{True} if the address belongs to a commited and executable page. 2518 2519 @raise WindowsError: An exception is raised on error. 2520 """ 2521 try: 2522 mbi = self.mquery(address) 2523 except WindowsError, e: 2524 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2525 return False 2526 raise 2527 return mbi.is_executable()
2528
2529 - def is_address_executable_and_writeable(self, address):
2530 """ 2531 Determines if an address belongs to a commited, writeable and 2532 executable page. The page may or may not have additional permissions. 2533 2534 Looking for writeable and executable pages is important when 2535 exploiting a software vulnerability. 2536 2537 @note: Returns always C{False} for kernel mode addresses. 2538 2539 @type address: int 2540 @param address: Memory address to query. 2541 2542 @rtype: bool 2543 @return: 2544 C{True} if the address belongs to a commited, writeable and 2545 executable page. 2546 2547 @raise WindowsError: An exception is raised on error. 2548 """ 2549 try: 2550 mbi = self.mquery(address) 2551 except WindowsError, e: 2552 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2553 return False 2554 raise 2555 return mbi.is_executable_and_writeable()
2556
2557 - def is_buffer(self, address, size):
2558 """ 2559 Determines if the given memory area is a valid code or data buffer. 2560 2561 @note: Returns always C{False} for kernel mode addresses. 2562 2563 @see: L{mquery} 2564 2565 @type address: int 2566 @param address: Memory address. 2567 2568 @type size: int 2569 @param size: Number of bytes. Must be greater than zero. 2570 2571 @rtype: bool 2572 @return: C{True} if the memory area is a valid code or data buffer, 2573 C{False} otherwise. 2574 2575 @raise ValueError: The size argument must be greater than zero. 2576 @raise WindowsError: On error an exception is raised. 2577 """ 2578 if size <= 0: 2579 raise ValueError, "the size argument must be greater than zero" 2580 while size > 0: 2581 try: 2582 mbi = self.mquery(address) 2583 except WindowsError, e: 2584 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2585 return False 2586 raise 2587 if not mbi.has_content(): 2588 return False 2589 size = size - mbi.RegionSize 2590 return True
2591
2592 - def is_buffer_readable(self, address, size):
2593 """ 2594 Determines if the given memory area is readable. 2595 2596 @note: Returns always C{False} for kernel mode addresses. 2597 2598 @see: L{mquery} 2599 2600 @type address: int 2601 @param address: Memory address. 2602 2603 @type size: int 2604 @param size: Number of bytes. Must be greater than zero. 2605 2606 @rtype: bool 2607 @return: C{True} if the memory area is readable, C{False} otherwise. 2608 2609 @raise ValueError: The size argument must be greater than zero. 2610 @raise WindowsError: On error an exception is raised. 2611 """ 2612 if size <= 0: 2613 raise ValueError, "the size argument must be greater than zero" 2614 while size > 0: 2615 try: 2616 mbi = self.mquery(address) 2617 except WindowsError, e: 2618 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2619 return False 2620 raise 2621 if not mbi.is_readable(): 2622 return False 2623 size = size - mbi.RegionSize 2624 return True
2625
2626 - def is_buffer_writeable(self, address, size):
2627 """ 2628 Determines if the given memory area is writeable. 2629 2630 @note: Returns always C{False} for kernel mode addresses. 2631 2632 @see: L{mquery} 2633 2634 @type address: int 2635 @param address: Memory address. 2636 2637 @type size: int 2638 @param size: Number of bytes. Must be greater than zero. 2639 2640 @rtype: bool 2641 @return: C{True} if the memory area is writeable, C{False} otherwise. 2642 2643 @raise ValueError: The size argument must be greater than zero. 2644 @raise WindowsError: On error an exception is raised. 2645 """ 2646 if size <= 0: 2647 raise ValueError, "the size argument must be greater than zero" 2648 while size > 0: 2649 try: 2650 mbi = self.mquery(address) 2651 except WindowsError, e: 2652 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2653 return False 2654 raise 2655 if not mbi.is_writeable(): 2656 return False 2657 size = size - mbi.RegionSize 2658 return True
2659
2660 - def is_buffer_copy_on_write(self, address, size):
2661 """ 2662 Determines if the given memory area is marked as copy-on-write. 2663 2664 @note: Returns always C{False} for kernel mode addresses. 2665 2666 @see: L{mquery} 2667 2668 @type address: int 2669 @param address: Memory address. 2670 2671 @type size: int 2672 @param size: Number of bytes. Must be greater than zero. 2673 2674 @rtype: bool 2675 @return: C{True} if the memory area is marked as copy-on-write, 2676 C{False} otherwise. 2677 2678 @raise ValueError: The size argument must be greater than zero. 2679 @raise WindowsError: On error an exception is raised. 2680 """ 2681 if size <= 0: 2682 raise ValueError, "the size argument must be greater than zero" 2683 while size > 0: 2684 try: 2685 mbi = self.mquery(address) 2686 except WindowsError, e: 2687 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2688 return False 2689 raise 2690 if not mbi.is_copy_on_write(): 2691 return False 2692 size = size - mbi.RegionSize 2693 return True
2694
2695 - def is_buffer_executable(self, address, size):
2696 """ 2697 Determines if the given memory area is executable. 2698 2699 @note: Returns always C{False} for kernel mode addresses. 2700 2701 @see: L{mquery} 2702 2703 @type address: int 2704 @param address: Memory address. 2705 2706 @type size: int 2707 @param size: Number of bytes. Must be greater than zero. 2708 2709 @rtype: bool 2710 @return: C{True} if the memory area is executable, C{False} otherwise. 2711 2712 @raise ValueError: The size argument must be greater than zero. 2713 @raise WindowsError: On error an exception is raised. 2714 """ 2715 if size <= 0: 2716 raise ValueError, "the size argument must be greater than zero" 2717 while size > 0: 2718 try: 2719 mbi = self.mquery(address) 2720 except WindowsError, e: 2721 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2722 return False 2723 raise 2724 if not mbi.is_executable(): 2725 return False 2726 size = size - mbi.RegionSize 2727 return True
2728
2729 - def is_buffer_executable_and_writeable(self, address, size):
2730 """ 2731 Determines if the given memory area is writeable and executable. 2732 2733 Looking for writeable and executable pages is important when 2734 exploiting a software vulnerability. 2735 2736 @note: Returns always C{False} for kernel mode addresses. 2737 2738 @see: L{mquery} 2739 2740 @type address: int 2741 @param address: Memory address. 2742 2743 @type size: int 2744 @param size: Number of bytes. Must be greater than zero. 2745 2746 @rtype: bool 2747 @return: C{True} if the memory area is writeable and executable, 2748 C{False} otherwise. 2749 2750 @raise ValueError: The size argument must be greater than zero. 2751 @raise WindowsError: On error an exception is raised. 2752 """ 2753 if size <= 0: 2754 raise ValueError, "the size argument must be greater than zero" 2755 while size > 0: 2756 try: 2757 mbi = self.mquery(address) 2758 except WindowsError, e: 2759 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2760 return False 2761 raise 2762 if not mbi.is_executable(): 2763 return False 2764 size = size - mbi.RegionSize 2765 return True
2766
2767 - def get_memory_map(self, minAddr = None, maxAddr = None):
2768 """ 2769 Produces a memory map to the process address space. 2770 Optionally restrict the map to the given address range. 2771 2772 @see: L{mquery} 2773 2774 @type minAddr: int 2775 @param minAddr: (Optional) Starting address in address range to query. 2776 2777 @type maxAddr: int 2778 @param maxAddr: (Optional) Ending address in address range to query. 2779 2780 @rtype: list( L{win32.MemoryBasicInformation} ) 2781 @return: List of memory region information objects. 2782 """ 2783 if minAddr is None: 2784 minAddr = 0 2785 if maxAddr is None: 2786 maxAddr = win32.LPVOID(-1).value # XXX HACK 2787 if minAddr > maxAddr: 2788 minAddr, maxAddr = maxAddr, minAddr 2789 minAddr = MemoryAddresses.align_address_to_page_start(minAddr) 2790 if maxAddr != MemoryAddresses.align_address_to_page_start(maxAddr): 2791 maxAddr = MemoryAddresses.align_address_to_page_end(maxAddr) 2792 prevAddr = minAddr - 1 2793 currentAddr = minAddr 2794 memoryMap = list() 2795 while currentAddr < maxAddr and currentAddr > prevAddr: 2796 try: 2797 mbi = self.mquery(currentAddr) 2798 except WindowsError, e: 2799 if win32.winerror(e) == win32.ERROR_INVALID_PARAMETER: 2800 break 2801 raise 2802 memoryMap.append(mbi) 2803 prevAddr = currentAddr 2804 currentAddr = mbi.BaseAddress + mbi.RegionSize 2805 return memoryMap
2806
2807 - def get_mapped_filenames(self, memoryMap = None):
2808 """ 2809 Retrieves the filenames for memory mapped files in the debugee. 2810 2811 @type memoryMap: list( L{win32.MemoryBasicInformation} ) 2812 @param memoryMap: (Optional) Memory map returned by L{get_memory_map}. 2813 If not given, the current memory map is used. 2814 2815 @rtype: dict( int S{->} str ) 2816 @return: Dictionary mapping memory addresses to file names. 2817 Native filenames are converted to Win32 filenames when possible. 2818 """ 2819 hProcess = self.get_handle() 2820 if not memoryMap: 2821 memoryMap = self.get_memory_map() 2822 mappedFilenames = dict() 2823 for mbi in memoryMap: 2824 2825 # this check is redundant, but it saves an API call 2826 # just comment it out if it gives problems 2827 if mbi.Type not in (win32.MEM_IMAGE, win32.MEM_MAPPED): 2828 continue 2829 2830 baseAddress = mbi.BaseAddress 2831 fileName = "" 2832 try: 2833 fileName = win32.GetMappedFileName(hProcess, baseAddress) 2834 fileName = PathOperations.native_to_win32_pathname(fileName) 2835 except WindowsError, e: 2836 ## print str(e) # XXX DEBUG 2837 pass 2838 mappedFilenames[baseAddress] = fileName 2839 return mappedFilenames
2840
2841 - def generate_memory_snapshot(self, minAddr = None, maxAddr = None):
2842 """ 2843 Returns a generator that allows you to iterate through the memory 2844 contents of a process. 2845 2846 It's basically the same as the L{take_memory_snapshot} method, but it 2847 takes the snapshot of each memory region as it goes, as opposed to 2848 taking the whole snapshot at once. This allows you to work with very 2849 large snapshots without a significant performance penalty. 2850 2851 Example:: 2852 # Print the memory contents of a process. 2853 process.suspend() 2854 try: 2855 snapshot = process.generate_memory_snapshot() 2856 for mbi in snapshot: 2857 print HexDump.hexblock(mbi.content, mbi.BaseAddress) 2858 finally: 2859 process.resume() 2860 2861 The downside of this is the process must remain suspended while 2862 iterating the snapshot, otherwise strange things may happen. 2863 2864 The snapshot can be iterated more than once. Each time it's iterated 2865 the memory contents of the process will be fetched again. 2866 2867 You can also iterate the memory of a dead process, just as long as the 2868 last open handle to it hasn't been closed. 2869 2870 @see: L{take_memory_snapshot} 2871 2872 @type minAddr: int 2873 @param minAddr: (Optional) Starting address in address range to query. 2874 2875 @type maxAddr: int 2876 @param maxAddr: (Optional) Ending address in address range to query. 2877 2878 @rtype: generator of L{win32.MemoryBasicInformation} 2879 @return: Generator that when iterated returns memory region information 2880 objects. Two extra properties are added to these objects: 2881 - C{filename}: Mapped filename, or C{None}. 2882 - C{content}: Memory contents, or C{None}. 2883 """ 2884 return Regenerator(self.__generate_memory_snapshot, minAddr, maxAddr)
2885
2886 - def __generate_memory_snapshot(self, minAddr = None, maxAddr = None):
2887 """ 2888 Internally used by L{generate_memory_snapshot}. 2889 """ 2890 2891 # One may feel tempted to include calls to self.suspend() and 2892 # self.resume() here, but that wouldn't work on a dead process. 2893 # It also wouldn't be needed when debugging since the process is 2894 # already suspended when the debug event arrives. So it's up to 2895 # the user to suspend the process if needed. 2896 2897 # Get the memory map. 2898 memory = self.get_memory_map(minAddr, maxAddr) 2899 2900 # Abort if the map couldn't be retrieved. 2901 if not memory: 2902 return 2903 2904 # Get the mapped filenames. 2905 filenames = self.get_mapped_filenames(memory) 2906 2907 # Trim the first memory information block if needed. 2908 if minAddr is not None: 2909 minAddr = MemoryAddresses.align_address_to_page_start(minAddr) 2910 mbi = memory[0] 2911 if mbi.BaseAddress < minAddr: 2912 mbi.RegionSize = mbi.BaseAddress + mbi.RegionSize - minAddr 2913 mbi.BaseAddress = minAddr 2914 2915 # Trim the last memory information block if needed. 2916 if maxAddr is not None: 2917 if maxAddr != MemoryAddresses.align_address_to_page_start(maxAddr): 2918 maxAddr = MemoryAddresses.align_address_to_page_end(maxAddr) 2919 mbi = memory[-1] 2920 if mbi.BaseAddress + mbi.RegionSize > maxAddr: 2921 mbi.RegionSize = maxAddr - mbi.BaseAddress 2922 2923 # Read the contents of each block and yield it. 2924 while memory: 2925 mbi = memory.pop(0) # so the garbage collector can take it 2926 mbi.filename = filenames.get(mbi.BaseAddress, None) 2927 if mbi.has_content(): 2928 mbi.content = self.read(mbi.BaseAddress, mbi.RegionSize) 2929 else: 2930 mbi.content = None 2931 yield mbi
2932
2933 - def take_memory_snapshot(self, minAddr = None, maxAddr = None):
2934 """ 2935 Takes a snapshot of the memory contents of the process. 2936 2937 It's best if the process is suspended when taking the snapshot. 2938 Execution can be resumed afterwards. 2939 2940 You can also iterate the memory of a dead process, just as long as the 2941 last open handle to it hasn't been closed. 2942 2943 @warning: If the target process has a very big memory footprint, the 2944 resulting snapshot will be equally big. This may result in a severe 2945 performance penalty. 2946 2947 @see: L{generate_memory_snapshot} 2948 2949 @type minAddr: int 2950 @param minAddr: (Optional) Starting address in address range to query. 2951 2952 @type maxAddr: int 2953 @param maxAddr: (Optional) Ending address in address range to query. 2954 2955 @rtype: list( L{win32.MemoryBasicInformation} ) 2956 @return: List of memory region information objects. 2957 Two extra properties are added to these objects: 2958 - C{filename}: Mapped filename, or C{None}. 2959 - C{content}: Memory contents, or C{None}. 2960 """ 2961 return list( self.generate_memory_snapshot(minAddr, maxAddr) )
2962
2963 - def restore_memory_snapshot(self, snapshot, bSkipMappedFiles = True):
2964 """ 2965 Attempts to restore the memory state as it was when the given snapshot 2966 was taken. 2967 2968 @warning: Currently only the memory contents, state and protect bits 2969 are restored. Under some circumstances this method may fail (for 2970 example if memory was freed and then reused by a mapped file). 2971 2972 @type snapshot: list( L{win32.MemoryBasicInformation} ) 2973 @param snapshot: Memory snapshot returned by L{take_memory_snapshot}. 2974 Snapshots returned by L{generate_memory_snapshot} don't work here. 2975 2976 @type bSkipMappedFiles: bool 2977 @param bSkipMappedFiles: C{True} to avoid restoring the contents of 2978 memory mapped files, C{False} otherwise. Use with care! Setting 2979 this to C{False} can cause undesired side effects - changes to 2980 memory mapped files may be written to disk by the OS. Also note 2981 that most mapped files are typically executables and don't change, 2982 so trying to restore their contents is usually a waste of time. 2983 2984 @raise WindowsError: An error occured while restoring the snapshot. 2985 @raise RuntimeError: An error occured while restoring the snapshot. 2986 @raise TypeError: A snapshot of the wrong type was passed. 2987 """ 2988 if not isinstance(snapshot, list): 2989 raise TypeError, "Only snapshots returned by " \ 2990 "take_memory_snapshots() can be used here." 2991 2992 # Get the process handle. 2993 hProcess = self.get_handle() 2994 2995 # Freeze the process. 2996 self.suspend() 2997 try: 2998 2999 # For each memory region in the snapshot... 3000 for old_mbi in snapshot: 3001 3002 # If the region matches, restore it directly. 3003 new_mbi = self.mquery(old_mbi.BaseAddress) 3004 if new_mbi.BaseAddress == old_mbi.BaseAddress and new_mbi.RegionSize == old_mbi.RegionSize: 3005 self.__restore_mbi(hProcess, new_mbi, old_mbi) 3006 3007 # If the region doesn't match, restore it page by page. 3008 else: 3009 # We need a copy so we don't corrupt the snapshot. 3010 old_mbi = win32.MemoryBasicInformation(old_mbi) 3011 3012 # Get the overlapping range of pages. 3013 old_start = old_mbi.BaseAddress 3014 old_end = old_start + old_mbi.RegionSize 3015 new_start = new_mbi.BaseAddress 3016 new_end = new_start + new_mbi.RegionSize 3017 if old_start > new_start: 3018 start = old_start 3019 else: 3020 start = new_start 3021 if old_end < new_end: 3022 end = old_end 3023 else: 3024 end = new_end 3025 3026 # Restore each page in the overlapping range. 3027 step = System.pageSize 3028 old_mbi.RegionSize = step 3029 new_mbi.RegionSize = step 3030 address = start 3031 while address < end: 3032 old_mbi.BaseAddress = address 3033 new_mbi.BaseAddress = address 3034 self.__restore_mbi(hProcess, new_mbi, old_mbi) 3035 address = address + step 3036 3037 # Resume execution. 3038 finally: 3039 self.resume()
3040
3041 - def __restore_mbi(self, hProcess, new_mbi, old_mbi):
3042 """ 3043 Used internally by L{restore_memory_snapshot}. 3044 """ 3045 3046 ## print "Restoring %s-%s" % (HexDump.address(old_mbi.BaseAddress), HexDump.address(old_mbi.BaseAddress + old_mbi.RegionSize)) 3047 3048 # Restore the region state. 3049 if new_mbi.State != old_mbi.State: 3050 if new_mbi.is_free(): 3051 if old_mbi.is_reserved(): 3052 3053 # Free -> Reserved 3054 address = win32.VirtualAllocEx(hProcess, old_mbi.BaseAddress, old_mbi.RegionSize, win32.MEM_RESERVE, old_mbi.Protect) 3055 if address != old_mbi.BaseAddress: 3056 self.free(address) 3057 msg = "Error restoring region at address %s" 3058 msg = msg % HexDump(old_mbi.BaseAddress) 3059 raise RuntimeError, msg 3060 new_mbi.Protect = old_mbi.Protect # permissions already restored 3061 3062 else: # elif old_mbi.is_commited(): 3063 3064 # Free -> Commited 3065 address = win32.VirtualAllocEx(hProcess, old_mbi.BaseAddress, old_mbi.RegionSize, win32.MEM_RESERVE | win32.MEM_COMMIT, old_mbi.Protect) 3066 if address != old_mbi.BaseAddress: 3067 self.free(address) 3068 msg = "Error restoring region at address %s" 3069 msg = msg % HexDump(old_mbi.BaseAddress) 3070 raise RuntimeError, msg 3071 new_mbi.Protect = old_mbi.Protect # permissions already restored 3072 3073 elif new_mbi.is_reserved(): 3074 if old_mbi.is_commited(): 3075 3076 # Reserved -> Commited 3077 address = win32.VirtualAllocEx(hProcess, old_mbi.BaseAddress, old_mbi.RegionSize, win32.MEM_COMMIT, old_mbi.Protect) 3078 if address != old_mbi.BaseAddress: 3079 self.free(address) 3080 msg = "Error restoring region at address %s" 3081 msg = msg % HexDump(old_mbi.BaseAddress) 3082 raise RuntimeError, msg 3083 new_mbi.Protect = old_mbi.Protect # permissions already restored 3084 3085 else: # elif old_mbi.is_free(): 3086 3087 # Reserved -> Free 3088 win32.VirtualFreeEx(hProcess, old_mbi.BaseAddress, old_mbi.RegionSize, win32.MEM_RELEASE) 3089 3090 else: # elif new_mbi.is_commited(): 3091 if old_mbi.is_reserved(): 3092 3093 # Commited -> Reserved 3094 win32.VirtualFreeEx(hProcess, old_mbi.BaseAddress, old_mbi.RegionSize, win32.MEM_DECOMMIT) 3095 3096 else: # elif old_mbi.is_free(): 3097 3098 # Commited -> Free 3099 win32.VirtualFreeEx(hProcess, old_mbi.BaseAddress, old_mbi.RegionSize, win32.MEM_DECOMMIT | win32.MEM_RELEASE) 3100 3101 new_mbi.State = old_mbi.State 3102 3103 # Restore the region permissions. 3104 if old_mbi.is_commited() and old_mbi.Protect != new_mbi.Protect: 3105 win32.VirtualProtectEx(hProcess, old_mbi.BaseAddress, 3106 old_mbi.RegionSize, old_mbi.Protect) 3107 new_mbi.Protect = old_mbi.Protect 3108 3109 # Restore the region data. 3110 # Ignore write errors when the region belongs to a mapped file. 3111 if old_mbi.has_content(): 3112 if old_mbi.Type != 0: 3113 if not bSkipMappedFiles: 3114 self.poke(old_mbi.BaseAddress, old_mbi.content) 3115 else: 3116 self.write(old_mbi.BaseAddress, old_mbi.content) 3117 new_mbi.content = old_mbi.content
3118
3119 #============================================================================== 3120 3121 -class SymbolEnumerator (object):
3122 """ 3123 Internally used by L{SymbolContainer} to enumerate symbols in a module. 3124 """ 3125
3126 - def __init__(self):
3127 self.symbols = list()
3128
3129 - def __call__(self, SymbolName, SymbolAddress, SymbolSize, UserContext):
3130 """ 3131 Callback that receives symbols and stores them in a Python list. 3132 """ 3133 ## try: 3134 ## SymbolName = win32.UnDecorateSymbolName(SymbolName) 3135 ## except Exception, e: 3136 ## pass 3137 self.symbols.append( (SymbolName, SymbolAddress, SymbolSize) ) 3138 return win32.TRUE
3139
3140 -class SymbolContainer (object):
3141 """ 3142 Capability to contain symbols. Used by L{Module}. 3143 3144 @group Symbols: 3145 load_symbols, unload_symbols, get_symbols, iter_symbols, 3146 resolve_symbol, get_symbol_at_address 3147 """ 3148
3149 - def __init__(self):
3150 self.__symbols = list()
3151 3152 # XXX FIXME 3153 # I've been told sometimes the debugging symbols APIs don't correctly 3154 # handle redirected exports (for example ws2_32!recv). 3155 # I haven't been able to reproduce the bug yet.
3156 - def load_symbols(self):
3157 """ 3158 Loads the debugging symbols for a module. 3159 Automatically called by L{get_symbols}. 3160 """ 3161 hProcess = self.get_process().get_handle() 3162 hFile = self.hFile 3163 BaseOfDll = self.get_base() 3164 SizeOfDll = self.get_size() 3165 Enumerator = SymbolEnumerator() 3166 try: 3167 win32.SymInitialize(hProcess) 3168 SymOptions = win32.SymGetOptions() 3169 win32.SymSetOptions(SymOptions | \ 3170 win32.SYMOPT_ALLOW_ZERO_ADDRESS | \ 3171 win32.SYMOPT_CASE_INSENSITIVE | \ 3172 win32.SYMOPT_FAVOR_COMPRESSED | \ 3173 win32.SYMOPT_INCLUDE_32BIT_MODULES | \ 3174 win32.SYMOPT_UNDNAME) 3175 try: 3176 win32.SymSetOptions(SymOptions | win32.SYMOPT_ALLOW_ABSOLUTE_SYMBOLS) 3177 except WindowsError: 3178 pass 3179 try: 3180 try: 3181 win32.SymLoadModule64(hProcess, hFile, None, None, BaseOfDll, SizeOfDll) 3182 except WindowsError: 3183 ImageName = self.get_filename() 3184 win32.SymLoadModule64(hProcess, None, ImageName, None, BaseOfDll, SizeOfDll) 3185 try: 3186 win32.SymEnumerateSymbols64(hProcess, BaseOfDll, Enumerator) 3187 finally: 3188 win32.SymUnloadModule64(hProcess, BaseOfDll) 3189 finally: 3190 win32.SymCleanup(hProcess) 3191 except WindowsError, e: 3192 ## import traceback # XXX DEBUG 3193 ## traceback.print_exc() 3194 pass 3195 self.__symbols = Enumerator.symbols
3196
3197 - def unload_symbols(self):
3198 """ 3199 Unloads the debugging symbols for a module. 3200 """ 3201 self.__symbols = list()
3202
3203 - def get_symbols(self):
3204 """ 3205 Returns the debugging symbols for a module. 3206 The symbols are automatically loaded when needed. 3207 3208 @rtype: list of tuple( str, int, int ) 3209 @return: List of symbols. 3210 Each symbol is represented by a tuple that contains: 3211 - Symbol name 3212 - Symbol memory address 3213 - Symbol size in bytes 3214 """ 3215 if not self.__symbols: 3216 self.load_symbols() 3217 return list(self.__symbols)
3218
3219 - def iter_symbols(self):
3220 """ 3221 Returns an iterator for the debugging symbols in a module, 3222 in no particular order. 3223 The symbols are automatically loaded when needed. 3224 3225 @rtype: iterator of tuple( str, int, int ) 3226 @return: Iterator of symbols. 3227 Each symbol is represented by a tuple that contains: 3228 - Symbol name 3229 - Symbol memory address 3230 - Symbol size in bytes 3231 """ 3232 if not self.__symbols: 3233 self.load_symbols() 3234 return self.__symbols.__iter__()
3235
3236 - def resolve_symbol(self, symbol, bCaseSensitive = False):
3237 """ 3238 Resolves a debugging symbol's address. 3239 3240 @type symbol: str 3241 @param symbol: Name of the symbol to resolve. 3242 3243 @type bCaseSensitive: bool 3244 @param bCaseSensitive: C{True} for case sensitive matches, 3245 C{False} for case insensitive. 3246 3247 @rtype: int or None 3248 @return: Memory address of symbol. C{None} if not found. 3249 """ 3250 if bCaseSensitive: 3251 for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols(): 3252 if symbol == SymbolName: 3253 return SymbolAddress 3254 else: 3255 symbol = symbol.lower() 3256 for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols(): 3257 if symbol == SymbolName.lower(): 3258 return SymbolAddress
3259
3260 - def get_symbol_at_address(self, address):
3261 """ 3262 Tries to find the closest matching symbol for the given address. 3263 3264 @type address: int 3265 @param address: Memory address to query. 3266 3267 @rtype: None or tuple( str, int, int ) 3268 @return: Returns a tuple consisting of: 3269 - Name 3270 - Address 3271 - Size (in bytes) 3272 Returns C{None} if no symbol could be matched. 3273 """ 3274 found = None 3275 for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols(): 3276 if SymbolAddress > address: 3277 continue 3278 if SymbolAddress + SymbolSize > address: 3279 if not found or found[1] < SymbolAddress: 3280 found = (SymbolName, SymbolAddress, SymbolSize) 3281 return found
3282
3283 #============================================================================== 3284 3285 -class SymbolOperations (object):
3286 """ 3287 Encapsulates symbol operations capabilities. 3288 3289 Requires a L{ModuleContainer}. 3290 3291 @note: Labels are an approximated way of referencing memory locations 3292 across different executions of the same process, or different processes 3293 with common modules. They are not meant to be perfectly unique, and 3294 some errors may occur when multiple modules with the same name are 3295 loaded, or when module filenames can't be retrieved. 3296 3297 @group Labels: 3298 parse_label, 3299 split_label, 3300 sanitize_label, 3301 resolve_label, 3302 get_label_at_address, 3303 split_label_strict, 3304 split_label_fuzzy 3305 3306 @group Symbols: 3307 load_symbols, unload_symbols, get_symbols, iter_symbols, 3308 resolve_symbol, get_symbol_at_address 3309 3310 @group Debugging: 3311 is_system_defined_breakpoint, get_system_breakpoint, 3312 get_user_breakpoint, get_breakin_breakpoint, 3313 get_wow64_system_breakpoint, get_wow64_user_breakpoint, 3314 get_wow64_breakin_breakpoint 3315 """ 3316
3317 - def __init__(self):
3318 super(SymbolOperations, self).__init__() 3319 3320 # Replace split_label with the fuzzy version on object instances. 3321 self.split_label = self.__use_fuzzy_mode
3322 3323 @staticmethod
3324 - def parse_label(module = None, function = None, offset = None):
3325 """ 3326 Creates a label from a module and a function name, plus an offset. 3327 3328 @warning: This method only parses the label, it doesn't make sure the 3329 label actually points to a valid memory location. 3330 3331 @type module: None or str 3332 @param module: (Optional) Module name. 3333 3334 @type function: None, str or int 3335 @param function: (Optional) Function name or ordinal. 3336 3337 @type offset: None or int 3338 @param offset: (Optional) Offset value. 3339 3340 If C{function} is specified, offset from the function. 3341 3342 If C{function} is C{None}, offset from the module. 3343 3344 @rtype: str 3345 @return: 3346 Label representing the given function in the given module. 3347 3348 @raise ValueError: 3349 The module or function name contain invalid characters. 3350 """ 3351 3352 # TODO 3353 # Invalid characters should be escaped or filtered. 3354 3355 # Convert ordinals to strings. 3356 try: 3357 function = "#0x%x" % function 3358 except TypeError: 3359 pass 3360 3361 # Validate the parameters. 3362 if module is not None and ('!' in module or '+' in module): 3363 raise ValueError, "Invalid module name: %s" % module 3364 if function is not None and ('!' in function or '+' in function): 3365 raise ValueError, "Invalid function name: %s" % function 3366 3367 # Parse the label. 3368 if module: 3369 if function: 3370 if offset: 3371 label = "%s!%s+0x%x" % (module, function, offset) 3372 else: 3373 label = "%s!%s" % (module, function) 3374 else: 3375 if offset: 3376 ## label = "%s+0x%x!" % (module, offset) 3377 label = "%s!0x%x" % (module, offset) 3378 else: 3379 label = "%s!" % module 3380 else: 3381 if function: 3382 if offset: 3383 label = "!%s+0x%x" % (function, offset) 3384 else: 3385 label = "!%s" % function 3386 else: 3387 if offset: 3388 label = "0x%x" % offset 3389 else: 3390 label = "0x0" 3391 3392 return label
3393 3394 @staticmethod
3395 - def split_label_strict(label):
3396 """ 3397 Splits a label created with L{parse_label}. 3398 3399 To parse labels with a less strict syntax, use the L{split_label_fuzzy} 3400 method instead. 3401 3402 @warning: This method only parses the label, it doesn't make sure the 3403 label actually points to a valid memory location. 3404 3405 @type label: str 3406 @param label: Label to split. 3407 3408 @rtype: tuple( str or None, str or int or None, int or None ) 3409 @return: Tuple containing the C{module} name, 3410 the C{function} name or ordinal, and the C{offset} value. 3411 3412 If the label doesn't specify a module, 3413 then C{module} is C{None}. 3414 3415 If the label doesn't specify a function, 3416 then C{function} is C{None}. 3417 3418 If the label doesn't specify an offset, 3419 then C{offset} is C{0}. 3420 3421 @raise ValueError: The label is malformed. 3422 """ 3423 module = function = None 3424 offset = 0 3425 3426 # Special case: None 3427 if not label: 3428 label = "0x0" 3429 else: 3430 3431 # Remove all blanks. 3432 label = label.replace(' ', '') 3433 label = label.replace('\t', '') 3434 label = label.replace('\r', '') 3435 label = label.replace('\n', '') 3436 3437 # Special case: empty label. 3438 if not label: 3439 label = "0x0" 3440 3441 # * ! * 3442 if '!' in label: 3443 try: 3444 module, function = label.split('!') 3445 except ValueError: 3446 raise ValueError, "Malformed label: %s" % label 3447 3448 # module ! function 3449 if function: 3450 if '+' in module: 3451 raise ValueError, "Malformed label: %s" % label 3452 3453 # module ! function + offset 3454 if '+' in function: 3455 try: 3456 function, offset = function.split('+') 3457 except ValueError: 3458 raise ValueError, "Malformed label: %s" % label 3459 try: 3460 offset = HexInput.integer(offset) 3461 except ValueError: 3462 raise ValueError, "Malformed label: %s" % label 3463 else: 3464 3465 # module ! offset 3466 try: 3467 offset = HexInput.integer(function) 3468 function = None 3469 except ValueError: 3470 pass 3471 else: 3472 3473 # module + offset ! 3474 if '+' in module: 3475 try: 3476 module, offset = module.split('+') 3477 except ValueError: 3478 raise ValueError, "Malformed label: %s" % label 3479 try: 3480 offset = HexInput.integer(offset) 3481 except ValueError: 3482 raise ValueError, "Malformed label: %s" % label 3483 3484 else: 3485 3486 # module ! 3487 try: 3488 offset = HexInput.integer(module) 3489 module = None 3490 3491 # offset ! 3492 except ValueError: 3493 pass 3494 3495 if not module: 3496 module = None 3497 if not function: 3498 function = None 3499 3500 # * 3501 else: 3502 3503 # offset 3504 try: 3505 offset = HexInput.integer(label) 3506 3507 # # ordinal 3508 except ValueError: 3509 if label.startswith('#'): 3510 function = label 3511 try: 3512 HexInput.integer(function[1:]) 3513 3514 # module? 3515 # function? 3516 except ValueError: 3517 raise ValueError, "Ambiguous label: %s" % label 3518 3519 # module? 3520 # function? 3521 else: 3522 raise ValueError, "Ambiguous label: %s" % label 3523 3524 # Convert function ordinal strings into integers. 3525 if function and function.startswith('#'): 3526 try: 3527 function = HexInput.integer(function[1:]) 3528 except ValueError: 3529 pass 3530 3531 # Convert null offsets to None. 3532 if not offset: 3533 offset = None 3534 3535 return (module, function, offset)
3536
3537 - def split_label_fuzzy(self, label):
3538 """ 3539 Splits a label entered as user input. 3540 3541 It's more flexible in it's syntax parsing than the L{split_label_strict} 3542 method, as it allows the exclamation mark (B{C{!}}) to be omitted. The 3543 ambiguity is resolved by searching the modules in the snapshot to guess 3544 if a label refers to a module or a function. It also tries to rebuild 3545 labels when they contain hardcoded addresses. 3546 3547 @warning: This method only parses the label, it doesn't make sure the 3548 label actually points to a valid memory location. 3549 3550 @type label: str 3551 @param label: Label to split. 3552 3553 @rtype: tuple( str or None, str or int or None, int or None ) 3554 @return: Tuple containing the C{module} name, 3555 the C{function} name or ordinal, and the C{offset} value. 3556 3557 If the label doesn't specify a module, 3558 then C{module} is C{None}. 3559 3560 If the label doesn't specify a function, 3561 then C{function} is C{None}. 3562 3563 If the label doesn't specify an offset, 3564 then C{offset} is C{0}. 3565 3566 @raise ValueError: The label is malformed. 3567 """ 3568 module = function = None 3569 offset = 0 3570 3571 # Special case: None 3572 if not label: 3573 label = "0x0" 3574 else: 3575 3576 # Remove all blanks. 3577 label = label.replace(' ', '') 3578 label = label.replace('\t', '') 3579 label = label.replace('\r', '') 3580 label = label.replace('\n', '') 3581 3582 # Special case: empty label. 3583 if not label: 3584 label = "0x0" 3585 3586 # If an exclamation sign is present, we know we can parse it strictly. 3587 if '!' in label: 3588 return self.split_label_strict(label) 3589 3590 ## # Try to parse it strictly, on error do it the fuzzy way. 3591 ## try: 3592 ## return self.split_label(label) 3593 ## except ValueError: 3594 ## pass 3595 3596 # * + offset 3597 if '+' in label: 3598 try: 3599 prefix, offset = label.split('+') 3600 except ValueError: 3601 raise ValueError, "Malformed label: %s" % label 3602 try: 3603 offset = HexInput.integer(offset) 3604 except ValueError: 3605 raise ValueError, "Malformed label: %s" % label 3606 label = prefix 3607 3608 # This parses both filenames and base addresses. 3609 modobj = self.get_module_by_name(label) 3610 if modobj: 3611 3612 # module 3613 # module + offset 3614 module = modobj.get_name() 3615 3616 else: 3617 3618 # TODO 3619 # If 0xAAAAAAAA + 0xBBBBBBBB is given, 3620 # A is interpreted as a module base address, 3621 # and B as an offset. 3622 # If that fails, it'd be good to add A+B and try to 3623 # use the nearest loaded module. 3624 3625 # offset 3626 # base address + offset (when no module has that base address) 3627 try: 3628 address = HexInput.integer(label) 3629 3630 if offset: 3631 # If 0xAAAAAAAA + 0xBBBBBBBB is given, 3632 # A is interpreted as a module base address, 3633 # and B as an offset. 3634 # If that fails, we get here, meaning no module was found 3635 # at A. Then add up A+B and work with that as a hardcoded 3636 # address. 3637 offset = address + offset 3638 else: 3639 # If the label is a hardcoded address, we get here. 3640 offset = address 3641 3642 # If only a hardcoded address is given, 3643 # rebuild the label using get_label_at_address. 3644 # Then parse it again, but this time strictly, 3645 # both because there is no need for fuzzy syntax and 3646 # to prevent an infinite recursion if there's a bug here. 3647 try: 3648 new_label = self.get_label_at_address(offset) 3649 module, function, offset = \ 3650 self.split_label_strict(new_label) 3651 except ValueError: 3652 pass 3653 3654 # function 3655 # function + offset 3656 except ValueError: 3657 function = label 3658 3659 # Convert function ordinal strings into integers. 3660 if function and function.startswith('#'): 3661 try: 3662 function = HexInput.integer(function[1:]) 3663 except ValueError: 3664 pass 3665 3666 # Convert null offsets to None. 3667 if not offset: 3668 offset = None 3669 3670 return (module, function, offset)
3671 3672 @classmethod
3673 - def split_label(cls, label):
3674 """ 3675 Splits a label into it's C{module}, C{function} and C{offset} 3676 components, as used in L{parse_label}. 3677 3678 When called as a static method, the strict syntax mode is used:: 3679 3680 winappdbg.Process.split_label( "kernel32!CreateFileA" ) 3681 3682 When called as an instance method, the fuzzy syntax mode is used:: 3683 3684 aProcessInstance.split_label( "CreateFileA" ) 3685 3686 @see: L{split_label_strict}, L{split_label_fuzzy} 3687 3688 @type label: str 3689 @param label: Label to split. 3690 3691 @rtype: tuple( str or None, str or int or None, int or None ) 3692 @return: 3693 Tuple containing the C{module} name, 3694 the C{function} name or ordinal, and the C{offset} value. 3695 3696 If the label doesn't specify a module, 3697 then C{module} is C{None}. 3698 3699 If the label doesn't specify a function, 3700 then C{function} is C{None}. 3701 3702 If the label doesn't specify an offset, 3703 then C{offset} is C{0}. 3704 3705 @raise ValueError: The label is malformed. 3706 """ 3707 3708 # XXX 3709 # Docstring indentation was removed so epydoc doesn't complain 3710 # when parsing the docs for __use_fuzzy_mode(). 3711 3712 # This function is overwritten by __init__ 3713 # so here is the static implementation only. 3714 return cls.split_label_strict(label)
3715 3716 # The split_label method is replaced with this function by __init__.
3717 - def __use_fuzzy_mode(self, label):
3718 "@see: L{split_label}" 3719 return self.split_label_fuzzy(label)
3720 ## __use_fuzzy_mode.__doc__ = split_label.__doc__ 3721
3722 - def sanitize_label(self, label):
3723 """ 3724 Converts a label taken from user input into a well-formed label. 3725 3726 @type label: str 3727 @param label: Label taken from user input. 3728 3729 @rtype: str 3730 @return: Sanitized label. 3731 """ 3732 (module, function, offset) = self.split_label_fuzzy(label) 3733 label = self.parse_label(module, function, offset) 3734 return label
3735
3736 - def resolve_label(self, label):
3737 """ 3738 Resolve the memory address of the given label. 3739 3740 @note: 3741 If multiple modules with the same name are loaded, 3742 the label may be resolved at any of them. For a more precise 3743 way to resolve functions use the base address to get the L{Module} 3744 object (see L{Process.get_module}) and then call L{Module.resolve}. 3745 3746 If no module name is specified in the label, the function may be 3747 resolved in any loaded module. If you want to resolve all functions 3748 with that name in all processes, call L{Process.iter_modules} to 3749 iterate through all loaded modules, and then try to resolve the 3750 function in each one of them using L{Module.resolve}. 3751 3752 @type label: str 3753 @param label: Label to resolve. 3754 3755 @rtype: int 3756 @return: Memory address pointed to by the label. 3757 3758 @raise ValueError: The label is malformed or impossible to resolve. 3759 @raise RuntimeError: Cannot resolve the module or function. 3760 """ 3761 # Default address if no module or function are given. 3762 # An offset may be added later. 3763 address = 0 3764 3765 # Split the label into module, function and offset components. 3766 module, function, offset = self.split_label_fuzzy(label) 3767 3768 # Resolve the module. 3769 if module: 3770 modobj = self.get_module_by_name(module) 3771 if not modobj: 3772 msg = "Module %r not found" % module 3773 raise RuntimeError, msg 3774 3775 # Resolve the exported function or debugging symbol. 3776 # If all else fails, check for the special symbol "start". 3777 if function: 3778 address = modobj.resolve(function) 3779 if address is None: 3780 address = modobj.resolve_symbol(function) 3781 if address is None: 3782 if function == "start": 3783 address = modobj.get_entry_point() 3784 if address is None: 3785 msg = "Symbol %r not found in module %s" 3786 msg = msg % (function, module) 3787 raise RuntimeError, msg 3788 3789 # No function, use the base address. 3790 else: 3791 address = modobj.get_base() 3792 3793 # Resolve the function in any module. 3794 elif function: 3795 for modobj in self.iter_modules(): 3796 address = modobj.resolve(function) 3797 if address is not None: 3798 break 3799 if address is None: 3800 msg = "Function %r not found in any module" % function 3801 raise RuntimeError, msg 3802 3803 # Return the address plus the offset. 3804 if offset: 3805 address = address + offset 3806 return address
3807
3808 - def get_label_at_address(self, address, offset = None):
3809 """ 3810 Creates a label from the given memory address. 3811 3812 @warning: This method uses the name of the nearest currently loaded 3813 module. If that module is unloaded later, the label becomes 3814 impossible to resolve. 3815 3816 @type address: int 3817 @param address: Memory address. 3818 3819 @type offset: None or int 3820 @param offset: (Optional) Offset value. 3821 3822 @rtype: str 3823 @return: Label pointing to the given address. 3824 """ 3825 if offset: 3826 address = address + offset 3827 modobj = self.get_module_at_address(address) 3828 if modobj: 3829 label = modobj.get_label_at_address(address) 3830 else: 3831 label = self.parse_label(None, None, address) 3832 return label
3833 3834 # XXX TODO 3835 # DbgBreakPointWithStatus: http://msdn.microsoft.com/en-us/library/ms792807.aspx 3836
3837 - def is_system_defined_breakpoint(self, address):
3838 """ 3839 @type address: int 3840 @param address: Memory address. 3841 3842 @rtype: bool 3843 @return: C{True} if the given address points to a system defined 3844 breakpoint. System defined breakpoints are hardcoded into 3845 system libraries. 3846 """ 3847 return address is not None and ( 3848 address == self.get_system_breakpoint() or \ 3849 address == self.get_wow64_system_breakpoint() or \ 3850 address == self.get_user_breakpoint() or \ 3851 address == self.get_breakin_breakpoint() 3852 )
3853 3854 # FIXME 3855 # In Wine, the system breakpoint seems to be somewhere in kernel32. 3856 # In Windows 2000 I've been told it's in ntdll!NtDebugBreak (not sure yet).
3857 - def get_system_breakpoint(self):
3858 """ 3859 @rtype: int or None 3860 @return: Memory address of the system breakpoint 3861 within the process address space. 3862 Returns C{None} on error. 3863 """ 3864 try: 3865 return self.resolve_label("ntdll!DbgBreakPoint") 3866 except Exception: 3867 return None
3868 3869 # Equivalent of ntdll!DbgBreakPoint in Wow64.
3871 """ 3872 @rtype: int or None 3873 @return: Memory address of the Wow64 system breakpoint 3874 within the process address space. 3875 Returns C{None} on error. 3876 """ 3877 try: 3878 return self.resolve_label("ntdll32!DbgBreakPoint") 3879 except Exception: 3880 return None
3881 3882 # I don't know when this breakpoint is actually used...
3883 - def get_user_breakpoint(self):
3884 """ 3885 @rtype: int or None 3886 @return: Memory address of the user breakpoint 3887 within the process address space. 3888 Returns C{None} on error. 3889 """ 3890 try: 3891 return self.resolve_label("ntdll!DbgUserBreakPoint") 3892 except Exception: 3893 return None
3894 3895 # Equivalent of ntdll!DbgBreakPoint in Wow64.
3896 - def get_wow64_user_breakpoint(self):
3897 """ 3898 @rtype: int or None 3899 @return: Memory address of the Wow64 user breakpoint 3900 within the process address space. 3901 Returns C{None} on error. 3902 """ 3903 try: 3904 return self.resolve_label("ntdll32!DbgUserBreakPoint") 3905 except Exception: 3906 return None
3907 3908 # This breakpoint can only be resolved when the 3909 # debugging symbols for ntdll.dll are loaded.
3910 - def get_breakin_breakpoint(self):
3911 """ 3912 @rtype: int or None 3913 @return: Memory address of the remote breakin breakpoint 3914 within the process address space. 3915 Returns C{None} on error. 3916 """ 3917 try: 3918 return self.resolve_label("ntdll!DbgUiRemoteBreakin") 3919 except Exception: 3920 return None
3921 3922 # Equivalent of ntdll!DbgBreakPoint in Wow64.
3924 """ 3925 @rtype: int or None 3926 @return: Memory address of the Wow64 remote breakin breakpoint 3927 within the process address space. 3928 Returns C{None} on error. 3929 """ 3930 try: 3931 return self.resolve_label("ntdll32!DbgUiRemoteBreakin") 3932 except Exception: 3933 return None
3934
3935 - def load_symbols(self):
3936 """ 3937 Loads the debugging symbols for all modules in this snapshot. 3938 Automatically called by L{get_symbols}. 3939 """ 3940 for aModule in self.iter_modules(): 3941 aModule.load_symbols()
3942
3943 - def unload_symbols(self):
3944 """ 3945 Unloads the debugging symbols for all modules in this snapshot. 3946 """ 3947 for aModule in self.iter_modules(): 3948 aModule.unload_symbols()
3949
3950 - def get_symbols(self):
3951 """ 3952 Returns the debugging symbols for all modules in this snapshot. 3953 The symbols are automatically loaded when needed. 3954 3955 @rtype: list of tuple( str, int, int ) 3956 @return: List of symbols. 3957 Each symbol is represented by a tuple that contains: 3958 - Symbol name 3959 - Symbol memory address 3960 - Symbol size in bytes 3961 """ 3962 symbols = list() 3963 for aModule in self.iter_modules(): 3964 for symbol in aModule.iter_symbols(): 3965 symbols.append(symbol) 3966 return symbols
3967
3968 - def iter_symbols(self):
3969 """ 3970 Returns an iterator for the debugging symbols in all modules in this 3971 snapshot, in no particular order. 3972 The symbols are automatically loaded when needed. 3973 3974 @rtype: iterator of tuple( str, int, int ) 3975 @return: Iterator of symbols. 3976 Each symbol is represented by a tuple that contains: 3977 - Symbol name 3978 - Symbol memory address 3979 - Symbol size in bytes 3980 """ 3981 for aModule in self.iter_modules(): 3982 for symbol in aModule.iter_symbols(): 3983 yield symbol
3984
3985 - def resolve_symbol(self, symbol, bCaseSensitive = False):
3986 """ 3987 Resolves a debugging symbol's address. 3988 3989 @type symbol: str 3990 @param symbol: Name of the symbol to resolve. 3991 3992 @type bCaseSensitive: bool 3993 @param bCaseSensitive: C{True} for case sensitive matches, 3994 C{False} for case insensitive. 3995 3996 @rtype: int or None 3997 @return: Memory address of symbol. C{None} if not found. 3998 """ 3999 if bCaseSensitive: 4000 for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols(): 4001 if symbol == SymbolName: 4002 return SymbolAddress 4003 else: 4004 symbol = symbol.lower() 4005 for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols(): 4006 if symbol == SymbolName.lower(): 4007 return SymbolAddress
4008
4009 - def get_symbol_at_address(self, address):
4010 """ 4011 Tries to find the closest matching symbol for the given address. 4012 4013 @type address: int 4014 @param address: Memory address to query. 4015 4016 @rtype: None or tuple( str, int, int ) 4017 @return: Returns a tuple consisting of: 4018 - Name 4019 - Address 4020 - Size (in bytes) 4021 Returns C{None} if no symbol could be matched. 4022 """ 4023 # Any module may have symbols pointing anywhere in memory, so there's 4024 # no easy way to optimize this. I guess we're stuck with brute force. 4025 found = None 4026 for (SymbolName, SymbolAddress, SymbolSize) in self.iter_symbols(): 4027 if SymbolAddress <= address: 4028 if SymbolAddress + SymbolSize > address: 4029 if not found or found[1] < SymbolAddress: 4030 found = (SymbolName, SymbolAddress, SymbolSize) 4031 return found
4032
4033 #============================================================================== 4034 4035 # TODO 4036 # + fetch special registers (MMX, XMM, 3DNow!, etc) 4037 4038 -class ThreadDebugOperations (object):
4039 """ 4040 Encapsulates several useful debugging routines for threads. 4041 4042 @group Properties: 4043 get_teb, get_teb_address, is_wow64 4044 4045 @group Debugging: 4046 get_seh_chain_pointer, set_seh_chain_pointer, 4047 get_seh_chain, get_wait_chain 4048 4049 @group Disassembly: 4050 disassemble, disassemble_around, disassemble_around_pc, 4051 disassemble_string, disassemble_instruction, disassemble_current 4052 4053 @group Stack: 4054 get_stack_frame, get_stack_frame_range, get_stack_range, 4055 get_stack_trace, get_stack_trace_with_labels, 4056 read_stack_data, read_stack_dwords, read_stack_qwords, 4057 peek_stack_data, peek_stack_dwords, peek_stack_qwords 4058 4059 @group Miscellaneous: 4060 read_code_bytes, peek_code_bytes, 4061 peek_pointers_in_data, peek_pointers_in_registers, 4062 get_linear_address, get_label_at_pc 4063 """ 4064
4065 - def is_wow64(self):
4066 """ 4067 Determines if the thread is running under WOW64. 4068 4069 @rtype: bool 4070 @return: 4071 C{True} if the thread is running under WOW64. That is, it belongs 4072 to a 32-bit application running in a 64-bit Windows. 4073 4074 C{False} if the thread belongs to either a 32-bit application 4075 running in a 32-bit Windows, or a 64-bit application running in a 4076 64-bit Windows. 4077 4078 @raise WindowsError: On error an exception is raised. 4079 4080 @see: U{http://msdn.microsoft.com/en-us/library/aa384249(VS.85).aspx} 4081 """ 4082 return self.get_process().is_wow64()
4083 4084 # TODO 4085 # Maybe it'd be a good idea to cache the TEB, or at least it's pointer. 4086 # The pointers may be obtained when debugging at create_thread_event. 4087
4088 - def get_teb(self):
4089 """ 4090 Returns a copy of the TEB. 4091 To dereference pointers in it call L{Process.read_structure}. 4092 4093 @rtype: L{TEB} 4094 @return: TEB structure. 4095 @raise WindowsError: An exception is raised on error. 4096 """ 4097 return self.get_process().read_structure( self.get_teb_address(), 4098 win32.TEB )
4099
4100 - def get_teb_address(self):
4101 """ 4102 Returns a remote pointer to the TEB. 4103 4104 @rtype: int 4105 @return: Remote pointer to the L{TEB} structure. 4106 @raise WindowsError: An exception is raised on error. 4107 """ 4108 try: 4109 tbi = win32.NtQueryInformationThread( self.get_handle(), 4110 win32.ThreadBasicInformation) 4111 address = tbi.TebBaseAddress 4112 except WindowsError: 4113 address = self.get_linear_address('SegFs', 0) # fs:[0] 4114 if not address: 4115 raise 4116 return address
4117
4118 - def get_linear_address(self, segment, address):
4119 """ 4120 Translates segment-relative addresses to linear addresses. 4121 4122 Linear addresses can be used to access a process memory, 4123 calling L{Process.read} and L{Process.write}. 4124 4125 @type segment: str 4126 @param segment: Segment register name. 4127 4128 @type address: int 4129 @param address: Segment relative memory address. 4130 4131 @rtype: int 4132 @return: Linear memory address. 4133 4134 @raise ValueError: Address is too large for selector. 4135 4136 @raise WindowsError: 4137 The current architecture does not support selectors. 4138 Selectors only exist in x86-based systems. 4139 """ 4140 selector = self.get_register(segment) 4141 ldt = win32.GetThreadSelectorEntry(self.get_handle(), selector) 4142 BaseLow = ldt.BaseLow 4143 BaseMid = ldt.HighWord.Bytes.BaseMid << 16 4144 BaseHi = ldt.HighWord.Bytes.BaseHi << 24 4145 Base = BaseLow | BaseMid | BaseHi 4146 LimitLow = ldt.LimitLow 4147 LimitHi = ldt.HighWord.Bits.LimitHi << 16 4148 Limit = LimitLow | LimitHi 4149 if address > Limit: 4150 msg = "Address %s too large for segment %s (selector %d)" 4151 msg = msg % (HexDump.address(address), segment, selector) 4152 raise ValueError, msg 4153 return Base + address
4154
4155 - def get_label_at_pc(self):
4156 """ 4157 @rtype: str 4158 @return: Label that points to the instruction currently being executed. 4159 """ 4160 return self.get_process().get_label_at_address( self.get_pc() )
4161
4162 - def get_seh_chain_pointer(self):
4163 """ 4164 Get the pointer to the first structured exception handler block. 4165 4166 @rtype: int 4167 @return: Remote pointer to the first block of the structured exception 4168 handlers linked list. If the list is empty, the returned value is 4169 C{0xFFFFFFFF}. 4170 4171 @raise NotImplementedError: 4172 This method is only supported in 32 bits versions of Windows. 4173 """ 4174 if System.arch != 'i386': 4175 raise NotImplementedError, \ 4176 "SEH chain parsing is only supported in 32-bit Windows." 4177 4178 process = self.get_process() 4179 address = self.get_linear_address( 'SegFs', 0 ) 4180 return process.read_pointer( address )
4181
4182 - def set_seh_chain_pointer(self, value):
4183 """ 4184 Change the pointer to the first structured exception handler block. 4185 4186 @type value: int 4187 @param value: Value of the remote pointer to the first block of the 4188 structured exception handlers linked list. To disable SEH set the 4189 value C{0xFFFFFFFF}. 4190 4191 @raise NotImplementedError: 4192 This method is only supported in 32 bits versions of Windows. 4193 """ 4194 if System.arch != 'i386': 4195 raise NotImplementedError, \ 4196 "SEH chain parsing is only supported in 32-bit Windows." 4197 4198 process = self.get_process() 4199 address = self.get_linear_address( 'SegFs', 0 ) 4200 process.write_pointer( address, value )
4201
4202 - def get_seh_chain(self):
4203 """ 4204 @rtype: list of tuple( int, int ) 4205 @return: List of structured exception handlers. 4206 Each SEH is represented as a tuple of two addresses: 4207 - Address of this SEH block 4208 - Address of the SEH callback function 4209 Do not confuse this with the contents of the SEH block itself, 4210 where the first member is a pointer to the B{next} block instead. 4211 4212 @raise NotImplementedError: 4213 This method is only supported in 32 bits versions of Windows. 4214 """ 4215 seh_chain = list() 4216 try: 4217 process = self.get_process() 4218 seh = self.get_seh_chain_pointer() 4219 while seh != 0xFFFFFFFF: 4220 seh_func = process.read_pointer( seh + 4 ) 4221 seh_chain.append( (seh, seh_func) ) 4222 seh = process.read_pointer( seh ) 4223 except WindowsError, e: 4224 seh_chain.append( (seh, None) ) 4225 return seh_chain
4226
4227 - def get_wait_chain(self):
4228 """ 4229 @rtype: 4230 tuple of ( 4231 list of L{win32.WAITCHAIN_NODE_INFO} structures, 4232 bool) 4233 @return: 4234 Wait chain for the thread. 4235 The boolean indicates if there's a cycle in the chain. 4236 @raise AttributeError: 4237 This method is only suppported in Windows Vista and above. 4238 @see: 4239 U{http://msdn.microsoft.com/en-us/library/ms681622%28VS.85%29.aspx} 4240 """ 4241 hWct = win32.OpenThreadWaitChainSession() 4242 try: 4243 return win32.GetThreadWaitChain(hWct, None, 0, self.get_tid()) 4244 finally: 4245 win32.CloseThreadWaitChainSession(hWct)
4246
4247 - def get_stack_range(self):
4248 """ 4249 @rtype: tuple( int, int ) 4250 @return: Stack beginning and end pointers, in memory addresses order. 4251 That is, the first pointer is the stack top, and the second pointer 4252 is the stack bottom, since the stack grows towards lower memory 4253 addresses. 4254 @raise WindowsError: Raises an exception on error. 4255 """ 4256 teb = self.get_teb() 4257 tib = teb.NtTib 4258 return ( tib.StackLimit, tib.StackBase ) # top, bottom
4259
4260 - def __get_stack_trace(self, depth = 16, bUseLabels = True, 4261 bMakePretty = True):
4262 """ 4263 Tries to get a stack trace for the current function. 4264 Only works for functions with standard prologue and epilogue. 4265 4266 @type depth: int 4267 @param depth: Maximum depth of stack trace. 4268 4269 @type bUseLabels: bool 4270 @param bUseLabels: C{True} to use labels, C{False} to use addresses. 4271 4272 @rtype: tuple of tuple( int, int, str ) 4273 @return: Stack trace of the thread as a tuple of 4274 ( return address, frame pointer address, module filename ) 4275 when C{bUseLabels} is C{True}, or a tuple of 4276 ( return address, frame pointer label ) 4277 when C{bUseLabels} is C{False}. 4278 4279 @raise WindowsError: Raises an exception on error. 4280 """ 4281 aProcess = self.get_process() 4282 st, sb = self.get_stack_range() # top, bottom 4283 fp = self.get_fp() 4284 trace = list() 4285 if aProcess.get_module_count() == 0: 4286 aProcess.scan_modules() 4287 while depth > 0: 4288 if fp == 0: 4289 break 4290 if not st <= fp < sb: 4291 break 4292 ra = aProcess.peek_pointer(fp + 4) 4293 if ra == 0: 4294 break 4295 lib = aProcess.get_module_at_address(ra) 4296 if lib is None: 4297 lib = "" 4298 else: 4299 if lib.fileName: 4300 lib = lib.fileName 4301 else: 4302 lib = "%s" % HexDump.address(lib.lpBaseOfDll) 4303 if bUseLabels: 4304 label = aProcess.get_label_at_address(ra) 4305 if bMakePretty: 4306 label = '%s (%s)' % (HexDump.address(ra), label) 4307 trace.append( (fp, label) ) 4308 else: 4309 trace.append( (fp, ra, lib) ) 4310 fp = aProcess.peek_pointer(fp) 4311 return tuple(trace)
4312
4313 - def get_stack_trace(self, depth = 16):
4314 """ 4315 Tries to get a stack trace for the current function. 4316 Only works for functions with standard prologue and epilogue. 4317 4318 @type depth: int 4319 @param depth: Maximum depth of stack trace. 4320 4321 @rtype: tuple of tuple( int, int, str ) 4322 @return: Stack trace of the thread as a tuple of 4323 ( return address, frame pointer address, module filename ). 4324 4325 @raise WindowsError: Raises an exception on error. 4326 """ 4327 return self.__get_stack_trace(depth, False)
4328
4329 - def get_stack_trace_with_labels(self, depth = 16, bMakePretty = True):
4330 """ 4331 Tries to get a stack trace for the current function. 4332 Only works for functions with standard prologue and epilogue. 4333 4334 @type depth: int 4335 @param depth: Maximum depth of stack trace. 4336 4337 @type bMakePretty: bool 4338 @param bMakePretty: 4339 C{True} for user readable labels, 4340 C{False} for labels that can be passed to L{Process.resolve_label}. 4341 4342 "Pretty" labels look better when producing output for the user to 4343 read, while pure labels are more useful programatically. 4344 4345 @rtype: tuple of tuple( int, int, str ) 4346 @return: Stack trace of the thread as a tuple of 4347 ( return address, frame pointer label ). 4348 4349 @raise WindowsError: Raises an exception on error. 4350 """ 4351 return self.__get_stack_trace(depth, True)
4352
4353 - def get_stack_frame_range(self):
4354 """ 4355 Returns the starting and ending addresses of the stack frame. 4356 Only works for functions with standard prologue and epilogue. 4357 4358 @rtype: tuple( int, int ) 4359 @return: Stack frame range. 4360 May not be accurate, depending on the compiler used. 4361 4362 @raise RuntimeError: The stack frame is invalid, 4363 or the function doesn't have a standard prologue 4364 and epilogue. 4365 4366 @raise WindowsError: An error occured when getting the thread context. 4367 """ 4368 st, sb = self.get_stack_range() # top, bottom 4369 sp = self.get_sp() 4370 fp = self.get_fp() 4371 size = fp - sp 4372 if not st <= sp < sb: 4373 raise RuntimeError, 'Stack pointer lies outside the stack' 4374 if not st <= fp < sb: 4375 raise RuntimeError, 'Frame pointer lies outside the stack' 4376 if sp > fp: 4377 raise RuntimeError, 'No valid stack frame found' 4378 return (sp, fp)
4379
4380 - def get_stack_frame(self, max_size = None):
4381 """ 4382 Reads the contents of the current stack frame. 4383 Only works for functions with standard prologue and epilogue. 4384 4385 @type max_size: int 4386 @param max_size: (Optional) Maximum amount of bytes to read. 4387 4388 @rtype: str 4389 @return: Stack frame data. 4390 May not be accurate, depending on the compiler used. 4391 May return an empty string. 4392 4393 @raise RuntimeError: The stack frame is invalid, 4394 or the function doesn't have a standard prologue 4395 and epilogue. 4396 4397 @raise WindowsError: An error occured when getting the thread context 4398 or reading data from the process memory. 4399 """ 4400 sp, fp = self.get_stack_frame_range() 4401 size = fp - sp 4402 if max_size and size > max_size: 4403 size = max_size 4404 return self.get_process().peek(sp, size)
4405
4406 - def read_stack_data(self, size = 128, offset = 0):
4407 """ 4408 Reads the contents of the top of the stack. 4409 4410 @type size: int 4411 @param size: Number of bytes to read. 4412 4413 @type offset: int 4414 @param offset: Offset from the stack pointer to begin reading. 4415 4416 @rtype: str 4417 @return: Stack data. 4418 4419 @raise WindowsError: Could not read the requested data. 4420 """ 4421 aProcess = self.get_process() 4422 return aProcess.read(self.get_sp() + offset, size)
4423
4424 - def peek_stack_data(self, size = 128, offset = 0):
4425 """ 4426 Tries to read the contents of the top of the stack. 4427 4428 @type size: int 4429 @param size: Number of bytes to read. 4430 4431 @type offset: int 4432 @param offset: Offset from the stack pointer to begin reading. 4433 4434 @rtype: str 4435 @return: Stack data. 4436 Returned data may be less than the requested size. 4437 """ 4438 aProcess = self.get_process() 4439 return aProcess.peek(self.get_sp() + offset, size)
4440
4441 - def read_stack_dwords(self, count, offset = 0):
4442 """ 4443 Reads DWORDs from the top of the stack. 4444 4445 @type count: int 4446 @param count: Number of DWORDs to read. 4447 4448 @type offset: int 4449 @param offset: Offset from the stack pointer to begin reading. 4450 4451 @rtype: tuple( int... ) 4452 @return: Tuple of integers read from the stack. 4453 4454 @raise WindowsError: Could not read the requested data. 4455 """ 4456 stackData = self.read_stack_data(count * 4, offset) 4457 return struct.unpack('<'+('L'*count), stackData)
4458
4459 - def peek_stack_dwords(self, count, offset = 0):
4460 """ 4461 Tries to read DWORDs from the top of the stack. 4462 4463 @type count: int 4464 @param count: Number of DWORDs to read. 4465 4466 @type offset: int 4467 @param offset: Offset from the stack pointer to begin reading. 4468 4469 @rtype: tuple( int... ) 4470 @return: Tuple of integers read from the stack. 4471 May be less than the requested number of DWORDs. 4472 """ 4473 stackData = self.peek_stack_data(count * 4, offset) 4474 if len(stackData) & 3: 4475 stackData = stackData[:-len(stackData) & 3] 4476 if not stackData: 4477 return () 4478 return struct.unpack('<'+('L'*count), stackData)
4479
4480 - def read_stack_qwords(self, count, offset = 0):
4481 """ 4482 Reads QWORDs from the top of the stack. 4483 4484 @type count: int 4485 @param count: Number of QWORDs to read. 4486 4487 @type offset: int 4488 @param offset: Offset from the stack pointer to begin reading. 4489 4490 @rtype: tuple( int... ) 4491 @return: Tuple of integers read from the stack. 4492 4493 @raise WindowsError: Could not read the requested data. 4494 """ 4495 stackData = self.read_stack_data(count * 8, offset) 4496 return struct.unpack('<'+('Q'*count), stackData)
4497
4498 - def peek_stack_qwords(self, count, offset = 0):
4499 """ 4500 Tries to read QWORDs from the top of the stack. 4501 4502 @type count: int 4503 @param count: Number of QWORDs to read. 4504 4505 @type offset: int 4506 @param offset: Offset from the stack pointer to begin reading. 4507 4508 @rtype: tuple( int... ) 4509 @return: Tuple of integers read from the stack. 4510 May be less than the requested number of QWORDs. 4511 """ 4512 stackData = self.peek_stack_data(count * 8, offset) 4513 if len(stackData) & 7: 4514 stackData = stackData[:-len(stackData) & 7] 4515 if not stackData: 4516 return () 4517 return struct.unpack('<'+('Q'*count), stackData)
4518
4519 - def read_code_bytes(self, size = 128, offset = 0):
4520 """ 4521 Tries to read some bytes of the code currently being executed. 4522 4523 @type size: int 4524 @param size: Number of bytes to read. 4525 4526 @type offset: int 4527 @param offset: Offset from the program counter to begin reading. 4528 4529 @rtype: str 4530 @return: Bytes read from the process memory. 4531 4532 @raise WindowsError: Could not read the requested data. 4533 """ 4534 return self.get_process().read(self.get_pc() + offset, size)
4535
4536 - def peek_code_bytes(self, size = 128, offset = 0):
4537 """ 4538 Tries to read some bytes of the code currently being executed. 4539 4540 @type size: int 4541 @param size: Number of bytes to read. 4542 4543 @type offset: int 4544 @param offset: Offset from the program counter to begin reading. 4545 4546 @rtype: str 4547 @return: Bytes read from the process memory. 4548 May be less than the requested number of bytes. 4549 """ 4550 return self.get_process().peek(self.get_pc() + offset, size)
4551
4552 - def peek_pointers_in_registers(self, peekSize = 16, context = None):
4553 """ 4554 Tries to guess which values in the registers are valid pointers, 4555 and reads some data from them. 4556 4557 @type peekSize: int 4558 @param peekSize: Number of bytes to read from each pointer found. 4559 4560 @type context: dict( str S{->} int ) 4561 @param context: (Optional) 4562 Dictionary mapping register names to their values. 4563 If not given, the current thread context will be used. 4564 4565 @rtype: dict( str S{->} str ) 4566 @return: Dictionary mapping register names to the data they point to. 4567 """ 4568 peekable_registers = ( 4569 'Eax', 'Ebx', 'Ecx', 'Edx', 'Esi', 'Edi', 'Ebp' 4570 ) 4571 if not context: 4572 context = self.get_context(win32.CONTEXT_CONTROL | \ 4573 win32.CONTEXT_INTEGER) 4574 aProcess = self.get_process() 4575 data = dict() 4576 for (reg_name, reg_value) in context.iteritems(): 4577 if reg_name not in peekable_registers: 4578 continue 4579 ## if reg_name == 'Ebp': 4580 ## stack_begin, stack_end = self.get_stack_range() 4581 ## print hex(stack_end), hex(reg_value), hex(stack_begin) 4582 ## if stack_begin and stack_end and stack_end < stack_begin and \ 4583 ## stack_begin <= reg_value <= stack_end: 4584 ## continue 4585 reg_data = aProcess.peek(reg_value, peekSize) 4586 if reg_data: 4587 data[reg_name] = reg_data 4588 return data
4589 4590 # TODO 4591 # try to avoid reading the same page twice by caching it
4592 - def peek_pointers_in_data(self, data, peekSize = 16, peekStep = 1):
4593 """ 4594 Tries to guess which values in the given data are valid pointers, 4595 and reads some data from them. 4596 4597 @type data: str 4598 @param data: Binary data to find pointers in. 4599 4600 @type peekSize: int 4601 @param peekSize: Number of bytes to read from each pointer found. 4602 4603 @type peekStep: int 4604 @param peekStep: Expected data alignment. 4605 Tipically you specify 1 when data alignment is unknown, 4606 or 4 when you expect data to be DWORD aligned. 4607 Any other value may be specified. 4608 4609 @rtype: dict( str S{->} str ) 4610 @return: Dictionary mapping stack offsets to the data they point to. 4611 """ 4612 aProcess = self.get_process() 4613 return aProcess.peek_pointers_in_data(data, peekSize, peekStep)
4614 4615 #------------------------------------------------------------------------------ 4616 4617 # TODO 4618 # The disassemble_around and disassemble_around_pc methods 4619 # should take as parameter instruction counts rather than sizes 4620
4621 - def disassemble_string(self, lpAddress, code):
4622 """ 4623 Disassemble instructions from a block of binary code. 4624 4625 @type lpAddress: int 4626 @param lpAddress: Memory address where the code was read from. 4627 4628 @type code: str 4629 @param code: Binary code to disassemble. 4630 4631 @rtype: list of tuple( long, int, str, str ) 4632 @return: List of tuples. Each tuple represents an assembly instruction 4633 and contains: 4634 - Memory address of instruction. 4635 - Size of instruction in bytes. 4636 - Disassembly line of instruction. 4637 - Hexadecimal dump of instruction. 4638 """ 4639 aProcess = self.get_process() 4640 return aProcess.disassemble_string(lpAddress, code)
4641
4642 - def disassemble(self, lpAddress, dwSize):
4643 """ 4644 Disassemble instructions from the address space of the process. 4645 4646 @type lpAddress: int 4647 @param lpAddress: Memory address where to read the code from. 4648 4649 @type dwSize: int 4650 @param dwSize: Size of binary code to disassemble. 4651 4652 @rtype: list of tuple( long, int, str, str ) 4653 @return: List of tuples. Each tuple represents an assembly instruction 4654 and contains: 4655 - Memory address of instruction. 4656 - Size of instruction in bytes. 4657 - Disassembly line of instruction. 4658 - Hexadecimal dump of instruction. 4659 """ 4660 aProcess = self.get_process() 4661 return aProcess.disassemble(lpAddress, dwSize)
4662
4663 - def disassemble_around(self, lpAddress, dwSize = 64):
4664 """ 4665 Disassemble around the given address. 4666 4667 @type lpAddress: int 4668 @param lpAddress: Memory address where to read the code from. 4669 4670 @type dwSize: int 4671 @param dwSize: Delta offset. 4672 Code will be read from lpAddress - dwSize to lpAddress + dwSize. 4673 4674 @rtype: list of tuple( long, int, str, str ) 4675 @return: List of tuples. Each tuple represents an assembly instruction 4676 and contains: 4677 - Memory address of instruction. 4678 - Size of instruction in bytes. 4679 - Disassembly line of instruction. 4680 - Hexadecimal dump of instruction. 4681 """ 4682 aProcess = self.get_process() 4683 return aProcess.disassemble_around(lpAddress, dwSize)
4684
4685 - def disassemble_around_pc(self, dwSize = 64):
4686 """ 4687 Disassemble around the program counter of the given thread. 4688 4689 @type dwSize: int 4690 @param dwSize: Delta offset. 4691 Code will be read from pc - dwSize to pc + dwSize. 4692 4693 @rtype: list of tuple( long, int, str, str ) 4694 @return: List of tuples. Each tuple represents an assembly instruction 4695 and contains: 4696 - Memory address of instruction. 4697 - Size of instruction in bytes. 4698 - Disassembly line of instruction. 4699 - Hexadecimal dump of instruction. 4700 """ 4701 aProcess = self.get_process() 4702 return aProcess.disassemble_around(self.get_pc(), dwSize)
4703
4704 - def disassemble_instruction(self, lpAddress):
4705 """ 4706 Disassemble the instruction at the given memory address. 4707 4708 @type lpAddress: int 4709 @param lpAddress: Memory address where to read the code from. 4710 4711 @rtype: tuple( long, int, str, str ) 4712 @return: The tuple represents an assembly instruction 4713 and contains: 4714 - Memory address of instruction. 4715 - Size of instruction in bytes. 4716 - Disassembly line of instruction. 4717 - Hexadecimal dump of instruction. 4718 """ 4719 aProcess = self.get_process() 4720 return aProcess.disassemble(lpAddress, 15)[0]
4721
4722 - def disassemble_current(self):
4723 """ 4724 Disassemble the instruction at the program counter of the given thread. 4725 4726 @rtype: tuple( long, int, str, str ) 4727 @return: The tuple represents an assembly instruction 4728 and contains: 4729 - Memory address of instruction. 4730 - Size of instruction in bytes. 4731 - Disassembly line of instruction. 4732 - Hexadecimal dump of instruction. 4733 """ 4734 return self.disassemble_instruction(self.get_pc())
4735
4736 #============================================================================== 4737 4738 # TODO 4739 # + remote GetLastError 4740 4741 -class ProcessDebugOperations (object):
4742 """ 4743 Encapsulates several useful debugging routines for processes. 4744 4745 @group Properties: 4746 is_wow64, get_peb, get_peb_address, 4747 get_main_module, get_image_base, get_image_name, 4748 get_command_line, get_environment, 4749 get_command_line_block, 4750 get_environment_block, get_environment_data, parse_environment_data 4751 4752 @group Disassembly: 4753 disassemble, disassemble_around, disassemble_around_pc, 4754 disassemble_string, disassemble_instruction, disassemble_current 4755 4756 @group Debugging: 4757 flush_instruction_cache, debug_break, peek_pointers_in_data 4758 """ 4759 4760 # Regular expression to find hexadecimal values of any size. 4761 __hexa_parameter = re.compile('0x[0-9A-Za-z]+') 4762
4763 - def __fixup_labels(self, disasm):
4764 """ 4765 Private method used when disassembling from process memory. 4766 4767 It has no return value because the list is modified in place. On return 4768 all raw memory addresses are replaced by labels when possible. 4769 4770 @type disasm: list of tuple(int, int, str, str) 4771 @param disasm: Output of one of the dissassembly functions. 4772 """ 4773 for index in xrange(len(disasm)): 4774 (address, size, text, dump) = disasm[index] 4775 m = self.__hexa_parameter.search(text) 4776 while m: 4777 s, e = m.span() 4778 value = text[s:e] 4779 try: 4780 label = self.get_label_at_address( int(value, 0x10) ) 4781 except Exception, e: 4782 label = None 4783 if label: 4784 text = text[:s] + label + text[e:] 4785 e = s + len(value) 4786 m = self.__hexa_parameter.search(text, e) 4787 disasm[index] = (address, size, text, dump)
4788
4789 - def disassemble_string(self, lpAddress, code):
4790 """ 4791 Disassemble instructions from a block of binary code. 4792 4793 @type lpAddress: int 4794 @param lpAddress: Memory address where the code was read from. 4795 4796 @type code: str 4797 @param code: Binary code to disassemble. 4798 4799 @rtype: list of tuple( long, int, str, str ) 4800 @return: List of tuples. Each tuple represents an assembly instruction 4801 and contains: 4802 - Memory address of instruction. 4803 - Size of instruction in bytes. 4804 - Disassembly line of instruction. 4805 - Hexadecimal dump of instruction. 4806 4807 @raise NotImplementedError: 4808 No compatible disassembler was found for the current platform. 4809 """ 4810 if System.arch not in ('i386', 'amd64'): 4811 raise NotImplementedError 4812 if (not System.wow64 and System.bits == 32) or self.is_wow64(): 4813 return Decode(lpAddress, code, Decode32Bits) 4814 return Decode(lpAddress, code, Decode64Bits)
4815
4816 - def disassemble(self, lpAddress, dwSize):
4817 """ 4818 Disassemble instructions from the address space of the process. 4819 4820 @type lpAddress: int 4821 @param lpAddress: Memory address where to read the code from. 4822 4823 @type dwSize: int 4824 @param dwSize: Size of binary code to disassemble. 4825 4826 @rtype: list of tuple( long, int, str, str ) 4827 @return: List of tuples. Each tuple represents an assembly instruction 4828 and contains: 4829 - Memory address of instruction. 4830 - Size of instruction in bytes. 4831 - Disassembly line of instruction. 4832 - Hexadecimal dump of instruction. 4833 """ 4834 data = self.read(lpAddress, dwSize) 4835 disasm = self.disassemble_string(lpAddress, data) 4836 self.__fixup_labels(disasm) 4837 return disasm
4838 4839 # FIXME 4840 # This algorithm really sucks, I've got to write a better one :P
4841 - def disassemble_around(self, lpAddress, dwSize = 64):
4842 """ 4843 Disassemble around the given address. 4844 4845 @type lpAddress: int 4846 @param lpAddress: Memory address where to read the code from. 4847 4848 @type dwSize: int 4849 @param dwSize: Delta offset. 4850 Code will be read from lpAddress - dwSize to lpAddress + dwSize. 4851 4852 @rtype: list of tuple( long, int, str, str ) 4853 @return: List of tuples. Each tuple represents an assembly instruction 4854 and contains: 4855 - Memory address of instruction. 4856 - Size of instruction in bytes. 4857 - Disassembly line of instruction. 4858 - Hexadecimal dump of instruction. 4859 """ 4860 dwDelta = int(float(dwSize) / 2.0) 4861 addr_1 = lpAddress - dwDelta 4862 addr_2 = lpAddress 4863 size_1 = dwDelta 4864 size_2 = dwSize - dwDelta 4865 data = self.read(addr_1, dwSize) 4866 data_1 = data[:size_1] 4867 data_2 = data[size_1:] 4868 disasm_1 = self.disassemble_string(addr_1, data_1) 4869 disasm_2 = self.disassemble_string(addr_2, data_2) 4870 disasm = disasm_1 + disasm_2 4871 self.__fixup_labels(disasm) 4872 return disasm
4873
4874 - def disassemble_around_pc(self, dwThreadId, dwSize = 64):
4875 """ 4876 Disassemble around the program counter of the given thread. 4877 4878 @type dwThreadId: int 4879 @param dwThreadId: Global thread ID. 4880 The program counter for this thread will be used as the disassembly 4881 address. 4882 4883 @type dwSize: int 4884 @param dwSize: Delta offset. 4885 Code will be read from pc - dwSize to pc + dwSize. 4886 4887 @rtype: list of tuple( long, int, str, str ) 4888 @return: List of tuples. Each tuple represents an assembly instruction 4889 and contains: 4890 - Memory address of instruction. 4891 - Size of instruction in bytes. 4892 - Disassembly line of instruction. 4893 - Hexadecimal dump of instruction. 4894 """ 4895 aThread = self.get_thread(dwThreadId) 4896 return self.disassemble_around(aThread.get_pc(), dwSize)
4897
4898 - def disassemble_instruction(self, lpAddress):
4899 """ 4900 Disassemble the instruction at the given memory address. 4901 4902 @type lpAddress: int 4903 @param lpAddress: Memory address where to read the code from. 4904 4905 @rtype: tuple( long, int, str, str ) 4906 @return: The tuple represents an assembly instruction 4907 and contains: 4908 - Memory address of instruction. 4909 - Size of instruction in bytes. 4910 - Disassembly line of instruction. 4911 - Hexadecimal dump of instruction. 4912 """ 4913 return self.disassemble(lpAddress, 15)[0]
4914
4915 - def disassemble_current(self, dwThreadId):
4916 """ 4917 Disassemble the instruction at the program counter of the given thread. 4918 4919 @type dwThreadId: int 4920 @param dwThreadId: Global thread ID. 4921 The program counter for this thread will be used as the disassembly 4922 address. 4923 4924 @rtype: tuple( long, int, str, str ) 4925 @return: The tuple represents an assembly instruction 4926 and contains: 4927 - Memory address of instruction. 4928 - Size of instruction in bytes. 4929 - Disassembly line of instruction. 4930 - Hexadecimal dump of instruction. 4931 """ 4932 aThread = self.get_thread(dwThreadId) 4933 return self.disassemble_instruction(aThread.get_pc())
4934 4935 #------------------------------------------------------------------------------ 4936
4937 - def flush_instruction_cache(self):
4938 """ 4939 Flush the instruction cache. This is required if the process memory is 4940 modified and one or more threads are executing nearby the modified 4941 memory region. 4942 4943 @see: U{http://blogs.msdn.com/oldnewthing/archive/2003/12/08/55954.aspx#55958} 4944 4945 @raise WindowsError: Raises exception on error. 4946 """ 4947 win32.FlushInstructionCache( self.get_handle() )
4948
4949 - def debug_break(self):
4950 """ 4951 Triggers the system breakpoint in the process. 4952 4953 @raise WindowsError: On error an exception is raised. 4954 """ 4955 # The exception is raised by a new thread. 4956 # When continuing the exception, the thread dies by itself. 4957 # This thread is hidden from the debugger. 4958 win32.DebugBreakProcess( self.get_handle() )
4959
4960 - def is_wow64(self):
4961 """ 4962 Determines if the process is running under WOW64. 4963 4964 @rtype: bool 4965 @return: 4966 C{True} if the process is running under WOW64. That is, a 32-bit 4967 application running in a 64-bit Windows. 4968 4969 C{False} if the process is either a 32-bit application running in 4970 a 32-bit Windows, or a 64-bit application running in a 64-bit 4971 Windows. 4972 4973 @raise WindowsError: On error an exception is raised. 4974 4975 @see: U{http://msdn.microsoft.com/en-us/library/aa384249(VS.85).aspx} 4976 """ 4977 hProcess = self.get_handle() 4978 try: 4979 return win32.IsWow64Process(hProcess) 4980 except AttributeError: 4981 return False
4982 4983 #------------------------------------------------------------------------------ 4984
4985 - def get_peb(self):
4986 """ 4987 Returns a copy of the PEB. 4988 To dereference pointers in it call L{Process.read_structure}. 4989 4990 @rtype: L{win32.PEB} 4991 @return: PEB structure. 4992 @raise WindowsError: An exception is raised on error. 4993 """ 4994 return self.read_structure(self.get_peb_address(), win32.PEB)
4995
4996 - def get_peb_address(self):
4997 """ 4998 Returns a remote pointer to the PEB. 4999 5000 @rtype: int 5001 @return: Remote pointer to the L{win32.PEB} structure. 5002 Returns C{None} on error. 5003 """ 5004 pbi = win32.NtQueryInformationProcess(self.get_handle(), 5005 win32.ProcessBasicInformation) 5006 return pbi.PebBaseAddress
5007
5008 - def get_main_module(self):
5009 """ 5010 @rtype: L{Module} 5011 @return: Module object for the process main module. 5012 """ 5013 return self.get_module(self.get_image_base())
5014
5015 - def get_image_base(self):
5016 """ 5017 @rtype: int 5018 @return: Image base address for the process main module. 5019 """ 5020 return self.get_peb().ImageBaseAddress
5021
5022 - def get_image_name(self):
5023 """ 5024 @rtype: int 5025 @return: Filename of the process main module. 5026 5027 This method does it's best to retrieve the filename. 5028 However sometimes this is not possible, so C{None} may 5029 be returned instead. 5030 """ 5031 5032 # Method 1: Module.fileName 5033 # It's cached if the filename was already found by the other methods, 5034 # if it came with the corresponding debug event, or it was found by the 5035 # toolhelp API. 5036 try: 5037 mainModule = self.get_main_module() 5038 name = mainModule.fileName 5039 if not name: 5040 name = None 5041 except (KeyError, AttributeError, WindowsError): 5042 name = None 5043 5044 # Method 2: QueryFullProcessImageName() 5045 # Not implemented until Windows Vista. 5046 if not name: 5047 try: 5048 name = win32.QueryFullProcessImageName(self.get_handle()) 5049 except (AttributeError, WindowsError): 5050 name = None 5051 5052 # Method 3: GetProcessImageFileName() 5053 # 5054 # Not implemented until Windows XP. 5055 # For more info see http://blog.voidnish.com/?p=72 5056 if not name: 5057 try: 5058 name = win32.GetProcessImageFileName(self.get_handle()) 5059 if name: 5060 name = PathOperations.native_to_win32_pathname(name) 5061 else: 5062 name = None 5063 except (AttributeError, WindowsError): 5064 if not name: 5065 name = None 5066 5067 # Method 4: GetModuleFileNameEx() 5068 # Not implemented until Windows 2000. 5069 # 5070 # May be spoofed by malware, since this information resides 5071 # in usermode space (see http://www.ragestorm.net/blogs/?p=163). 5072 if not name: 5073 try: 5074 try: 5075 name = win32.GetModuleFileNameEx(self.get_handle()) 5076 except WindowsError: 5077 name = win32.GetModuleFileNameEx(self.get_handle(), 5078 self.get_image_base()) 5079 if name: 5080 name = PathOperations.native_to_win32_pathname(name) 5081 else: 5082 name = None 5083 except (AttributeError, WindowsError): 5084 if not name: 5085 name = None 5086 5087 # Method 5: PEB.ProcessParameters->ImagePathName 5088 # 5089 # May fail since it's using an undocumented internal structure. 5090 # 5091 # May be spoofed by malware, since this information resides 5092 # in usermode space (see http://www.ragestorm.net/blogs/?p=163). 5093 if not name: 5094 try: 5095 peb = self.get_peb() 5096 pp = self.read_structure(peb.ProcessParameters, 5097 win32.RTL_USER_PROCESS_PARAMETERS) 5098 s = pp.ImagePathName 5099 name = self.peek_string(s.Buffer, 5100 dwMaxSize=s.MaximumLength, fUnicode=True) 5101 if name: 5102 name = PathOperations.native_to_win32_pathname(name) 5103 else: 5104 name = None 5105 except (AttributeError, WindowsError): 5106 name = None 5107 5108 # Method 6: Module.get_filename() 5109 # It tries to get the filename from the file handle. 5110 # 5111 # There are currently some problems due to the strange way the API 5112 # works - it returns the pathname without the drive letter, and I 5113 # couldn't figure out a way to fix it. 5114 if not name: 5115 if vars().has_key('mainModule'): 5116 try: 5117 name = mainModule.get_filename() 5118 if not name: 5119 name = None 5120 except (AttributeError, WindowsError): 5121 name = None 5122 5123 # Remember the filename. 5124 if name: 5125 try: 5126 mainModule.fileName = name 5127 except UnboundLocalError: 5128 pass 5129 5130 # Return the image filename, or None on error. 5131 return name
5132
5133 - def get_command_line_block(self):
5134 """ 5135 Retrieves the command line block memory address and size. 5136 5137 @rtype: tuple(int, int) 5138 @return: Tuple with the memory address of the command line block 5139 and it's maximum size in Unicode characters. 5140 5141 @raise WindowsError: On error an exception is raised. 5142 """ 5143 peb = self.get_peb() 5144 pp = self.read_structure(peb.ProcessParameters, 5145 win32.RTL_USER_PROCESS_PARAMETERS) 5146 s = pp.CommandLine 5147 return (s.Buffer, s.MaximumLength)
5148
5149 - def get_environment_block(self):
5150 """ 5151 Retrieves the environment block memory address for the process. 5152 5153 @note: The size is always C{None} on Windows XP and below. 5154 5155 @rtype: tuple(int, int) 5156 @return: Tuple with the memory address of the environment block 5157 and it's size. 5158 5159 @raise WindowsError: On error an exception is raised. 5160 """ 5161 peb = self.get_peb() 5162 pp = self.read_structure(peb.ProcessParameters, 5163 win32.RTL_USER_PROCESS_PARAMETERS) 5164 Environment = pp.Environment 5165 try: 5166 EnvironmentSize = pp.EnvironmentSize 5167 except AttributeError: 5168 EnvironmentSize = None 5169 5170 return (Environment, EnvironmentSize)
5171
5172 - def get_command_line(self):
5173 """ 5174 Retrieves the command line with wich the program was started. 5175 5176 @rtype: str 5177 @return: Command line string. 5178 5179 @raise WindowsError: On error an exception is raised. 5180 """ 5181 (Buffer, MaximumLength) = self.get_command_line_block() 5182 CommandLine = self.peek_string(Buffer, dwMaxSize=MaximumLength, 5183 fUnicode=True) 5184 gst = win32.GuessStringType 5185 if gst.t_default == gst.t_ansi: 5186 CommandLine = str(CommandLine) 5187 return CommandLine
5188
5189 - def get_environment_data(self):
5190 """ 5191 Retrieves the environment block data with wich the program is running. 5192 5193 @rtype: list of str 5194 @return: Environment keys and values separated by a C{=} character, 5195 as found in the process memory. 5196 5197 @raise WindowsError: On error an exception is raised. 5198 """ 5199 block = list() 5200 address, size = self.get_environment_block() 5201 char_size = ctypes.sizeof(win32.WCHAR) 5202 5203 # If we know the block size, read the memory once and parse it. 5204 if size: 5205 data = self.peek(address, size) 5206 while data: 5207 chunk = ctypes.create_unicode_string(data).value 5208 if not chunk: 5209 break 5210 block.append(chunk) 5211 data = data[ (len(chunk) + 1) * char_size : ] 5212 5213 # If we don't know the block size, read the memory many times. 5214 # XXX FIXME 5215 # This is inefficient! A process memory cache would help here... 5216 else: 5217 while 1: 5218 chunk = self.peek_string(address, dwMaxSize = System.pageSize, 5219 fUnicode = True) 5220 print "Chunk: ", 5221 print chunk 5222 if not chunk: 5223 break 5224 block.append(chunk) 5225 address += (len(chunk) + 1) * char_size 5226 5227 # Return the environment data. 5228 return block
5229 5230 @staticmethod
5231 - def parse_environment_data(block):
5232 """ 5233 Parse the environment block into a Python dictionary. 5234 5235 @note: Duplicated keys are joined using null characters. 5236 5237 @type block: list of str 5238 @param block: List of Unicode strings as returned by 5239 L{get_environment_data}. 5240 5241 @rtype: dict(str S{->} str) 5242 @return: Dictionary of environment keys and values. 5243 """ 5244 environment = dict() 5245 5246 # Split the blocks into key/value pairs. 5247 for chunk in block: 5248 sep = chunk.find(u'=') 5249 if sep >= 0: 5250 key, value = chunk[:sep], chunk[sep:] 5251 else: 5252 key, value = chunk, u'' 5253 if not environment.has_key(key): 5254 environment[key] = value 5255 else: 5256 environment[key] += u'\0' + value 5257 5258 # Convert to ANSI if this is the default string type. 5259 gst = win32.GuessStringType 5260 if gst.t_default == gst.t_ansi: 5261 environment = dict( [ (str(key), str(value)) \ 5262 for (key, value) in environment.iteritems() ] ) 5263 5264 # Return the environment dictionary. 5265 return environment
5266
5267 - def get_environment(self):
5268 """ 5269 Retrieves the environment with wich the program is running. 5270 5271 @note: Duplicated keys are joined using null characters. 5272 5273 @rtype: dict(str S{->} str) 5274 @return: Dictionary of environment keys and values. 5275 5276 @raise WindowsError: On error an exception is raised. 5277 """ 5278 return self.parse_environment_block( self.get_environment_data() )
5279 5280 #------------------------------------------------------------------------------ 5281 5282 # TODO 5283 # try to avoid reading the same page twice by caching it
5284 - def peek_pointers_in_data(self, data, peekSize = 16, peekStep = 1):
5285 """ 5286 Tries to guess which values in the given data are valid pointers, 5287 and reads some data from them. 5288 5289 @type data: str 5290 @param data: Binary data to find pointers in. 5291 5292 @type peekSize: int 5293 @param peekSize: Number of bytes to read from each pointer found. 5294 5295 @type peekStep: int 5296 @param peekStep: Expected data alignment. 5297 Tipically you specify 1 when data alignment is unknown, 5298 or 4 when you expect data to be DWORD aligned. 5299 Any other value may be specified. 5300 5301 @rtype: dict( str S{->} str ) 5302 @return: Dictionary mapping stack offsets to the data they point to. 5303 """ 5304 result = dict() 5305 ptrSize = win32.sizeof(win32.LPVOID) 5306 if ptrSize == 4: 5307 ptrFmt = '<L' 5308 else: 5309 ptrFmt = '<Q' 5310 if len(data) > 0: 5311 for i in xrange(0, len(data), peekStep): 5312 packed = data[i:i+ptrSize] 5313 if len(packed) == ptrSize: 5314 address = struct.unpack(ptrFmt, packed)[0] 5315 ## if not address & (~0xFFFF): continue 5316 peek_data = self.peek(address, peekSize) 5317 if peek_data: 5318 result[i] = peek_data 5319 return result
5320
5321 #============================================================================== 5322 5323 # Unlike Process, Thread and Module, there's no container for Window objects. 5324 # That's because Window objects don't really store any data besides the handle. 5325 5326 # XXX TODO 5327 # * implement sending fake user input (mouse and keyboard messages) 5328 # * maybe implement low-level hooks? (they don't require a dll to be injected) 5329 5330 # XXX TODO 5331 # 5332 # Will it be possible to implement window hooks too? That requires a DLL to be 5333 # injected in the target process. Perhaps with CPython it could be done easier, 5334 # compiling a native extension is the safe bet, but both require having a non 5335 # pure Python module, which is something I was trying to avoid so far. 5336 # 5337 # Another possibility would be to malloc some CC's in the target process and 5338 # point the hook callback to it. We'd need to have the remote procedure call 5339 # feature first as (I believe) the hook can't be set remotely in this case. 5340 5341 -class Window (object):
5342 """ 5343 Interface to an open window in the current desktop. 5344 5345 @group Properties: 5346 hWnd, dwProcessId, dwThreadId, 5347 get_handle, get_pid, get_tid, 5348 get_process, get_thread, 5349 set_process, set_thread, 5350 get_classname, get_text, set_text, get_placement, set_placement, 5351 screen_to_client, client_to_screen 5352 5353 @group State: 5354 is_valid, is_visible, is_enabled, is_maximized, is_minimized, is_child, 5355 is_zoomed, is_iconic 5356 5357 @group Navigation: 5358 get_parent, get_children, get_root, get_tree, 5359 get_child_at 5360 5361 @group Instrumentation: 5362 enable, disable, show, hide, maximize, minimize, restore, move, kill 5363 5364 @group Low-level access: 5365 send, post 5366 5367 @type hWnd: int 5368 @ivar hWnd: Window handle. 5369 5370 @type dwProcessId: int 5371 @ivar dwProcessId: Global ID of the process that owns this window. 5372 5373 @type dwThreadId: int 5374 @ivar dwThreadId: Global ID of the thread that owns this window. 5375 5376 @type process: L{Process} 5377 @ivar process: Process that owns this window. 5378 Use the L{get_process} method instead. 5379 5380 @type thread: L{Thread} 5381 @ivar thread: Thread that owns this window. 5382 Use the L{get_thread} method instead. 5383 """ 5384
5385 - def __init__(self, hWnd = None, process = None, thread = None):
5386 """ 5387 @type hWnd: int or L{win32.HWND} 5388 @param hWnd: Window handle. 5389 5390 @type process: L{Process} 5391 @param process: (Optional) Process that owns this window. 5392 5393 @type thread: L{Thread} 5394 @param thread: (Optional) Thread that owns this window. 5395 """ 5396 self.hWnd = hWnd 5397 self.dwProcessId = None 5398 self.dwThreadId = None 5399 self.set_process(process) 5400 self.set_thread(thread)
5401 5402 @property
5403 - def _as_parameter_(self):
5404 """ 5405 Compatibility with ctypes. 5406 Allows passing transparently a Window object to an API call. 5407 """ 5408 return self.get_handle()
5409
5410 - def get_handle(self):
5411 """ 5412 @rtype: int 5413 @return: Window handle. 5414 @raise ValueError: No window handle set. 5415 """ 5416 if self.hWnd is None: 5417 raise ValueError, "No window handle set!" 5418 return self.hWnd
5419
5420 - def get_pid(self):
5421 """ 5422 @rtype: int 5423 @return: Global ID of the process that owns this window. 5424 """ 5425 if self.dwProcessId is not None: 5426 return self.dwProcessId 5427 self.__get_pid_and_tid() 5428 return self.dwProcessId
5429
5430 - def get_tid(self):
5431 """ 5432 @rtype: int 5433 @return: Global ID of the thread that owns this window. 5434 """ 5435 if self.dwThreadId is not None: 5436 return self.dwThreadId 5437 self.__get_pid_and_tid() 5438 return self.dwThreadId
5439
5440 - def __get_pid_and_tid(self):
5441 "Internally used by get_pid() and get_tid()." 5442 self.dwThreadId, self.dwProcessId = \ 5443 win32.GetWindowThreadProcessId(self.hWnd)
5444
5445 - def get_process(self):
5446 """ 5447 @rtype: L{Process} 5448 @return: Parent Process object. 5449 Returns C{None} if unknown. 5450 """ 5451 if self.__process is not None: 5452 ## if isinstance(self.__process, weakref.ref): 5453 ## process = self.__process() 5454 ## if process is not None: 5455 ## return process 5456 #### else: # XXX DEBUG 5457 #### print "Lost reference to parent process at %r" % self 5458 ## else: 5459 return self.__process 5460 # can't use weakrefs here, it's our only reference 5461 self.__process = Process(self.get_pid()) 5462 return self.__process
5463
5464 - def set_process(self, process = None):
5465 """ 5466 Manually set the parent process. Use with care! 5467 5468 @type process: L{Process} 5469 @param process: (Optional) Process object. Use C{None} for no process. 5470 """ 5471 if process is None: 5472 self.__process = None 5473 else: 5474 if not isinstance(process, Process): 5475 msg = "Parent process must be a Process instance, " 5476 msg += "got %s instead" % type(process) 5477 raise TypeError, msg 5478 self.dwProcessId = process.get_pid() 5479 ## self.__process = weakref.ref(process) 5480 self.__process = process
5481 5482 # This horrible kludge is needed to keep Epydoc from complaining... 5483 # if it wasn't for that it'd be a tidy one liner. :P 5484 tmp = get_process.__doc__, set_process.__doc__ 5485 del get_process.__doc__ 5486 del set_process.__doc__ 5487 process = property(get_process, set_process) 5488 get_process.__doc__, set_process.__doc__ = tmp 5489 del tmp 5490
5491 - def get_thread(self):
5492 """ 5493 @rtype: L{Thread} 5494 @return: Parent Thread object. 5495 Returns C{None} if unknown. 5496 """ 5497 if self.__thread is not None: 5498 ## if isinstance(self.__thread, weakref.ref): 5499 ## thread = self.__thread() 5500 ## if thread is not None: 5501 ## return thread 5502 #### else: # XXX DEBUG 5503 #### print "Lost reference to parent thread at %r" % self 5504 ## else: 5505 return self.__thread 5506 # can't use weakrefs here, it's our only reference 5507 self.__thread = Thread(self.get_pid()) 5508 return self.__thread
5509
5510 - def set_thread(self, thread = None):
5511 """ 5512 Manually set the thread process. Use with care! 5513 5514 @type thread: L{Thread} 5515 @param thread: (Optional) Thread object. Use C{None} for no thread. 5516 """ 5517 if thread is None: 5518 self.__thread = None 5519 else: 5520 if not isinstance(thread, Thread): 5521 msg = "Parent thread must be a Thread instance, " 5522 msg += "got %s instead" % type(thread) 5523 raise TypeError, msg 5524 self.dwThreadId = thread.get_tid() 5525 ## self.__thread = weakref.ref(thread) 5526 self.__thread = thread
5527 5528 # This horrible kludge is needed to keep Epydoc from complaining... 5529 # if it wasn't for that it'd be a tidy one liner. :P 5530 tmp = get_thread.__doc__, set_thread.__doc__ 5531 del get_thread.__doc__ 5532 del set_thread.__doc__ 5533 thread = property(get_thread, set_thread) 5534 get_thread.__doc__, set_thread.__doc__ = tmp 5535 del tmp 5536
5537 - def __get_window(self, hWnd):
5538 """ 5539 User internally to get another Window from this one. 5540 It'll try to copy the parent Process and Thread references if possible. 5541 """ 5542 window = Window(hWnd) 5543 if window.get_pid() == self.get_pid(): 5544 window.set_process( self.get_process() ) 5545 if window.get_tid() == self.get_tid(): 5546 window.set_thread( self.get_thread() ) 5547 return window
5548 5549 #------------------------------------------------------------------------------ 5550
5551 - def get_classname(self):
5552 """ 5553 @rtype: str 5554 @return: Window class name. 5555 @raise WindowsError: An error occured while processing this request. 5556 """ 5557 return win32.GetClassName( self.get_handle() )
5558
5559 - def get_text(self):
5560 """ 5561 @see: L{set_text} 5562 @rtype: str 5563 @return: Window text (caption). 5564 """ 5565 length = self.send(win32.WM_GETTEXTLENGTH) 5566 if not length: 5567 raise ctypes.WinError() 5568 length = length + 1 5569 c_buffer = ctypes.create_string_buffer("", length) 5570 success = self.send(win32.WM_GETTEXT, length, c_buffer) 5571 if success == 0: 5572 return "" 5573 return c_buffer.value
5574
5575 - def set_text(self, text):
5576 """ 5577 Set the window text (caption). 5578 5579 @see: L{get_text} 5580 5581 @type text: str 5582 @param text: New window text. 5583 """ 5584 return self.send(win32.WM_SETTEXT, len(text), text)
5585
5586 - def get_placement(self):
5587 """ 5588 Retrieve the window placement in the desktop. 5589 5590 @see: L{set_placement} 5591 5592 @rtype: L{win32.WindowPlacement} 5593 @return: Window placement in the desktop. 5594 """ 5595 return win32.GetWindowPlacement( self.get_handle() )
5596
5597 - def set_placement(self, placement):
5598 """ 5599 Set the window placement in the desktop. 5600 5601 @see: L{get_placement} 5602 5603 @type placement: L{win32.WindowPlacement} 5604 @param placement: Window placement in the desktop. 5605 5606 @raise WindowsError: An error occured while processing this request. 5607 """ 5608 win32.SetWindowPlacement( self.get_handle(), placement )
5609 5610 # XXX TODO 5611 # * get_screen_rect, get_client_rect 5612 # * properties x, y, width, height 5613 # * properties left, top, right, bottom 5614 5615 #------------------------------------------------------------------------------ 5616
5617 - def client_to_screen(self, x, y):
5618 """ 5619 Translates window client coordinates to screen coordinates. 5620 5621 @note: This is a simplified interface to some of the functionality of 5622 the L{win32.Point} class. 5623 5624 @see: {win32.Point.client_to_screen} 5625 5626 @type x: int 5627 @param x: Horizontal coordinate. 5628 @type y: int 5629 @param y: Vertical coordinate. 5630 5631 @rtype: tuple( int, int ) 5632 @return: Translated coordinates in a tuple (x, y). 5633 5634 @raise WindowsError: An error occured while processing this request. 5635 """ 5636 return tuple( ClientToScreen( self.get_handle(), (x, y) ) )
5637
5638 - def screen_to_client(self, x, y):
5639 """ 5640 Translates window screen coordinates to client coordinates. 5641 5642 @note: This is a simplified interface to some of the functionality of 5643 the L{win32.Point} class. 5644 5645 @see: {win32.Point.screen_to_client} 5646 5647 @type x: int 5648 @param x: Horizontal coordinate. 5649 @type y: int 5650 @param y: Vertical coordinate. 5651 5652 @rtype: tuple( int, int ) 5653 @return: Translated coordinates in a tuple (x, y). 5654 5655 @raise WindowsError: An error occured while processing this request. 5656 """ 5657 return tuple( ScreenToClient( self.get_handle(), (x, y) ) )
5658 5659 #------------------------------------------------------------------------------ 5660
5661 - def get_parent(self):
5662 """ 5663 @see: L{get_children} 5664 @rtype: L{Window} or None 5665 @return: Parent window. Returns C{None} if the window has no parent. 5666 @raise WindowsError: An error occured while processing this request. 5667 """ 5668 hWnd = win32.GetParent( self.get_handle() ) 5669 if hWnd: 5670 return self.__get_window(hWnd)
5671
5672 - def get_children(self):
5673 """ 5674 @see: L{get_parent} 5675 @rtype: list( L{Window} ) 5676 @return: List of child windows. 5677 @raise WindowsError: An error occured while processing this request. 5678 """ 5679 return [ 5680 self.__get_window(hWnd) \ 5681 for hWnd in win32.EnumChildWindows( self.get_handle() ) 5682 ]
5683
5684 - def get_tree(self):
5685 """ 5686 @see: L{get_root} 5687 @rtype: dict( L{Window} S{->} dict( ... ) ) 5688 @return: Dictionary of dictionaries forming a tree of child windows. 5689 @raise WindowsError: An error occured while processing this request. 5690 """ 5691 subtree = dict() 5692 for aWindow in self.get_children(): 5693 subtree[ aWindow ] = aWindow.get_tree() 5694 return subtree
5695
5696 - def get_root(self):
5697 """ 5698 @see: L{get_tree} 5699 @rtype: L{Window} 5700 @return: Root window for this tree. 5701 @raise RuntimeError: Can't find the root window for this tree. 5702 @raise WindowsError: An error occured while processing this request. 5703 """ 5704 hWnd = self.get_handle() 5705 history = set() 5706 hPrevWnd = hWnd 5707 while hWnd and hWnd not in history: 5708 history.add(hWnd) 5709 hPrevWnd = hWnd 5710 hWnd = win32.GetParent(hWnd) 5711 if hWnd in history: 5712 # See: https://docs.google.com/View?id=dfqd62nk_228h28szgz 5713 raise RuntimeError, "Can't find the root window for this tree" 5714 if hPrevWnd != self.hWnd: 5715 return self.__get_window(hPrevWnd) 5716 return self
5717
5718 - def get_child_at(self, x, y):
5719 """ 5720 Get the child window located at the given coordinates. If no such 5721 window exists an exception is raised. 5722 5723 @see: L{get_children} 5724 5725 @type x: int 5726 @param x: Horizontal coordinate. 5727 @type y: int 5728 @param y: Vertical coordinate. 5729 5730 @rtype: L{Window} 5731 @return: Child window at the requested position. If no such window 5732 exists a C{WindowsError} exception is raised. 5733 5734 @raise WindowsError: An error occured while processing this request. 5735 """ 5736 win32.ChildWindowFromPoint( self.get_handle(), (x, y) )
5737 ## win32.RealChildWindowFromPoint( self.get_handle(), (x, y) ) 5738 5739 #------------------------------------------------------------------------------ 5740
5741 - def is_valid(self):
5742 """ 5743 @rtype: bool 5744 @return: C{True} if the window handle is still valid. 5745 """ 5746 return win32.IsWindow( self.get_handle() )
5747
5748 - def is_visible(self):
5749 """ 5750 @see: {show}, {hide} 5751 @rtype: bool 5752 @return: C{True} if the window is in a visible state. 5753 """ 5754 return win32.IsWindowVisible( self.get_handle() )
5755
5756 - def is_enabled(self):
5757 """ 5758 @see: {enable}, {disable} 5759 @rtype: bool 5760 @return: C{True} if the window is in an enabled state. 5761 """ 5762 return win32.IsWindowEnabled( self.get_handle() )
5763
5764 - def is_maximized(self):
5765 """ 5766 @see: L{maximize} 5767 @rtype: bool 5768 @return: C{True} if the window is maximized. 5769 """ 5770 return win32.IsZoomed( self.get_handle() )
5771
5772 - def is_minimized(self):
5773 """ 5774 @see: L{minimize} 5775 @rtype: bool 5776 @return: C{True} if the window is minimized. 5777 """ 5778 return win32.IsIconic( self.get_handle() )
5779
5780 - def is_child(self):
5781 """ 5782 @see: L{get_parent} 5783 @rtype: bool 5784 @return: C{True} if the window is a child window. 5785 """ 5786 return win32.IsChild( self.get_handle() )
5787 5788 is_zoomed = is_maximized 5789 is_iconic = is_minimized 5790 5791 #------------------------------------------------------------------------------ 5792
5793 - def enable(self):
5794 """ 5795 Enable the user input for the window. 5796 5797 @see: L{disable} 5798 5799 @raise WindowsError: An error occured while processing this request. 5800 """ 5801 win32.EnableWindow( self.get_handle(), True )
5802
5803 - def disable(self):
5804 """ 5805 Disable the user input for the window. 5806 5807 @see: L{enable} 5808 5809 @raise WindowsError: An error occured while processing this request. 5810 """ 5811 win32.EnableWindow( self.get_handle(), False )
5812
5813 - def show(self, bAsync = True):
5814 """ 5815 Make the window visible. 5816 5817 @see: L{hide} 5818 5819 @type bAsync: bool 5820 @param bAsync: Perform the request asynchronously. 5821 5822 @raise WindowsError: An error occured while processing this request. 5823 """ 5824 if bAsync: 5825 win32.ShowWindowAsync( self.get_handle(), win32.SW_SHOW ) 5826 else: 5827 win32.ShowWindow( self.get_handle(), win32.SW_SHOW )
5828
5829 - def hide(self, bAsync = True):
5830 """ 5831 Make the window invisible. 5832 5833 @see: L{show} 5834 5835 @type bAsync: bool 5836 @param bAsync: Perform the request asynchronously. 5837 5838 @raise WindowsError: An error occured while processing this request. 5839 """ 5840 if bAsync: 5841 win32.ShowWindowAsync( self.get_handle(), win32.SW_HIDE ) 5842 else: 5843 win32.ShowWindow( self.get_handle(), win32.SW_HIDE )
5844
5845 - def maximize(self, bAsync = True):
5846 """ 5847 Maximize the window. 5848 5849 @see: L{minimize}, L{restore} 5850 5851 @type bAsync: bool 5852 @param bAsync: Perform the request asynchronously. 5853 5854 @raise WindowsError: An error occured while processing this request. 5855 """ 5856 if bAsync: 5857 win32.ShowWindowAsync( self.get_handle(), win32.SW_MAXIMIZE ) 5858 else: 5859 win32.ShowWindow( self.get_handle(), win32.SW_MAXIMIZE )
5860
5861 - def minimize(self, bAsync = True):
5862 """ 5863 Minimize the window. 5864 5865 @see: L{maximize}, L{restore} 5866 5867 @type bAsync: bool 5868 @param bAsync: Perform the request asynchronously. 5869 5870 @raise WindowsError: An error occured while processing this request. 5871 """ 5872 if bAsync: 5873 win32.ShowWindowAsync( self.get_handle(), win32.SW_MINIMIZE ) 5874 else: 5875 win32.ShowWindow( self.get_handle(), win32.SW_MINIMIZE )
5876
5877 - def restore(self, bAsync = True):
5878 """ 5879 Unmaximize and unminimize the window. 5880 5881 @see: L{maximize}, L{minimize} 5882 5883 @type bAsync: bool 5884 @param bAsync: Perform the request asynchronously. 5885 5886 @raise WindowsError: An error occured while processing this request. 5887 """ 5888 if bAsync: 5889 win32.ShowWindowAsync( self.get_handle(), win32.SW_RESTORE ) 5890 else: 5891 win32.ShowWindow( self.get_handle(), win32.SW_RESTORE )
5892
5893 - def move(self, x, y, width, height, bRepaint = True):
5894 """ 5895 Moves and/or resizes the window. 5896 5897 @note: This is request is performed syncronously. 5898 5899 @type x: int 5900 @param x: New horizontal coordinate. 5901 5902 @type y: int 5903 @param y: New vertical coordinate. 5904 5905 @type width: int 5906 @param width: Desired window width. 5907 5908 @type height: int 5909 @param height: Desired window height. 5910 5911 @type bRepaint: bool 5912 @param bRepaint: C{True} if the window should be redrawn afterwards. 5913 5914 @raise WindowsError: An error occured while processing this request. 5915 """ 5916 # XXX TODO 5917 # Make the parameters optional by querying the current position first. 5918 win32.MoveWindow(self.get_handle(), x, y, width, height, bRepaint)
5919
5920 - def kill(self):
5921 """ 5922 Signals the program to quit. 5923 5924 @note: This is an asyncronous request. 5925 5926 @raise WindowsError: An error occured while processing this request. 5927 """ 5928 self.post(win32.WM_QUIT)
5929
5930 - def send(self, uMsg, wParam = None, lParam = None):
5931 """ 5932 Send a low-level window message syncronically. 5933 5934 @type uMsg: int 5935 @param uMsg: Message code. 5936 5937 @param wParam: 5938 The type and meaning of this parameter depends on the message. 5939 5940 @param lParam: 5941 The type and meaning of this parameter depends on the message. 5942 5943 @rtype: int 5944 @return: The meaning of the return value depends on the window message. 5945 Typically a value of C{0} means an error occured. You can get the 5946 error code by calling L{win32.GetLastError}. 5947 """ 5948 return win32.SendMessage(self.get_handle(), uMsg, wParam, lParam)
5949
5950 - def post(self, uMsg, wParam = None, lParam = None):
5951 """ 5952 Post a low-level window message asyncronically. 5953 5954 @type uMsg: int 5955 @param uMsg: Message code. 5956 5957 @param wParam: 5958 The type and meaning of this parameter depends on the message. 5959 5960 @param lParam: 5961 The type and meaning of this parameter depends on the message. 5962 5963 @raise WindowsError: An error occured while sending the message. 5964 """ 5965 win32.PostMessage(self.get_handle(), uMsg, wParam, lParam)
5966
5967 #============================================================================== 5968 5969 -class Module (SymbolContainer):
5970 """ 5971 Interface to a DLL library loaded in the context of another process. 5972 5973 @group Properties: 5974 get_base, get_filename, get_name, get_size, get_entry_point, 5975 get_process, set_process, get_pid 5976 5977 @group Labels: 5978 get_label, get_label_at_address, is_address_here, 5979 resolve, resolve_label, match_name 5980 5981 @group Handle: 5982 get_handle, open_handle, close_handle 5983 5984 @type unknown: str 5985 @cvar unknown: Suggested tag for unknown modules. 5986 5987 @type lpBaseOfDll: int 5988 @ivar lpBaseOfDll: Base of DLL module. 5989 Use L{get_base} instead. 5990 5991 @type hFile: L{FileHandle} 5992 @ivar hFile: Handle to the module file. 5993 Use L{get_handle} instead. 5994 5995 @type fileName: str 5996 @ivar fileName: Module filename. 5997 Use L{get_filename} instead. 5998 5999 @type SizeOfImage: int 6000 @ivar SizeOfImage: Size of the module. 6001 Use L{get_size} instead. 6002 6003 @type EntryPoint: int 6004 @ivar EntryPoint: Entry point of the module. 6005 Use L{get_entry_point} instead. 6006 6007 @type process: L{Process} 6008 @ivar process: Process where the module is loaded. 6009 Use the L{get_process} method instead. 6010 """ 6011 6012 unknown = '<unknown>' 6013
6014 - def __init__(self, lpBaseOfDll, hFile = None, fileName = None, 6015 SizeOfImage = None, 6016 EntryPoint = None, 6017 process = None):
6018 """ 6019 @type lpBaseOfDll: str 6020 @param lpBaseOfDll: Base address of the module. 6021 6022 @type hFile: L{FileHandle} 6023 @param hFile: (Optional) Handle to the module file. 6024 6025 @type fileName: str 6026 @param fileName: (Optional) Module filename. 6027 6028 @type SizeOfImage: int 6029 @param SizeOfImage: (Optional) Size of the module. 6030 6031 @type EntryPoint: int 6032 @param EntryPoint: (Optional) Entry point of the module. 6033 6034 @type process: L{Process} 6035 @param process: (Optional) Process where the module is loaded. 6036 """ 6037 super(Module, self).__init__() 6038 self.lpBaseOfDll = lpBaseOfDll 6039 self.hFile = hFile 6040 self.fileName = fileName 6041 self.SizeOfImage = SizeOfImage 6042 self.EntryPoint = EntryPoint 6043 self.set_process(process)
6044 6045 # Not really sure if it's a good idea... 6046 ## def __eq__(self, aModule): 6047 ## """ 6048 ## Compare two Module objects. The comparison is made using the process 6049 ## IDs and the module bases. 6050 ## 6051 ## @type aModule: L{Module} 6052 ## @param aModule: Another Module object. 6053 ## 6054 ## @rtype: bool 6055 ## @return: C{True} if the two process IDs and module bases are equal, 6056 ## C{False} otherwise. 6057 ## """ 6058 ## return isinstance(aModule, Module) and \ 6059 ## self.get_pid() == aModule.get_pid() and \ 6060 ## self.get_base() == aModule.get_base() 6061
6062 - def get_process(self):
6063 """ 6064 @rtype: L{Process} 6065 @return: Parent Process object. 6066 Returns C{None} if unknown. 6067 """ 6068 if self.__process is not None: 6069 ## if isinstance(self.__process, weakref.ref): 6070 ## process = self.__process() 6071 ## if process is not None: 6072 ## return process 6073 #### else: # XXX DEBUG 6074 #### print "Lost reference to parent process at %r" % self 6075 ## else: 6076 return self.__process 6077 # no way to guess! 6078 return None
6079
6080 - def set_process(self, process = None):
6081 """ 6082 Manually set the parent process. Use with care! 6083 6084 @type process: L{Process} 6085 @param process: (Optional) Process object. Use C{None} for no process. 6086 """ 6087 if process is None: 6088 self.__process = None 6089 else: 6090 if not isinstance(process, Process): 6091 msg = "Parent process must be a Process instance, " 6092 msg += "got %s instead" % type(process) 6093 raise TypeError, msg 6094 ## self.__process = weakref.ref(process) 6095 self.__process = process
6096 6097 # This horrible kludge is needed to keep Epydoc from complaining... 6098 # if it wasn't for that it'd be a tidy one liner. :P 6099 tmp = get_process.__doc__, set_process.__doc__ 6100 del get_process.__doc__ 6101 del set_process.__doc__ 6102 process = property(get_process, set_process) 6103 get_process.__doc__, set_process.__doc__ = tmp 6104 del tmp 6105
6106 - def get_pid(self):
6107 """ 6108 @rtype: int or None 6109 @return: Parent process global ID. 6110 Returns C{None} on error. 6111 """ 6112 process = self.get_process() 6113 if process is not None: 6114 return process.get_pid()
6115
6116 - def get_base(self):
6117 """ 6118 @rtype: int or None 6119 @return: Base address of the module. 6120 Returns C{None} if unknown. 6121 """ 6122 return self.lpBaseOfDll
6123
6124 - def get_size(self):
6125 """ 6126 @rtype: int or None 6127 @return: Base size of the module. 6128 Returns C{None} if unknown. 6129 """ 6130 if not self.SizeOfImage: 6131 self.__get_size_and_entry_point() 6132 return self.SizeOfImage
6133
6134 - def get_entry_point(self):
6135 """ 6136 @rtype: int or None 6137 @return: Entry point of the module. 6138 Returns C{None} if unknown. 6139 """ 6140 if not self.EntryPoint: 6141 self.__get_size_and_entry_point() 6142 return self.EntryPoint
6143
6144 - def __get_size_and_entry_point(self):
6145 "Get the size and entry point of the module using the Win32 API." 6146 process = self.get_process() 6147 if process: 6148 try: 6149 handle = process.get_handle() 6150 base = self.get_base() 6151 mi = win32.GetModuleInformation(handle, base) 6152 self.SizeOfImage = mi.SizeOfImage 6153 self.EntryPoint = mi.EntryPoint 6154 except WindowsError: 6155 ## raise # XXX DEBUG 6156 pass
6157
6158 - def get_filename(self):
6159 """ 6160 @rtype: str or None 6161 @return: Module filename. 6162 Returns C{None} if unknown. 6163 """ 6164 if self.fileName is None: 6165 if self.hFile not in (None, win32.INVALID_HANDLE_VALUE): 6166 fileName = self.hFile.get_filename() 6167 if fileName: 6168 fileName = PathOperations.native_to_win32_pathname(fileName) 6169 self.fileName = fileName 6170 return self.fileName
6171
6172 - def __filename_to_modname(self, pathname):
6173 """ 6174 @type pathname: str 6175 @param pathname: Pathname to a module. 6176 6177 @rtype: str 6178 @return: Module name. 6179 """ 6180 filename = PathOperations.pathname_to_filename(pathname) 6181 if filename: 6182 filename = filename.lower() 6183 filepart, extpart = PathOperations.split_extension(filename) 6184 if filepart and extpart: 6185 modName = filepart 6186 else: 6187 modName = filename 6188 else: 6189 modName = pathname 6190 return modName
6191
6192 - def get_name(self):
6193 """ 6194 @rtype: str 6195 @return: Module name, as used in labels. 6196 6197 @warning: Names are B{NOT} guaranteed to be unique. 6198 6199 If you need unique identification for a loaded module, 6200 use the base address instead. 6201 6202 @see: L{get_label} 6203 """ 6204 pathname = self.get_filename() 6205 if pathname: 6206 modName = self.__filename_to_modname(pathname) 6207 else: 6208 modName = "0x%x" % self.get_base() 6209 return modName
6210
6211 - def match_name(self, name):
6212 """ 6213 @rtype: bool 6214 @return: 6215 C{True} if the given name could refer to this module. 6216 It may not be exactly the same returned by L{get_name}. 6217 """ 6218 6219 # If the given name is exactly our name, return True. 6220 # Comparison is case insensitive. 6221 my_name = self.get_name().lower() 6222 if name.lower() == my_name: 6223 return True 6224 6225 # If the given name is a base address, compare it with ours. 6226 try: 6227 base = HexInput.integer(name) 6228 except ValueError: 6229 base = None 6230 if base is not None and base == self.get_base(): 6231 return True 6232 6233 # If the given name is a filename, convert it to a module name. 6234 # Then compare it with ours, case insensitive. 6235 modName = self.__filename_to_modname(name) 6236 if modName.lower() == my_name: 6237 return True 6238 6239 # No match. 6240 return False
6241 6242 #------------------------------------------------------------------------------ 6243
6244 - def open_handle(self):
6245 """ 6246 Opens a new handle to the module. 6247 6248 The new handle is stored in the L{hFile} property. 6249 """ 6250 6251 if not self.get_filename(): 6252 msg = "Cannot retrieve filename for module at %s" 6253 msg = msg % HexDump.address( self.get_base() ) 6254 raise Exception, msg 6255 6256 hFile = win32.CreateFile(self.get_filename(), 6257 dwShareMode = win32.FILE_SHARE_READ, 6258 dwCreationDisposition = win32.OPEN_EXISTING) 6259 6260 # In case hFile was set to an actual handle value instead of a Handle 6261 # object. This shouldn't happen unless the user tinkered with hFile. 6262 if not hasattr(self.hFile, '__del__'): 6263 self.close_handle() 6264 6265 self.hFile = hFile
6266
6267 - def close_handle(self):
6268 """ 6269 Closes the handle to the module. 6270 6271 @note: Normally you don't need to call this method. All handles 6272 created by I{WinAppDbg} are automatically closed when the garbage 6273 collector claims them. So unless you've been tinkering with it, 6274 setting L{hFile} to C{None} should be enough. 6275 """ 6276 try: 6277 if hasattr(self.hFile, 'close'): 6278 self.hFile.close() 6279 elif self.hFile not in (None, win32.INVALID_HANDLE_VALUE): 6280 win32.CloseHandle(self.hFile) 6281 finally: 6282 self.hFile = None
6283
6284 - def get_handle(self):
6285 """ 6286 @rtype: L{FileHandle} 6287 @return: Handle to the module file. 6288 """ 6289 if self.hFile in (None, win32.INVALID_HANDLE_VALUE): 6290 self.open_handle() 6291 return self.hFile
6292 6293 #------------------------------------------------------------------------------ 6294
6295 - def get_label(self, function = None, offset = None):
6296 """ 6297 Retrieves the label for the given function of this module or the module 6298 base address if no function name is given. 6299 6300 @type function: str 6301 @param function: (Optional) Exported function name. 6302 6303 @type offset: int 6304 @param offset: (Optional) Offset from the module base address. 6305 6306 @rtype: str 6307 @return: Label for the module base address, plus the offset if given. 6308 """ 6309 return SymbolOperations.parse_label(self.get_name(), function, offset)
6310
6311 - def get_label_at_address(self, address, offset = None):
6312 """ 6313 Creates a label from the given memory address. 6314 6315 If the address belongs to the module, the label is made relative to 6316 it's base address. 6317 6318 @type address: int 6319 @param address: Memory address. 6320 6321 @type offset: None or int 6322 @param offset: (Optional) Offset value. 6323 6324 @rtype: str 6325 @return: Label pointing to the given address. 6326 """ 6327 6328 # Add the offset to the address. 6329 if offset: 6330 address = address + offset 6331 6332 # Make the label relative to the base address if no match is found. 6333 module = self.get_name() 6334 function = None 6335 offset = address - self.get_base() 6336 6337 # Make the label relative to the entrypoint if no other match is found. 6338 # Skip if the entry point is unknown. 6339 start = self.get_entry_point() 6340 if start and start <= address: 6341 function = "start" 6342 offset = address - start 6343 6344 # Enumerate exported functions and debug symbols, 6345 # then find the closest match, if possible. 6346 try: 6347 symbol = self.get_symbol_at_address(address) 6348 if symbol: 6349 (SymbolName, SymbolAddress, SymbolSize) = symbol 6350 new_offset = address - SymbolAddress 6351 if new_offset <= offset: 6352 function = SymbolName 6353 offset = new_offset 6354 except WindowsError, e: 6355 pass 6356 6357 # Parse the label and return it. 6358 return SymbolOperations.parse_label(module, function, offset)
6359
6360 - def is_address_here(self, address):
6361 """ 6362 Tries to determine if the given address belongs to this module. 6363 6364 @type address: int 6365 @param address: Memory address. 6366 6367 @rtype: bool or None 6368 @return: C{True} if the address belongs to the module, 6369 C{False} if it doesn't, 6370 and C{None} if it can't be determined. 6371 """ 6372 base = self.get_base() 6373 size = self.get_size() 6374 if base and size: 6375 return base <= address < (base + size) 6376 return None
6377
6378 - def resolve(self, function):
6379 """ 6380 Resolves a function exported by this module. 6381 6382 @type function: str or int 6383 @param function: 6384 str: Name of the function. 6385 int: Ordinal of the function. 6386 6387 @rtype: int 6388 @return: Memory address of the exported function in the process. 6389 Returns None on error. 6390 """ 6391 6392 # Unknown DLL filename, there's nothing we can do. 6393 filename = self.get_filename() 6394 if not filename: 6395 return None 6396 6397 # If the DLL is already mapped locally, resolve the function. 6398 try: 6399 hlib = win32.GetModuleHandle(filename) 6400 address = win32.GetProcAddress(hlib, function) 6401 except WindowsError, e: 6402 6403 # Load the DLL locally, resolve the function and unload it. 6404 try: 6405 hlib = win32.LoadLibraryEx(filename, 6406 win32.DONT_RESOLVE_DLL_REFERENCES) 6407 try: 6408 address = win32.GetProcAddress(hlib, function) 6409 finally: 6410 win32.FreeLibrary(hlib) 6411 except WindowsError, e: 6412 return None 6413 6414 # A NULL pointer means the function was not found. 6415 if address in (None, 0): 6416 return None 6417 6418 # Compensate for DLL base relocations locally and remotely. 6419 return address - hlib + self.lpBaseOfDll
6420
6421 - def resolve_label(self, label):
6422 """ 6423 Resolves a label for this module only. If the label refers to another 6424 module, an exception is raised. 6425 6426 @type label: str 6427 @param label: Label to resolve. 6428 6429 @rtype: int 6430 @return: Memory address pointed to by the label. 6431 6432 @raise ValueError: The label is malformed or impossible to resolve. 6433 @raise RuntimeError: Cannot resolve the module or function. 6434 """ 6435 6436 # Split the label into it's components. 6437 # Use the fuzzy mode whenever possible. 6438 aProcess = self.get_process() 6439 if aProcess is not None: 6440 (module, procedure, offset) = aProcess.split_label(label) 6441 else: 6442 (module, procedure, offset) = Process.split_label(label) 6443 6444 # If a module name is given that doesn't match ours, 6445 # raise an exception. 6446 if module and not self.match_name(module): 6447 raise RuntimeError, "Label does not belong to this module" 6448 6449 # Resolve the procedure if given. 6450 if procedure: 6451 address = self.resolve(procedure) 6452 if address is None: 6453 6454 # If it's a symbol, use the symbol. 6455 address = self.resolve_symbol(procedure) 6456 6457 # If it's the keyword "start" use the entry point. 6458 if address is None and procedure == "start": 6459 address = self.get_entry_point() 6460 6461 # The procedure was not found. 6462 if address is None: 6463 if not module: 6464 module = self.get_name() 6465 msg = "Can't find procedure %s in module %s" 6466 msg = msg % (procedure, module) 6467 raise RuntimeError, msg 6468 6469 # If no procedure is given use the base address of the module. 6470 else: 6471 address = self.get_base() 6472 6473 # Add the offset if given and return the resolved address. 6474 if offset: 6475 address = address + offset 6476 return address
6477
6478 #============================================================================== 6479 6480 -class Thread (ThreadDebugOperations):
6481 """ 6482 Interface to a thread in another process. 6483 6484 @group Properties: 6485 get_tid, get_pid, get_process, set_process, get_exit_code, is_alive, 6486 get_name, set_name, get_windows 6487 @group Instrumentation: 6488 suspend, resume, kill, wait 6489 @group Registers: 6490 get_context, 6491 get_register, 6492 get_flags, get_flag_value, 6493 get_pc, get_sp, get_fp, 6494 get_gp, get_rp, 6495 get_cf, get_df, get_sf, get_tf, get_zf, 6496 set_context, 6497 set_register, 6498 set_flags, set_flag_value, 6499 set_pc, set_sp, set_fp, 6500 set_gp, set_rp, 6501 set_cf, set_df, set_sf, set_tf, set_zf, 6502 clear_cf, clear_df, clear_sf, clear_tf, clear_zf, 6503 Flags 6504 @group Handle: 6505 get_handle, open_handle, close_handle 6506 6507 @type dwThreadId: int 6508 @ivar dwThreadId: Global thread ID. Use L{get_tid} instead. 6509 6510 @type hThread: L{ThreadHandle} 6511 @ivar hThread: Handle to the thread. Use L{get_handle} instead. 6512 6513 @type process: L{Process} 6514 @ivar process: Parent process object. Use L{get_process} instead. 6515 6516 @type pInjectedMemory: int 6517 @ivar pInjectedMemory: If the thread was created by L{Process.inject_code}, 6518 this member contains a pointer to the memory buffer for the injected 6519 code. Otherwise it's C{None}. 6520 6521 The L{kill} method uses this member to free the buffer 6522 when the injected thread is killed. 6523 """ 6524
6525 - def __init__(self, dwThreadId, hThread = None, process = None):
6526 """ 6527 @type dwThreadId: int 6528 @param dwThreadId: Global thread ID. 6529 6530 @type hThread: L{ThreadHandle} 6531 @param hThread: (Optional) Handle to the thread. 6532 6533 @type process: L{Process} 6534 @param process: (Optional) Parent Process object. 6535 """ 6536 super(Thread, self).__init__() 6537 self.dwProcessId = None 6538 self.dwThreadId = dwThreadId 6539 self.hThread = hThread 6540 self.pInjectedMemory = None 6541 self.set_name() 6542 self.set_process(process)
6543 6544 # Not really sure if it's a good idea... 6545 ## def __eq__(self, aThread): 6546 ## """ 6547 ## Compare two Thread objects. The comparison is made using the IDs. 6548 ## 6549 ## @warning: 6550 ## If you have two Thread instances with different handles the 6551 ## equality operator still returns C{True}, so be careful! 6552 ## 6553 ## @type aThread: L{Thread} 6554 ## @param aThread: Another Thread object. 6555 ## 6556 ## @rtype: bool 6557 ## @return: C{True} if the two thread IDs are equal, 6558 ## C{False} otherwise. 6559 ## """ 6560 ## return isinstance(aThread, Thread) and \ 6561 ## self.get_tid() == aThread.get_tid() 6562
6563 - def get_process(self):
6564 """ 6565 @rtype: L{Process} 6566 @return: Parent Process object. 6567 Returns C{None} if unknown. 6568 """ 6569 if self.__process is not None: 6570 ## if isinstance(self.__process, weakref.ref): 6571 ## process = self.__process() 6572 ## if process is not None: 6573 ## return process 6574 #### else: # XXX DEBUG 6575 #### print "Lost reference to parent process at %r" % self 6576 ## else: 6577 return self.__process 6578 # can't use weakrefs here, it's our only reference 6579 self.__process = Process(self.get_pid()) 6580 return self.__process
6581
6582 - def set_process(self, process = None):
6583 """ 6584 Manually set the parent Process object. Use with care! 6585 6586 @type process: L{Process} 6587 @param process: (Optional) Process object. Use C{None} for no process. 6588 """ 6589 if process is None: 6590 self.__process = None 6591 else: 6592 if not isinstance(process, Process): 6593 msg = "Parent process must be a Process instance, " 6594 msg += "got %s instead" % type(process) 6595 raise TypeError, msg 6596 self.dwProcessId = process.get_pid() 6597 ## self.__process = weakref.ref(process) 6598 self.__process = process
6599 6600 # This horrible kludge is needed to keep Epydoc from complaining... 6601 # if it wasn't for that it'd be a tidy one liner. :P 6602 tmp = get_process.__doc__, set_process.__doc__ 6603 del get_process.__doc__ 6604 del set_process.__doc__ 6605 process = property(get_process, set_process) 6606 get_process.__doc__, set_process.__doc__ = tmp 6607 del tmp 6608
6609 - def get_pid(self):
6610 """ 6611 @rtype: int 6612 @return: Parent process global ID. 6613 6614 @raise WindowsError: An error occured when calling a Win32 API function. 6615 @raise RuntimeError: The parent process ID can't be found. 6616 """ 6617 if self.dwProcessId is None: 6618 if self.__process is not None: 6619 # Infinite loop if self.__process is None 6620 self.dwProcessId = self.get_process().get_pid() 6621 else: 6622 hThread = self.get_handle() 6623 try: 6624 # I wish this had been implemented before Vista... 6625 # XXX TODO find the real ntdll call under this api 6626 self.dwProcessId = win32.GetProcessIdOfThread(hThread) 6627 except AttributeError: 6628 # This method really sucks :P 6629 self.dwProcessId = self.__get_pid_by_scanning() 6630 return self.dwProcessId
6631
6632 - def __get_pid_by_scanning(self):
6633 'Internally used by get_pid().' 6634 dwProcessId = None 6635 dwThreadId = self.get_tid() 6636 hSnapshot = win32.CreateToolhelp32Snapshot(win32.TH32CS_SNAPTHREAD) 6637 try: 6638 te = win32.Thread32First(hSnapshot) 6639 while te is not None: 6640 if te.th32ThreadID == dwThreadId: 6641 dwProcessId = te.th32OwnerProcessID 6642 break 6643 te = win32.Thread32Next(hSnapshot) 6644 finally: 6645 win32.CloseHandle(hSnapshot) 6646 if dwProcessId is None: 6647 msg = "Cannot find thread ID %d in any process" % dwThreadId 6648 raise RuntimeError, msg 6649 return dwProcessId
6650
6651 - def get_tid(self):
6652 """ 6653 @rtype: int 6654 @return: Thread global ID. 6655 """ 6656 return self.dwThreadId
6657
6658 - def get_name(self):
6659 """ 6660 @rtype: str 6661 @return: Thread name, or C{None} if the thread is nameless. 6662 """ 6663 return self.name
6664
6665 - def set_name(self, name = None):
6666 """ 6667 Sets the thread's name. 6668 6669 @type name: str 6670 @param name: Thread name, or C{None} if the thread is nameless. 6671 """ 6672 self.name = name
6673 6674 #------------------------------------------------------------------------------ 6675
6676 - def open_handle(self, dwDesiredAccess = win32.PROCESS_ALL_ACCESS):
6677 """ 6678 Opens a new handle to the thread. 6679 6680 The new handle is stored in the L{hThread} property. 6681 """ 6682 hThread = win32.OpenThread(dwDesiredAccess, win32.FALSE, self.dwThreadId) 6683 6684 # In case hThread was set to an actual handle value instead of a Handle 6685 # object. This shouldn't happen unless the user tinkered with hFile. 6686 if not hasattr(self.hThread, '__del__'): 6687 self.close_handle() 6688 6689 self.hThread = hThread
6690
6691 - def close_handle(self):
6692 """ 6693 Closes the handle to the thread. 6694 6695 @note: Normally you don't need to call this method. All handles 6696 created by I{WinAppDbg} are automatically closed when the garbage 6697 collector claims them. So unless you've been tinkering with it, 6698 setting L{hThread} to C{None} should be enough. 6699 """ 6700 try: 6701 if hasattr(self.hThread, 'close'): 6702 self.hThread.close() 6703 elif self.hThread not in (None, win32.INVALID_HANDLE_VALUE): 6704 win32.CloseHandle(self.hThread) 6705 finally: 6706 self.hThread = None
6707
6708 - def get_handle(self):
6709 """ 6710 @rtype: ThreadHandle 6711 @return: Handle to the thread. 6712 """ 6713 if self.hThread in (None, win32.INVALID_HANDLE_VALUE): 6714 self.open_handle() 6715 return self.hThread
6716 6717 #------------------------------------------------------------------------------ 6718
6719 - def wait(self, dwTimeout = None):
6720 """ 6721 Waits for the thread to finish executing. 6722 6723 @type dwTimeout: int 6724 @param dwTimeout: (Optional) Timeout value in milliseconds. 6725 Use C{INFINITE} or C{None} for no timeout. 6726 """ 6727 self.get_handle().wait(dwTimeout)
6728
6729 - def kill(self, dwExitCode = 0):
6730 """ 6731 Terminates the thread execution. 6732 6733 @note: If the C{lpInjectedMemory} member contains a valid pointer, 6734 the memory is freed. 6735 6736 @type dwExitCode: int 6737 @param dwExitCode: (Optional) Thread exit code. 6738 """ 6739 win32.TerminateThread(self.get_handle(), dwExitCode) 6740 if self.pInjectedMemory is not None: 6741 try: 6742 self.get_process().free(self.pInjectedMemory) 6743 self.pInjectedMemory = None 6744 except Exception: 6745 ## raise # XXX DEBUG 6746 pass
6747 6748 # XXX TODO 6749 # suspend() and resume() should have a counter of how many times a thread 6750 # was suspended, so on debugger exit they could (optionally!) be restored 6751
6752 - def suspend(self):
6753 """ 6754 Suspends the thread execution. 6755 6756 @rtype: int 6757 @return: Suspend count. If zero, the thread is running. 6758 """ 6759 return win32.SuspendThread(self.get_handle())
6760
6761 - def resume(self):
6762 """ 6763 Resumes the thread execution. 6764 6765 @rtype: int 6766 @return: Suspend count. If zero, the thread is running. 6767 """ 6768 return win32.ResumeThread(self.get_handle())
6769
6770 - def is_alive(self):
6771 """ 6772 @rtype: bool 6773 @return: C{True} if the thread if currently running. 6774 """ 6775 try: 6776 hProcess = self.get_handle() 6777 except WindowsError: 6778 return False 6779 try: 6780 hProcess.wait(0) 6781 except WindowsError: 6782 return False 6783 return True
6784
6785 - def get_exit_code(self):
6786 """ 6787 @rtype: int 6788 @return: Thread exit code, or C{STILL_ACTIVE} if it's still alive. 6789 """ 6790 return win32.GetExitCodeThread(self.get_handle())
6791 6792 #------------------------------------------------------------------------------ 6793 6794 # XXX TODO 6795 # Support for string searches on the window captions. 6796
6797 - def get_windows(self):
6798 """ 6799 @rtype: list of L{Window} 6800 @return: Returns a list of windows handled by this thread. 6801 """ 6802 try: 6803 process = self.get_process() 6804 except Exception: 6805 process = None 6806 return [ 6807 Window( hWnd, process, self ) \ 6808 for hWnd in win32.EnumThreadWindows( self.get_tid() ) 6809 ]
6810 6811 #------------------------------------------------------------------------------ 6812 6813 # TODO 6814 # A registers cache could be implemented here.
6815 - def get_context(self, ContextFlags = None):
6816 """ 6817 @type ContextFlags: int 6818 @param ContextFlags: Optional, specify which registers to retrieve. 6819 Defaults to C{win32.CONTEXT_ALL} which retrieves all registes 6820 for the current platform. 6821 6822 @rtype: dict( str S{->} int ) 6823 @return: Dictionary mapping register names to their values. 6824 6825 @see: L{set_context} 6826 """ 6827 6828 # Get the thread handle. 6829 hThread = self.get_handle() 6830 6831 # Threads can't be suspended when the exit process event arrives. 6832 # Funny thing is, you can still get the context. (?) 6833 try: 6834 self.suspend() 6835 bSuspended = True 6836 except WindowsError: 6837 bSuspended = False 6838 6839 # If an exception is raised, make sure the thread execution is resumed. 6840 try: 6841 6842 # If we're not in WOW64, things are simple :) 6843 if not System.wow64: 6844 ## if self.is_wow64(): 6845 ## if ContextFlags is not None: 6846 ## ContextFlags = ContextFlags & (~win32.ContextArchMask) 6847 ## ContextFlags = ContextFlags | win32.WOW64_CONTEXT_i386 6848 ## ctx = win32.Wow64GetThreadContext(hThread, 6849 ## ContextFlags = ContextFlags) 6850 ## else: 6851 ctx = win32.GetThreadContext(hThread, 6852 ContextFlags = ContextFlags) 6853 6854 # If we're in WOW64, things are tricky! 6855 else: 6856 if self.is_wow64(): 6857 ctx = win32.GetThreadContext(hThread, 6858 ContextFlags = ContextFlags) 6859 else: 6860 # XXX only i386/AMD64 is supported in this particular case 6861 if System.arch != 'i386': 6862 raise NotImplementedError 6863 if ContextFlags is not None: 6864 ContextFlags = ContextFlags & (~win32.ContextArchMask) 6865 ContextFlags = ContextFlags | win32.context_amd64.CONTEXT_AMD64 6866 ctx = win32.context_amd64.GetThreadContext(hThread, 6867 ContextFlags = ContextFlags) 6868 finally: 6869 if bSuspended: 6870 self.resume() 6871 return ctx
6872
6873 - def set_context(self, context):
6874 """ 6875 Sets the values of the registers. 6876 6877 @see: L{get_context} 6878 6879 @type context: dict( str S{->} int ) 6880 @param context: Dictionary mapping register names to their values. 6881 """ 6882 # No fix for the exit process event bug. 6883 # Setting the context of a dead thread is pointless anyway. 6884 self.suspend() 6885 try: 6886 if System.bits == 64 and self.is_wow64(): 6887 win32.Wow64SetThreadContext(self.get_handle(), context) 6888 else: 6889 win32.SetThreadContext(self.get_handle(), context) 6890 finally: 6891 self.resume()
6892
6893 - def get_register(self, register):
6894 """ 6895 @type register: str 6896 @param register: Register name. 6897 6898 @rtype: int 6899 @return: Value of the requested register. 6900 """ 6901 'Returns the value of a specific register.' 6902 context = self.get_context() 6903 return context[register]
6904
6905 - def set_register(self, register, value):
6906 """ 6907 Sets the value of a specific register. 6908 6909 @type register: str 6910 @param register: Register name. 6911 6912 @rtype: int 6913 @return: Register value. 6914 """ 6915 context = self.get_context() 6916 context[register] = value 6917 self.set_context(context)
6918 6919 #------------------------------------------------------------------------------ 6920 6921 if win32.CONTEXT.arch in ('i386', 'amd64'): 6922
6923 - def get_pc(self):
6924 """ 6925 @rtype: int 6926 @return: Value of the program counter register. 6927 """ 6928 context = self.get_context(win32.CONTEXT_CONTROL) 6929 return context.pc
6930
6931 - def set_pc(self, pc):
6932 """ 6933 Sets the value of the program counter register. 6934 6935 @type pc: int 6936 @param pc: Value of the program counter register. 6937 """ 6938 context = self.get_context(win32.CONTEXT_CONTROL) 6939 context.pc = pc 6940 self.set_context(context)
6941
6942 - def get_sp(self):
6943 """ 6944 @rtype: int 6945 @return: Value of the stack pointer register. 6946 """ 6947 context = self.get_context(win32.CONTEXT_CONTROL) 6948 return context.sp
6949
6950 - def set_sp(self, sp):
6951 """ 6952 Sets the value of the stack pointer register. 6953 6954 @type sp: int 6955 @param sp: Value of the stack pointer register. 6956 """ 6957 context = self.get_context(win32.CONTEXT_CONTROL) 6958 context.sp = sp 6959 self.set_context(context)
6960
6961 - def get_fp(self):
6962 """ 6963 @rtype: int 6964 @return: Value of the frame pointer register. 6965 """ 6966 context = self.get_context(win32.CONTEXT_CONTROL) 6967 return context.fp
6968
6969 - def set_fp(self, fp):
6970 """ 6971 Sets the value of the frame pointer register. 6972 6973 @type fp: int 6974 @param fp: Value of the frame pointer register. 6975 """ 6976 context = self.get_context(win32.CONTEXT_CONTROL) 6977 context.fp = fp 6978 self.set_context(context)
6979 6980 elif win32.CONTEXT.arch == 'ia64': 6981
6982 - def get_gp(self):
6983 """ 6984 @rtype: int 6985 @return: Value of the GP register. 6986 """ 6987 context = self.get_context(win32.CONTEXT_CONTROL) 6988 return context.gp
6989
6990 - def set_gp(self, gp):
6991 """ 6992 Sets the value of the frame pointer register. 6993 6994 @type gp: int 6995 @param gp: Value of the GP register. 6996 """ 6997 context = self.get_context(win32.CONTEXT_CONTROL) 6998 context.gp = gp 6999 self.set_context(context)
7000
7001 - def get_sp(self):
7002 """ 7003 @rtype: int 7004 @return: Value of the SP register. 7005 """ 7006 context = self.get_context(win32.CONTEXT_CONTROL) 7007 return context.sp
7008
7009 - def set_sp(self, sp):
7010 """ 7011 Sets the value of the SP register. 7012 7013 @type sp: int 7014 @param sp: Value of the SP register. 7015 """ 7016 context = self.get_context(win32.CONTEXT_CONTROL) 7017 context.sp = sp 7018 self.set_context(context)
7019
7020 - def get_rp(self):
7021 """ 7022 @rtype: int 7023 @return: Value of the RP register. 7024 """ 7025 context = self.get_context(win32.CONTEXT_CONTROL) 7026 return context.rp
7027
7028 - def set_rp(self, rp):
7029 """ 7030 Sets the value of the RP register. 7031 7032 @type rp: int 7033 @param rp: Value of the RP register. 7034 """ 7035 context = self.get_context(win32.CONTEXT_CONTROL) 7036 context.rp = rp 7037 self.set_context(context)
7038 7039 #------------------------------------------------------------------------------ 7040 7041 if win32.CONTEXT.arch in ('i386', 'amd64'): 7042
7043 - class Flags (object):
7044 'Commonly used processor flags' 7045 Overflow = 0x800 7046 Direction = 0x400 7047 Interrupts = 0x200 7048 Trap = 0x100 7049 Sign = 0x80 7050 Zero = 0x40 7051 # 0x20 ??? 7052 Auxiliary = 0x10 7053 # 0x8 ??? 7054 Parity = 0x4 7055 # 0x2 ??? 7056 Carry = 0x1
7057
7058 - def get_flags(self, FlagMask = 0xFFFFFFFF):
7059 """ 7060 @type FlagMask: int 7061 @param FlagMask: (Optional) Bitwise-AND mask. 7062 7063 @rtype: int 7064 @return: Flags register contents, optionally masking out some bits. 7065 """ 7066 context = self.get_context(win32.CONTEXT_CONTROL) 7067 return context['EFlags'] & FlagMask
7068
7069 - def set_flags(self, eflags, FlagMask = 0xFFFFFFFF):
7070 """ 7071 Sets the flags register, optionally masking some bits. 7072 7073 @type eflags: int 7074 @param eflags: Flags register contents. 7075 7076 @type FlagMask: int 7077 @param FlagMask: (Optional) Bitwise-AND mask. 7078 """ 7079 context = self.get_context(win32.CONTEXT_CONTROL) 7080 context['EFlags'] = (context['EFlags'] & FlagMask) | eflags 7081 self.set_context(context)
7082
7083 - def get_flag_value(self, FlagBit):
7084 """ 7085 @type FlagBit: int 7086 @param FlagBit: One of the L{Flags}. 7087 7088 @rtype: bool 7089 @return: Boolean value of the requested flag. 7090 """ 7091 return bool( self.get_flags(FlagBit) )
7092
7093 - def set_flag_value(self, FlagBit, FlagValue):
7094 """ 7095 Sets a single flag, leaving the others intact. 7096 7097 @type FlagBit: int 7098 @param FlagBit: One of the L{Flags}. 7099 7100 @type FlagValue: bool 7101 @param FlagValue: Boolean value of the flag. 7102 """ 7103 if FlagValue: 7104 eflags = FlagBit 7105 else: 7106 eflags = 0 7107 FlagMask = 0xFFFFFFFF ^ FlagBit 7108 self.set_flags(eflags, FlagMask)
7109
7110 - def get_zf(self):
7111 """ 7112 @rtype: bool 7113 @return: Boolean value of the Zero flag. 7114 """ 7115 return self.get_flag_value(self.Flags.Zero)
7116
7117 - def get_cf(self):
7118 """ 7119 @rtype: bool 7120 @return: Boolean value of the Carry flag. 7121 """ 7122 return self.get_flag_value(self.Flags.Carry)
7123
7124 - def get_sf(self):
7125 """ 7126 @rtype: bool 7127 @return: Boolean value of the Sign flag. 7128 """ 7129 return self.get_flag_value(self.Flags.Sign)
7130
7131 - def get_df(self):
7132 """ 7133 @rtype: bool 7134 @return: Boolean value of the Direction flag. 7135 """ 7136 return self.get_flag_value(self.Flags.Direction)
7137
7138 - def get_tf(self):
7139 """ 7140 @rtype: bool 7141 @return: Boolean value of the Trap flag. 7142 """ 7143 return self.get_flag_value(self.Flags.Trap)
7144
7145 - def clear_zf(self):
7146 'Clears the Zero flag.' 7147 self.set_flag_value(self.Flags.Zero, False)
7148
7149 - def clear_cf(self):
7150 'Clears the Carry flag.' 7151 self.set_flag_value(self.Flags.Carry, False)
7152
7153 - def clear_sf(self):
7154 'Clears the Sign flag.' 7155 self.set_flag_value(self.Flags.Sign, False)
7156
7157 - def clear_df(self):
7158 'Clears the Direction flag.' 7159 self.set_flag_value(self.Flags.Direction, False)
7160
7161 - def clear_tf(self):
7162 'Clears the Trap flag.' 7163 self.set_flag_value(self.Flags.Trap, False)
7164
7165 - def set_zf(self):
7166 'Sets the Zero flag.' 7167 self.set_flag_value(self.Flags.Zero, True)
7168
7169 - def set_cf(self):
7170 'Sets the Carry flag.' 7171 self.set_flag_value(self.Flags.Carry, True)
7172
7173 - def set_sf(self):
7174 'Sets the Sign flag.' 7175 self.set_flag_value(self.Flags.Sign, True)
7176
7177 - def set_df(self):
7178 'Sets the Direction flag.' 7179 self.set_flag_value(self.Flags.Direction, True)
7180
7181 - def set_tf(self):
7182 'Sets the Trap flag.' 7183 self.set_flag_value(self.Flags.Trap, True)
7184
7185 #============================================================================== 7186 7187 -class Process (MemoryOperations, ProcessDebugOperations, SymbolOperations, \ 7188 ThreadContainer, ModuleContainer):
7189 """ 7190 Interface to a process. Contains threads and modules snapshots. 7191 7192 @group Properties: 7193 get_pid, get_filename, get_exit_code, 7194 is_alive, is_debugged 7195 7196 @group Instrumentation: 7197 kill, wait, suspend, resume, inject_code, inject_dll, clean_exit 7198 7199 @group Processes snapshot: 7200 scan, clear, __contains__, __iter__, __len__ 7201 7202 @group Handle: 7203 get_handle, open_handle, close_handle 7204 7205 @type dwProcessId: int 7206 @ivar dwProcessId: Global process ID. Use L{get_pid} instead. 7207 7208 @type hProcess: L{ProcessHandle} 7209 @ivar hProcess: Handle to the process. Use L{get_handle} instead. 7210 7211 @type fileName: str 7212 @ivar fileName: Filename of the main module. Use L{get_filename} instead. 7213 """ 7214
7215 - def __init__(self, dwProcessId, hProcess = None, fileName = None):
7216 """ 7217 @type dwProcessId: int 7218 @param dwProcessId: Global process ID. 7219 7220 @type hProcess: L{ProcessHandle} 7221 @param hProcess: Handle to the process. 7222 7223 @type fileName: str 7224 @param fileName: (Optional) Filename of the main module. 7225 """ 7226 MemoryOperations.__init__(self) 7227 ProcessDebugOperations.__init__(self) 7228 SymbolOperations.__init__(self) 7229 ThreadContainer.__init__(self) 7230 ModuleContainer.__init__(self) 7231 7232 self.dwProcessId = dwProcessId 7233 self.hProcess = hProcess 7234 self.fileName = fileName
7235
7236 - def get_pid(self):
7237 """ 7238 @rtype: int 7239 @return: Process global ID. 7240 """ 7241 return self.dwProcessId
7242
7243 - def get_filename(self):
7244 """ 7245 @rtype: str 7246 @return: Filename of the main module of the process. 7247 """ 7248 if not self.fileName: 7249 self.fileName = self.get_image_name() 7250 return self.fileName
7251
7252 - def open_handle(self):
7253 """ 7254 Opens a new handle to the process. 7255 7256 The new handle is stored in the L{hProcess} property. 7257 """ 7258 hProcess = win32.OpenProcess(win32.PROCESS_ALL_ACCESS, win32.FALSE, 7259 self.dwProcessId) 7260 7261 # In case hProcess was set to an actual handle value instead of a Handle 7262 # object. This shouldn't happen unless the user tinkered with hFile. 7263 if not hasattr(self.hProcess, '__del__'): 7264 self.close_handle() 7265 7266 self.hProcess = hProcess
7267
7268 - def close_handle(self):
7269 """ 7270 Closes the handle to the process. 7271 7272 @note: Normally you don't need to call this method. All handles 7273 created by I{WinAppDbg} are automatically closed when the garbage 7274 collector claims them. So unless you've been tinkering with it, 7275 setting L{hProcess} to C{None} should be enough. 7276 """ 7277 try: 7278 if hasattr(self.hProcess, 'close'): 7279 self.hProcess.close() 7280 elif self.hProcess not in (None, win32.INVALID_HANDLE_VALUE): 7281 win32.CloseHandle(self.hProcess) 7282 finally: 7283 self.hProcess = None
7284
7285 - def get_handle(self):
7286 """ 7287 @rtype: L{ProcessHandle} 7288 @return: Handle to the process. 7289 """ 7290 if self.hProcess in (None, win32.INVALID_HANDLE_VALUE): 7291 self.open_handle() 7292 return self.hProcess
7293 7294 #------------------------------------------------------------------------------ 7295 7296 # Not really sure if it's a good idea... 7297 ## def __eq__(self, aProcess): 7298 ## """ 7299 ## Compare two Process objects. The comparison is made using the IDs. 7300 ## 7301 ## @warning: 7302 ## If you have two Process instances with different handles the 7303 ## equality operator still returns C{True}, so be careful! 7304 ## 7305 ## @type aProcess: L{Process} 7306 ## @param aProcess: Another Process object. 7307 ## 7308 ## @rtype: bool 7309 ## @return: C{True} if the two process IDs are equal, 7310 ## C{False} otherwise. 7311 ## """ 7312 ## return isinstance(aProcess, Process) and \ 7313 ## self.get_pid() == aProcess.get_pid() 7314
7315 - def __contains__(self, anObject):
7316 """ 7317 The same as: C{self.has_thread(anObject) or self.has_module(anObject)} 7318 7319 @type anObject: L{Thread}, L{Module} or int 7320 @param anObject: Object to look for. 7321 Can be a Thread, Module, thread global ID or module base address. 7322 7323 @rtype: bool 7324 @return: C{True} if the requested object was found in the snapshot. 7325 """ 7326 return ThreadContainer.__contains__(self, anObject) or \ 7327 ModuleContainer.__contains__(self, anObject)
7328
7329 - def __len__(self):
7330 """ 7331 @see: L{get_thread_count}, L{get_module_count} 7332 @rtype: int 7333 @return: Count of L{Thread} and L{Module} objects in this snapshot. 7334 """ 7335 return ThreadContainer.__len__(self) + \ 7336 ModuleContainer.__len__(self)
7337
7338 - class __ThreadsAndModulesIterator (object):
7339 """ 7340 Iterator object for L{Process} objects. 7341 Iterates through L{Thread} objects first, L{Module} objects next. 7342 """ 7343
7344 - def __init__(self, container):
7345 """ 7346 @type container: L{Process} 7347 @param container: L{Thread} and L{Module} container. 7348 """ 7349 self.__container = container 7350 self.__iterator = None 7351 self.__state = 0
7352
7353 - def __iter__(self):
7354 'x.__iter__() <==> iter(x)' 7355 return self
7356
7357 - def next(self):
7358 'x.next() -> the next value, or raise StopIteration' 7359 if self.__state == 0: 7360 self.__iterator = self.__container.iter_threads() 7361 self.__state = 1 7362 if self.__state == 1: 7363 try: 7364 return self.__iterator.next() 7365 except StopIteration: 7366 self.__iterator = self.__container.iter_modules() 7367 self.__state = 2 7368 if self.__state == 2: 7369 try: 7370 return self.__iterator.next() 7371 except StopIteration: 7372 self.__iterator = None 7373 self.__state = 3 7374 raise StopIteration
7375
7376 - def __iter__(self):
7377 """ 7378 @see: L{iter_threads}, L{iter_modules} 7379 @rtype: iterator 7380 @return: Iterator of L{Thread} and L{Module} objects in this snapshot. 7381 All threads are iterated first, then all modules. 7382 """ 7383 return self.__ThreadsAndModulesIterator(self)
7384 7385 #------------------------------------------------------------------------------ 7386
7387 - def wait(self, dwTimeout = None):
7388 """ 7389 Waits for the process to finish executing. 7390 7391 @raise WindowsError: On error an exception is raised. 7392 """ 7393 self.get_handle().wait(dwTimeout)
7394
7395 - def kill(self, dwExitCode = 0):
7396 """ 7397 Terminates the execution of the process. 7398 7399 @raise WindowsError: On error an exception is raised. 7400 """ 7401 win32.TerminateProcess(self.get_handle(), dwExitCode)
7402
7403 - def suspend(self):
7404 """ 7405 Suspends execution on all threads of the process. 7406 7407 @raise WindowsError: On error an exception is raised. 7408 """ 7409 if self.get_thread_count() == 0: 7410 self.scan_threads() 7411 suspended = list() 7412 try: 7413 for aThread in self.iter_threads(): 7414 aThread.suspend() 7415 suspended.append(aThread) 7416 except Exception: 7417 for aThread in suspended: 7418 try: 7419 aThread.resume() 7420 except Exception: 7421 pass 7422 raise
7423
7424 - def resume(self):
7425 """ 7426 Resumes execution on all threads of the process. 7427 7428 @raise WindowsError: On error an exception is raised. 7429 """ 7430 if self.get_thread_count() == 0: 7431 self.scan_threads() 7432 resumed = list() 7433 try: 7434 for aThread in self.iter_threads(): 7435 aThread.resume() 7436 resumed.append(aThread) 7437 except Exception: 7438 for aThread in resumed: 7439 try: 7440 aThread.suspend() 7441 except Exception: 7442 pass 7443 raise
7444
7445 - def is_debugged(self):
7446 """ 7447 Tries to determine if the process is being debugged by another process. 7448 It may detect other debuggers besides WinAppDbg. 7449 7450 @rtype: bool 7451 @return: C{True} if the process has a debugger attached. 7452 7453 @warning: 7454 May return inaccurate results when some anti-debug techniques are 7455 used by the target process. 7456 7457 @note: To know if a process currently being debugged by a L{Debug} 7458 object, call L{Debug.is_debugee} instead. 7459 """ 7460 return win32.CheckRemoteDebuggerPresent(self.get_handle())
7461
7462 - def is_alive(self):
7463 """ 7464 @rtype: bool 7465 @return: C{True} if the process is currently running. 7466 """ 7467 try: 7468 self.wait(0) 7469 except WindowsError, e: 7470 return win32.winerror(e) == win32.WAIT_TIMEOUT 7471 return False
7472
7473 - def get_exit_code(self):
7474 """ 7475 @rtype: int 7476 @return: Process exit code, or C{STILL_ACTIVE} if it's still alive. 7477 7478 @warning: If a process returns C{STILL_ACTIVE} as it's exit code, 7479 you may not be able to determine if it's active or not with this 7480 method. Use L{is_alive} to check if the process is still active. 7481 Alternatively you can call L{get_handle} to get the handle object 7482 and then L{ProcessHandle.wait} on it to wait until the process 7483 finishes running. 7484 """ 7485 return win32.GetExitCodeProcess(self.get_handle())
7486 7487 #------------------------------------------------------------------------------ 7488
7489 - def scan(self):
7490 """ 7491 Populates the snapshot of threads and modules. 7492 """ 7493 self.scan_threads() 7494 self.scan_modules()
7495
7496 - def clear(self):
7497 """ 7498 Clears the snapshot of threads and modules. 7499 """ 7500 self.clear_threads() 7501 self.clear_modules()
7502 7503 #------------------------------------------------------------------------------ 7504
7505 - def inject_code(self, payload, lpParameter = 0):
7506 """ 7507 Injects relocatable code into the process memory and executes it. 7508 7509 @see: L{inject_dll} 7510 7511 @type payload: str 7512 @param payload: Relocatable code to run in a new thread. 7513 7514 @type lpParameter: int 7515 @param lpParameter: (Optional) Parameter to be pushed in the stack. 7516 7517 @rtype: tuple( L{Thread}, int ) 7518 @return: The injected Thread object 7519 and the memory address where the code was written. 7520 7521 @raise WindowsError: An exception is raised on error. 7522 """ 7523 7524 # Uncomment for debugging... 7525 ## payload = '\xCC' + payload 7526 7527 # Allocate the memory for the shellcode. 7528 lpStartAddress = self.malloc(len(payload)) 7529 7530 # Catch exceptions so we can free the memory on error. 7531 try: 7532 7533 # Write the shellcode to our memory location. 7534 self.write(lpStartAddress, payload) 7535 7536 # Start a new thread for the shellcode to run. 7537 aThread = self.start_thread(lpStartAddress, lpParameter, 7538 bSuspended = False) 7539 7540 # Remember the shellcode address. 7541 # It will be freed ONLY by the Thread.kill() method 7542 # and the EventHandler class, otherwise you'll have to 7543 # free it in your code, or have your shellcode clean up 7544 # after itself (recommended). 7545 aThread.pInjectedMemory = lpStartAddress 7546 7547 # Free the memory on error. 7548 except Exception, e: 7549 self.free(lpStartAddress) 7550 raise 7551 7552 # Return the Thread object and the shellcode address. 7553 return aThread, lpStartAddress
7554 7555 # TODO 7556 # The shellcode should check for errors, otherwise it just crashes 7557 # when the DLL can't be loaded or the procedure can't be found. 7558 # On error the shellcode should execute an int3 instruction.
7559 - def inject_dll(self, dllname, procname = None, lpParameter = 0, 7560 bWait = True, dwTimeout = None):
7561 """ 7562 Injects a DLL into the process memory. 7563 7564 @warning: Setting C{bWait} to C{True} when the process is frozen by a 7565 debug event will cause a deadlock in your debugger. 7566 7567 @see: L{inject_code} 7568 7569 @type dllname: str 7570 @param dllname: Name of the DLL module to load. 7571 7572 @type procname: str 7573 @param procname: (Optional) Procedure to call when the DLL is loaded. 7574 7575 @type lpParameter: int 7576 @param lpParameter: (Optional) Parameter to the C{procname} procedure. 7577 7578 @type bWait: bool 7579 @param bWait: C{True} to wait for the process to finish. 7580 C{False} to return immediately. 7581 7582 @type dwTimeout: int 7583 @param dwTimeout: (Optional) Timeout value in milliseconds. 7584 Ignored if C{bWait} is C{False}. 7585 7586 @raise NotImplementedError: The target platform is not supported. 7587 Currently calling a procedure in the library is only supported in 7588 the I{i386} architecture. 7589 7590 @raise WindowsError: An exception is raised on error. 7591 """ 7592 7593 # Resolve kernel32.dll 7594 aModule = self.get_module_by_name('kernel32.dll') 7595 if aModule is None: 7596 self.scan_modules() 7597 aModule = self.get_module_by_name('kernel32.dll') 7598 if aModule is None: 7599 raise RuntimeError, \ 7600 "Cannot resolve kernel32.dll in the remote process" 7601 7602 # Old method, using shellcode. 7603 if procname: 7604 if System.arch != 'i386': 7605 raise NotImplementedError 7606 dllname = str(dllname) 7607 7608 # Resolve kernel32.dll!LoadLibraryA 7609 pllib = aModule.resolve('LoadLibraryA') 7610 if not pllib: 7611 raise RuntimeError, \ 7612 "Cannot resolve kernel32.dll!LoadLibraryA in the remote process" 7613 7614 # Resolve kernel32.dll!GetProcAddress 7615 pgpad = aModule.resolve('GetProcAddress') 7616 if not pgpad: 7617 raise RuntimeError, \ 7618 "Cannot resolve kernel32.dll!GetProcAddress in the remote process" 7619 7620 # Resolve kernel32.dll!VirtualFree 7621 pvf = aModule.resolve('VirtualFree') 7622 if not pvf: 7623 raise RuntimeError, \ 7624 "Cannot resolve kernel32.dll!VirtualFree in the remote process" 7625 7626 # Shellcode follows... 7627 code = ''.encode('utf8') 7628 7629 # push dllname 7630 code += '\xe8' + struct.pack('<L', len(dllname) + 1) + dllname + '\0' 7631 7632 # mov eax, LoadLibraryA 7633 code += '\xb8' + struct.pack('<L', pllib) 7634 7635 # call eax 7636 code += '\xff\xd0' 7637 7638 if procname: 7639 7640 # push procname 7641 code += '\xe8' + struct.pack('<L', len(procname) + 1) 7642 code += procname + '\0' 7643 7644 # push eax 7645 code += '\x50' 7646 7647 # mov eax, GetProcAddress 7648 code += '\xb8' + struct.pack('<L', pgpad) 7649 7650 # call eax 7651 code += '\xff\xd0' 7652 7653 # mov ebp, esp ; preserve stack pointer 7654 code += '\x8b\xec' 7655 7656 # push lpParameter 7657 code += '\x68' + struct.pack('<L', lpParameter) 7658 7659 # call eax 7660 code += '\xff\xd0' 7661 7662 # mov esp, ebp ; restore stack pointer 7663 code += '\x8b\xe5' 7664 7665 # pop edx ; our own return address 7666 code += '\x5a' 7667 7668 # push MEM_RELEASE ; dwFreeType 7669 code += '\x68' + struct.pack('<L', win32.MEM_RELEASE) 7670 7671 # push 0x1000 ; dwSize, shellcode max size 4096 bytes 7672 code += '\x68' + struct.pack('<L', 0x1000) 7673 7674 # call $+5 7675 code += '\xe8\x00\x00\x00\x00' 7676 7677 # and dword ptr [esp], 0xFFFFF000 ; align to page boundary 7678 code += '\x81\x24\x24\x00\xf0\xff\xff' 7679 7680 # mov eax, VirtualFree 7681 code += '\xb8' + struct.pack('<L', pvf) 7682 7683 # push edx ; our own return address 7684 code += '\x52' 7685 7686 # jmp eax ; VirtualFree will return to our own return address 7687 code += '\xff\xe0' 7688 7689 # Inject the shellcode. 7690 aThread, lpStartAddress = self.inject_code(code, lpParameter) 7691 7692 # There's no need to free the memory, 7693 # because the shellcode will free it itself. 7694 aThread.pInjectedMemory = None 7695 7696 # New method, not using shellcode. 7697 else: 7698 7699 # Resolve kernel32.dll!LoadLibrary (A/W) 7700 if type(dllname) == type(u''): 7701 pllibname = 'LoadLibraryW' 7702 bufferlen = (len(dllname) + 1) * 2 7703 dllname = win32.ctypes.create_unicode_buffer(dllname).raw[:bufferlen + 1] 7704 else: 7705 pllibname = 'LoadLibraryA' 7706 dllname = str(dllname) + '\x00' 7707 bufferlen = len(dllname) 7708 pllib = aModule.resolve(pllibname) 7709 if not pllib: 7710 msg = "Cannot resolve kernel32.dll!%s in the remote process" 7711 raise RuntimeError, msg % pllibname 7712 7713 # Copy the library name into the process memory space. 7714 pbuffer = self.malloc(bufferlen) 7715 try: 7716 self.write(pbuffer, dllname) 7717 7718 # Create a new thread to load the library. 7719 aThread = self.start_thread(pllib, pbuffer) 7720 7721 # Remember the buffer address. 7722 # It will be freed ONLY by the Thread.kill() method 7723 # and the EventHandler class, otherwise you'll have to 7724 # free it in your code. 7725 aThread.pInjectedMemory = pbuffer 7726 7727 # Free the memory on error. 7728 except Exception: 7729 self.free(pbuffer, bufferlen) 7730 raise 7731 7732 # Wait for the thread to finish. 7733 if bWait: 7734 aThread.wait(dwTimeout)
7735
7736 - def clean_exit(self, dwExitCode = 0, bWait = False, dwTimeout = None):
7737 """ 7738 Injects a new thread to call ExitProcess(). 7739 Optionally waits for the injected thread to finish. 7740 7741 @warning: Setting C{bWait} to C{True} when the process is frozen by a 7742 debug event will cause a deadlock in your debugger. 7743 7744 @type dwExitCode: int 7745 @param dwExitCode: Process exit code. 7746 7747 @type bWait: bool 7748 @param bWait: C{True} to wait for the process to finish. 7749 C{False} to return immediately. 7750 7751 @type dwTimeout: int 7752 @param dwTimeout: (Optional) Timeout value in milliseconds. 7753 Ignored if C{bWait} is C{False}. 7754 7755 @raise WindowsError: An exception is raised on error. 7756 """ 7757 if not dwExitCode: 7758 dwExitCode = 0 7759 pExitProcess = self.resolve_label('kernel32!ExitProcess') 7760 aThread = self.start_thread(pExitProcess, dwExitCode) 7761 if bWait: 7762 aThread.wait(dwTimeout)
7763 7764 #------------------------------------------------------------------------------ 7765
7766 - def notify_create_process(self, event):
7767 """ 7768 Notify the creation of a new process. 7769 7770 This is done automatically by the L{Debug} class, you shouldn't need 7771 to call it yourself. 7772 7773 @type event: L{CreateProcessEvent} 7774 @param event: Create process event. 7775 7776 @rtype: bool 7777 @return: C{True} to call the user-defined handle, C{False} otherwise. 7778 """ 7779 # Do not use super() here. 7780 bCallHandler = ThreadContainer.notify_create_process(self, event) 7781 bCallHandler = bCallHandler and \ 7782 ModuleContainer.notify_create_process(self, event) 7783 return bCallHandler
7784
7785 #============================================================================== 7786 7787 -class System (ProcessContainer):
7788 """ 7789 Interface to a batch of processes, plus some system wide settings. 7790 Contains a snapshot of processes. 7791 7792 @group Instrumentation: 7793 find_window, get_window_at, get_desktop_window, get_foreground_window 7794 7795 @group Global settings: 7796 arch, bits, os, wow64, pageSize, 7797 set_kill_on_exit_mode, request_debug_privileges, load_dbghelp, 7798 read_msr, write_msr, enable_step_on_branch_mode, 7799 get_last_branch_location 7800 7801 @type arch: str 7802 @cvar arch: Name of the processor architecture we're running on. 7803 For more details see L{win32.version.get_arch}. 7804 7805 @type bits: int 7806 @cvar bits: Size of the machine word in bits for the current architecture. 7807 For more details see L{win32.version.get_bits}. 7808 7809 @type os: str 7810 @cvar os: Name of the Windows version we're runing on. 7811 For more details see L{win32.version.get_os}. 7812 7813 @type wow64: bool 7814 @cvar wow64: C{True} if the debugger is a 32 bits process running in a 64 7815 bits version of Windows, C{False} otherwise. 7816 7817 @type pageSize: int 7818 @cvar pageSize: Page size in bytes. Defaults to 0x1000 but it's 7819 automatically updated on runtime when importing the module. 7820 """ 7821 7822 arch = win32.version.arch 7823 bits = win32.version.bits 7824 os = win32.version.os 7825 7826 # Try to determine if the debugger itself is running on WOW64. 7827 # On error assume False. 7828 try: 7829 wow64 = win32.IsWow64Process( win32.GetCurrentProcess() ) 7830 except Exception: 7831 wow64 = False 7832 7833 pageSize = MemoryAddresses.pageSize 7834 7835 #------------------------------------------------------------------------------ 7836 7837 @staticmethod
7838 - def find_window(className = None, windowName = None):
7839 """ 7840 Find the first top-level window in the current desktop to match the 7841 given class name and/or window name. If neither are provided any 7842 top-level window will match. 7843 7844 @see: L{get_window_at} 7845 7846 @type className: str 7847 @param className: (Optional) Class name of the window to find. 7848 If C{None} or not used any class name will match the search. 7849 7850 @type windowName: str 7851 @param windowName: (Optional) Caption text of the window to find. 7852 If C{None} or not used any caption text will match the search. 7853 7854 @rtype: L{Window} or None 7855 @return: A window that matches the request. There may be more matching 7856 windows, but this method only returns one. If no matching window 7857 is found, the return value is C{None}. 7858 7859 @raise WindowsError: An error occured while processing this request. 7860 """ 7861 # I'd love to reverse the order of the parameters 7862 # but that might create some confusion. :( 7863 hWnd = win32.FindWindow(className, windowName) 7864 if hWnd: 7865 return Window(hWnd)
7866 7867 @staticmethod
7868 - def get_window_at(x, y):
7869 """ 7870 Get the window located at the given coordinates in the desktop. 7871 If no such window exists an exception is raised. 7872 7873 @see: L{find_window} 7874 7875 @type x: int 7876 @param x: Horizontal coordinate. 7877 @type y: int 7878 @param y: Vertical coordinate. 7879 7880 @rtype: L{Window} 7881 @return: Window at the requested position. If no such window 7882 exists a C{WindowsError} exception is raised. 7883 7884 @raise WindowsError: An error occured while processing this request. 7885 """ 7886 return Window( win32.WindowFromPoint( (x, y) ) )
7887 7888 @staticmethod
7889 - def get_desktop_window():
7890 """ 7891 @rtype: L{Window} 7892 @return: Returns the desktop window. 7893 @raise WindowsError: An error occured while processing this request. 7894 """ 7895 return Window( win32.GetDesktopWindow() )
7896 7897 @staticmethod
7899 """ 7900 @rtype: L{Window} 7901 @return: Returns the foreground window. 7902 @raise WindowsError: An error occured while processing this request. 7903 """ 7904 return Window( win32.GetForegroundWindow() )
7905 7906 #------------------------------------------------------------------------------ 7907 7908 @staticmethod
7909 - def request_debug_privileges(bIgnoreExceptions = False):
7910 """ 7911 Requests debug privileges. 7912 7913 This may be needed to debug processes running as SYSTEM 7914 (such as services) since Windows XP. 7915 """ 7916 try: 7917 privs = ( 7918 (win32.SE_DEBUG_NAME, True), 7919 ) 7920 hToken = win32.OpenProcessToken(win32.GetCurrentProcess(), 7921 win32.TOKEN_ADJUST_PRIVILEGES) 7922 try: 7923 win32.AdjustTokenPrivileges(hToken, privs) 7924 finally: 7925 win32.CloseHandle(hToken) 7926 return True 7927 except Exception, e: 7928 if not bIgnoreExceptions: 7929 raise 7930 return False
7931 7932 @staticmethod
7933 - def set_kill_on_exit_mode(bKillOnExit = False):
7934 """ 7935 Automatically detach from processes when the current thread dies. 7936 7937 Works on the following platforms: 7938 7939 - Microsoft Windows XP and above. 7940 - Wine (Windows Emulator). 7941 7942 Fails on the following platforms: 7943 7944 - Microsoft Windows 2000 and below. 7945 - ReactOS. 7946 7947 @type bKillOnExit: bool 7948 @param bKillOnExit: C{True} to automatically kill processes when the 7949 debugger thread dies. C{False} to automatically detach from 7950 processes when the debugger thread dies. 7951 7952 @rtype: bool 7953 @return: C{True} on success, C{False} on error. 7954 7955 @note: 7956 This call will fail if a debug port was not created. That is, if 7957 the debugger isn't attached to at least one process. For more info 7958 see: U{http://msdn.microsoft.com/en-us/library/ms679307.aspx} 7959 """ 7960 try: 7961 # won't work before calling CreateProcess or DebugActiveProcess 7962 # http://msdn.microsoft.com/en-us/library/ms679307.aspx 7963 win32.DebugSetProcessKillOnExit(bKillOnExit) 7964 return True 7965 except (AttributeError, WindowsError): 7966 pass 7967 return False
7968 7969 @classmethod
7970 - def load_dbghelp(cls, pathname = None):
7971 """ 7972 Load the C{dbghelp.dll} library shipped with the Debugging Tools for 7973 Windows. Essentially this enables symbol server support, since this 7974 version is newer than the one pre-installed with Windows, and the 7975 symbol server loader library (C{SymSrv.dll}) is present in the same 7976 directory. 7977 7978 For this method to have any effect it MUST be called BEFORE any 7979 function in C{dbghelp.dll}. It's recommended that you call it right 7980 after starting your debug script, or after instancing the L{Debug} 7981 object. 7982 7983 Example:: 7984 from winappdbg import Debug 7985 7986 def simple_debugger( argv ): 7987 7988 # Instance a Debug object, passing it the event handler callback 7989 debug = Debug( my_event_handler ) 7990 try: 7991 7992 # Enable support for symbol downloading 7993 debug.system.load_dbghelp() 7994 7995 # Start a new process for debugging 7996 debug.execv( argv ) 7997 7998 # Wait for the debugee to finish 7999 debug.loop() 8000 8001 # Stop the debugger 8002 finally: 8003 debug.stop() 8004 8005 @see: U{http://msdn.microsoft.com/en-us/library/ms679294(VS.85).aspx} 8006 8007 @type pathname: str 8008 @param pathname: 8009 (Optional) Full pathname to the C{dbghelp.dll} library. 8010 8011 @rtype: ctypes.WinDLL 8012 @return: Loaded instance of C{dbghelp.dll}. 8013 8014 @raise NotImplementedError: This feature was not implemented for the 8015 current architecture. 8016 8017 @raise WindowsError: An error occured while processing this request. 8018 """ 8019 if not pathname: 8020 if cls.arch == 'amd64': 8021 if cls.wow64: 8022 pathname = os.path.join( 8023 os.getenv("ProgramFiles(x86)", 8024 os.getenv("ProgramFiles")), 8025 "Debugging Tools for Windows (x86)", 8026 "dbghelp.dll") 8027 else: 8028 pathname = os.path.join( 8029 os.getenv("ProgramFiles"), 8030 "Debugging Tools for Windows (x64)", 8031 "dbghelp.dll") 8032 elif cls.arch == 'i386': 8033 pathname = os.path.join( 8034 os.getenv("ProgramFiles"), 8035 "Debugging Tools for Windows (x86)", 8036 "dbghelp.dll") 8037 else: 8038 msg = "Architecture %s is not currently supported." 8039 raise NotImplementedError, msg % cls.arch 8040 return ctypes.windll.LoadLibrary(pathname)
8041 8042 @classmethod
8043 - def read_msr(cls, address):
8044 """ 8045 Read the contents of the specified MSR (Machine Specific Register). 8046 8047 @type address: int 8048 @param address: MSR to read. 8049 8050 @rtype: int 8051 @return: Value of the specified MSR. 8052 8053 @raise WindowsError: 8054 Raises an exception on error. 8055 8056 @raise NotImplementedError: 8057 Current architecture is not C{i386} or C{amd64}. 8058 8059 @warning: 8060 It could potentially brick your machine. 8061 It works on my machine, but your mileage may vary. 8062 """ 8063 if cls.arch not in ('i386', 'amd64'): 8064 raise NotImplementedError, \ 8065 "MSR reading is only supported on i386 or amd64 processors." 8066 msr = win32.SYSDBG_MSR() 8067 msr.Address = address 8068 msr.Data = 0 8069 win32.NtSystemDebugControl(win32.SysDbgReadMsr, 8070 InputBuffer = msr, 8071 OutputBuffer = msr) 8072 return msr.Data
8073 8074 @classmethod
8075 - def write_msr(cls, address, value):
8076 """ 8077 Set the contents of the specified MSR (Machine Specific Register). 8078 8079 @type address: int 8080 @param address: MSR to write. 8081 8082 @type value: int 8083 @param value: Contents to write on the MSR. 8084 8085 @raise WindowsError: 8086 Raises an exception on error. 8087 8088 @raise NotImplementedError: 8089 Current architecture is not C{i386} or C{amd64}. 8090 8091 @warning: 8092 It could potentially brick your machine. 8093 It works on my machine, but your mileage may vary. 8094 """ 8095 if cls.arch not in ('i386', 'amd64'): 8096 raise NotImplementedError, \ 8097 "MSR reading is only supported on i386 or amd64 processors." 8098 msr = win32.SYSDBG_MSR() 8099 msr.Address = address 8100 msr.Data = value 8101 win32.NtSystemDebugControl(win32.SysDbgWriteMsr, InputBuffer = msr)
8102 8103 @classmethod
8105 """ 8106 When tracing, call this on every single step event 8107 for step on branch mode. 8108 8109 @raise WindowsError: 8110 Raises C{ERROR_DEBUGGER_INACTIVE} if the debugger is not attached 8111 to least one process. 8112 8113 @raise NotImplementedError: 8114 Current architecture is not C{i386} or C{amd64}. 8115 8116 @warning: 8117 This method uses the processor's machine specific registers (MSR). 8118 It could potentially brick your machine. 8119 It works on my machine, but your mileage may vary. 8120 8121 @note: 8122 It doesn't seem to work in VMWare or VirtualBox machines. 8123 Maybe it fails in other virtualization/emulation environments, 8124 no extensive testing was made so far. 8125 """ 8126 cls.write_msr(DebugRegister.DebugCtlMSR, 8127 DebugRegister.BranchTrapFlag | DebugRegister.LastBranchRecord)
8128 8129 @classmethod
8130 - def get_last_branch_location(cls):
8131 """ 8132 Returns the source and destination addresses of the last taken branch. 8133 8134 @rtype: tuple( int, int ) 8135 @return: Source and destination addresses of the last taken branch. 8136 8137 @raise WindowsError: 8138 Raises an exception on error. 8139 8140 @raise NotImplementedError: 8141 Current architecture is not C{i386} or C{amd64}. 8142 8143 @warning: 8144 This method uses the processor's machine specific registers (MSR). 8145 It could potentially brick your machine. 8146 It works on my machine, but your mileage may vary. 8147 8148 @note: 8149 It doesn't seem to work in VMWare or VirtualBox machines. 8150 Maybe it fails in other virtualization/emulation environments, 8151 no extensive testing was made so far. 8152 """ 8153 LastBranchFromIP = cls.read_msr(DebugRegister.LastBranchFromIP) 8154 LastBranchToIP = cls.read_msr(DebugRegister.LastBranchToIP) 8155 return ( LastBranchFromIP, LastBranchToIP )
8156