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

Source Code for Module winappdbg.crash

   1  #!~/.wine/drive_c/Python25/python.exe 
   2  # -*- coding: utf-8 -*- 
   3   
   4  # Copyright (c) 2009-2014, Mario Vilas 
   5  # All rights reserved. 
   6  # 
   7  # Redistribution and use in source and binary forms, with or without 
   8  # modification, are permitted provided that the following conditions are met: 
   9  # 
  10  #     * Redistributions of source code must retain the above copyright notice, 
  11  #       this list of conditions and the following disclaimer. 
  12  #     * Redistributions in binary form must reproduce the above copyright 
  13  #       notice,this list of conditions and the following disclaimer in the 
  14  #       documentation and/or other materials provided with the distribution. 
  15  #     * Neither the name of the copyright holder nor the names of its 
  16  #       contributors may be used to endorse or promote products derived from 
  17  #       this software without specific prior written permission. 
  18  # 
  19  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  20  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  21  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  22  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
  23  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  24  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  25  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  26  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  27  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  28  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  29  # POSSIBILITY OF SUCH DAMAGE. 
  30   
  31  """ 
  32  Crash dump support. 
  33   
  34  @group Crash reporting: 
  35      Crash, CrashDictionary 
  36   
  37  @group Warnings: 
  38      CrashWarning 
  39   
  40  @group Deprecated classes: 
  41      CrashContainer, CrashTable, CrashTableMSSQL, 
  42      VolatileCrashContainer, DummyCrashContainer 
  43  """ 
  44   
  45  __revision__ = "$Id: crash.py 1299 2013-12-20 09:30:55Z qvasimodo $" 
  46   
  47  __all__ = [ 
  48   
  49      # Object that represents a crash in the debugee. 
  50      'Crash', 
  51   
  52      # Crash storage. 
  53      'CrashDictionary', 
  54   
  55      # Warnings. 
  56      'CrashWarning', 
  57   
  58      # Backwards compatibility with WinAppDbg 1.4 and before. 
  59      'CrashContainer', 
  60      'CrashTable', 
  61      'CrashTableMSSQL', 
  62      'VolatileCrashContainer', 
  63      'DummyCrashContainer', 
  64  ] 
  65   
  66  import win32 
  67  from system import System 
  68  from textio import HexDump, CrashDump 
  69  from util import StaticClass, MemoryAddresses, PathOperations 
  70   
  71  import os 
  72  import time 
  73  import zlib 
  74  import warnings 
  75   
  76  # lazy imports 
  77  sql = None 
  78  anydbm = None 
  79   
  80  #============================================================================== 
  81   
  82  # Secure alternative to pickle, use it if present. 
  83  try: 
  84      import cerealizer 
  85      pickle = cerealizer 
86 87 # There is no optimization function for cerealized objects. 88 - def optimize(picklestring):
89 return picklestring
90 91 # There is no HIGHEST_PROTOCOL in cerealizer. 92 HIGHEST_PROTOCOL = 0 93 94 # Note: it's important NOT to provide backwards compatibility, otherwise 95 # it'd be just the same as not having this! 96 # 97 # To disable this security upgrade simply uncomment the following line: 98 # 99 # raise ImportError("Fallback to pickle for backwards compatibility") 100 101 # If cerealizer is not present fallback to the insecure pickle module. 102 except ImportError: 103 104 # Faster implementation of the pickle module as a C extension. 105 try: 106 import cPickle as pickle 107 108 # If all fails fallback to the classic pickle module. 109 except ImportError: 110 import pickle 111 112 # Fetch the highest protocol version. 113 HIGHEST_PROTOCOL = pickle.HIGHEST_PROTOCOL 114 115 # Try to use the pickle optimizer if found. 116 try: 117 from pickletools import optimize 118 except ImportError:
119 - def optimize(picklestring):
120 return picklestring
121
122 -class Marshaller (StaticClass):
123 """ 124 Custom pickler for L{Crash} objects. Optimizes the pickled data when using 125 the standard C{pickle} (or C{cPickle}) module. The pickled data is then 126 compressed using zlib. 127 """ 128 129 @staticmethod
130 - def dumps(obj, protocol=HIGHEST_PROTOCOL):
131 return zlib.compress(optimize(pickle.dumps(obj)), 9)
132 133 @staticmethod
134 - def loads(data):
135 return pickle.loads(zlib.decompress(data))
136
137 #============================================================================== 138 139 -class CrashWarning (Warning):
140 """ 141 An error occurred while gathering crash data. 142 Some data may be incomplete or missing. 143 """
144
145 #============================================================================== 146 147 # Crash object. Must be serializable. 148 -class Crash (object):
149 """ 150 Represents a crash, bug, or another interesting event in the debugee. 151 152 @group Basic information: 153 timeStamp, signature, eventCode, eventName, pid, tid, arch, os, bits, 154 registers, labelPC, pc, sp, fp 155 156 @group Optional information: 157 debugString, 158 modFileName, 159 lpBaseOfDll, 160 exceptionCode, 161 exceptionName, 162 exceptionDescription, 163 exceptionAddress, 164 exceptionLabel, 165 firstChance, 166 faultType, 167 faultAddress, 168 faultLabel, 169 isOurBreakpoint, 170 isSystemBreakpoint, 171 stackTrace, 172 stackTracePC, 173 stackTraceLabels, 174 stackTracePretty 175 176 @group Extra information: 177 commandLine, 178 environment, 179 environmentData, 180 registersPeek, 181 stackRange, 182 stackFrame, 183 stackPeek, 184 faultCode, 185 faultMem, 186 faultPeek, 187 faultDisasm, 188 memoryMap 189 190 @group Report: 191 briefReport, fullReport, notesReport, environmentReport, isExploitable 192 193 @group Notes: 194 addNote, getNotes, iterNotes, hasNotes, clearNotes, notes 195 196 @group Miscellaneous: 197 fetch_extra_data 198 199 @type timeStamp: float 200 @ivar timeStamp: Timestamp as returned by time.time(). 201 202 @type signature: object 203 @ivar signature: Approximately unique signature for the Crash object. 204 205 This signature can be used as an heuristic to determine if two crashes 206 were caused by the same software error. Ideally it should be treated as 207 as opaque serializable object that can be tested for equality. 208 209 @type notes: list( str ) 210 @ivar notes: List of strings, each string is a note. 211 212 @type eventCode: int 213 @ivar eventCode: Event code as defined by the Win32 API. 214 215 @type eventName: str 216 @ivar eventName: Event code user-friendly name. 217 218 @type pid: int 219 @ivar pid: Process global ID. 220 221 @type tid: int 222 @ivar tid: Thread global ID. 223 224 @type arch: str 225 @ivar arch: Processor architecture. 226 227 @type os: str 228 @ivar os: Operating system version. 229 230 May indicate a 64 bit version even if L{arch} and L{bits} indicate 32 231 bits. This means the crash occurred inside a WOW64 process. 232 233 @type bits: int 234 @ivar bits: C{32} or C{64} bits. 235 236 @type commandLine: None or str 237 @ivar commandLine: Command line for the target process. 238 239 C{None} if unapplicable or unable to retrieve. 240 241 @type environmentData: None or list of str 242 @ivar environmentData: Environment data for the target process. 243 244 C{None} if unapplicable or unable to retrieve. 245 246 @type environment: None or dict( str S{->} str ) 247 @ivar environment: Environment variables for the target process. 248 249 C{None} if unapplicable or unable to retrieve. 250 251 @type registers: dict( str S{->} int ) 252 @ivar registers: Dictionary mapping register names to their values. 253 254 @type registersPeek: None or dict( str S{->} str ) 255 @ivar registersPeek: Dictionary mapping register names to the data they point to. 256 257 C{None} if unapplicable or unable to retrieve. 258 259 @type labelPC: None or str 260 @ivar labelPC: Label pointing to the program counter. 261 262 C{None} or invalid if unapplicable or unable to retrieve. 263 264 @type debugString: None or str 265 @ivar debugString: Debug string sent by the debugee. 266 267 C{None} if unapplicable or unable to retrieve. 268 269 @type exceptionCode: None or int 270 @ivar exceptionCode: Exception code as defined by the Win32 API. 271 272 C{None} if unapplicable or unable to retrieve. 273 274 @type exceptionName: None or str 275 @ivar exceptionName: Exception code user-friendly name. 276 277 C{None} if unapplicable or unable to retrieve. 278 279 @type exceptionDescription: None or str 280 @ivar exceptionDescription: Exception description. 281 282 C{None} if unapplicable or unable to retrieve. 283 284 @type exceptionAddress: None or int 285 @ivar exceptionAddress: Memory address where the exception occured. 286 287 C{None} if unapplicable or unable to retrieve. 288 289 @type exceptionLabel: None or str 290 @ivar exceptionLabel: Label pointing to the exception address. 291 292 C{None} or invalid if unapplicable or unable to retrieve. 293 294 @type faultType: None or int 295 @ivar faultType: Access violation type. 296 Only applicable to memory faults. 297 Should be one of the following constants: 298 299 - L{win32.ACCESS_VIOLATION_TYPE_READ} 300 - L{win32.ACCESS_VIOLATION_TYPE_WRITE} 301 - L{win32.ACCESS_VIOLATION_TYPE_DEP} 302 303 C{None} if unapplicable or unable to retrieve. 304 305 @type faultAddress: None or int 306 @ivar faultAddress: Access violation memory address. 307 Only applicable to memory faults. 308 309 C{None} if unapplicable or unable to retrieve. 310 311 @type faultLabel: None or str 312 @ivar faultLabel: Label pointing to the access violation memory address. 313 Only applicable to memory faults. 314 315 C{None} if unapplicable or unable to retrieve. 316 317 @type firstChance: None or bool 318 @ivar firstChance: 319 C{True} for first chance exceptions, C{False} for second chance. 320 321 C{None} if unapplicable or unable to retrieve. 322 323 @type isOurBreakpoint: bool 324 @ivar isOurBreakpoint: 325 C{True} for breakpoints defined by the L{Debug} class, 326 C{False} otherwise. 327 328 C{None} if unapplicable. 329 330 @type isSystemBreakpoint: bool 331 @ivar isSystemBreakpoint: 332 C{True} for known system-defined breakpoints, 333 C{False} otherwise. 334 335 C{None} if unapplicable. 336 337 @type modFileName: None or str 338 @ivar modFileName: File name of module where the program counter points to. 339 340 C{None} or invalid if unapplicable or unable to retrieve. 341 342 @type lpBaseOfDll: None or int 343 @ivar lpBaseOfDll: Base of module where the program counter points to. 344 345 C{None} if unapplicable or unable to retrieve. 346 347 @type stackTrace: None or tuple of tuple( int, int, str ) 348 @ivar stackTrace: 349 Stack trace of the current thread as a tuple of 350 ( frame pointer, return address, module filename ). 351 352 C{None} or empty if unapplicable or unable to retrieve. 353 354 @type stackTracePretty: None or tuple of tuple( int, str ) 355 @ivar stackTracePretty: 356 Stack trace of the current thread as a tuple of 357 ( frame pointer, return location ). 358 359 C{None} or empty if unapplicable or unable to retrieve. 360 361 @type stackTracePC: None or tuple( int... ) 362 @ivar stackTracePC: Tuple of return addresses in the stack trace. 363 364 C{None} or empty if unapplicable or unable to retrieve. 365 366 @type stackTraceLabels: None or tuple( str... ) 367 @ivar stackTraceLabels: 368 Tuple of labels pointing to the return addresses in the stack trace. 369 370 C{None} or empty if unapplicable or unable to retrieve. 371 372 @type stackRange: tuple( int, int ) 373 @ivar stackRange: 374 Stack beginning and end pointers, in memory addresses order. 375 376 C{None} if unapplicable or unable to retrieve. 377 378 @type stackFrame: None or str 379 @ivar stackFrame: Data pointed to by the stack pointer. 380 381 C{None} or empty if unapplicable or unable to retrieve. 382 383 @type stackPeek: None or dict( int S{->} str ) 384 @ivar stackPeek: Dictionary mapping stack offsets to the data they point to. 385 386 C{None} or empty if unapplicable or unable to retrieve. 387 388 @type faultCode: None or str 389 @ivar faultCode: Data pointed to by the program counter. 390 391 C{None} or empty if unapplicable or unable to retrieve. 392 393 @type faultMem: None or str 394 @ivar faultMem: Data pointed to by the exception address. 395 396 C{None} or empty if unapplicable or unable to retrieve. 397 398 @type faultPeek: None or dict( intS{->} str ) 399 @ivar faultPeek: Dictionary mapping guessed pointers at L{faultMem} to the data they point to. 400 401 C{None} or empty if unapplicable or unable to retrieve. 402 403 @type faultDisasm: None or tuple of tuple( long, int, str, str ) 404 @ivar faultDisasm: Dissassembly around the program counter. 405 406 C{None} or empty if unapplicable or unable to retrieve. 407 408 @type memoryMap: None or list of L{win32.MemoryBasicInformation} objects. 409 @ivar memoryMap: Memory snapshot of the program. May contain the actual 410 data from the entire process memory if requested. 411 See L{fetch_extra_data} for more details. 412 413 C{None} or empty if unapplicable or unable to retrieve. 414 415 @type _rowid: int 416 @ivar _rowid: Row ID in the database. Internally used by the DAO layer. 417 Only present in crash dumps retrieved from the database. Do not rely 418 on this property to be present in future versions of WinAppDbg. 419 """ 420
421 - def __init__(self, event):
422 """ 423 @type event: L{Event} 424 @param event: Event object for crash. 425 """ 426 427 # First of all, take the timestamp. 428 self.timeStamp = time.time() 429 430 # Notes are initially empty. 431 self.notes = list() 432 433 # Get the process and thread, but dont't store them in the DB. 434 process = event.get_process() 435 thread = event.get_thread() 436 437 # Determine the architecture. 438 self.os = System.os 439 self.arch = process.get_arch() 440 self.bits = process.get_bits() 441 442 # The following properties are always retrieved for all events. 443 self.eventCode = event.get_event_code() 444 self.eventName = event.get_event_name() 445 self.pid = event.get_pid() 446 self.tid = event.get_tid() 447 self.registers = dict(thread.get_context()) 448 self.labelPC = process.get_label_at_address(self.pc) 449 450 # The following properties are only retrieved for some events. 451 self.commandLine = None 452 self.environment = None 453 self.environmentData = None 454 self.registersPeek = None 455 self.debugString = None 456 self.modFileName = None 457 self.lpBaseOfDll = None 458 self.exceptionCode = None 459 self.exceptionName = None 460 self.exceptionDescription = None 461 self.exceptionAddress = None 462 self.exceptionLabel = None 463 self.firstChance = None 464 self.faultType = None 465 self.faultAddress = None 466 self.faultLabel = None 467 self.isOurBreakpoint = None 468 self.isSystemBreakpoint = None 469 self.stackTrace = None 470 self.stackTracePC = None 471 self.stackTraceLabels = None 472 self.stackTracePretty = None 473 self.stackRange = None 474 self.stackFrame = None 475 self.stackPeek = None 476 self.faultCode = None 477 self.faultMem = None 478 self.faultPeek = None 479 self.faultDisasm = None 480 self.memoryMap = None 481 482 # Get information for debug string events. 483 if self.eventCode == win32.OUTPUT_DEBUG_STRING_EVENT: 484 self.debugString = event.get_debug_string() 485 486 # Get information for module load and unload events. 487 # For create and exit process events, get the information 488 # for the main module. 489 elif self.eventCode in (win32.CREATE_PROCESS_DEBUG_EVENT, 490 win32.EXIT_PROCESS_DEBUG_EVENT, 491 win32.LOAD_DLL_DEBUG_EVENT, 492 win32.UNLOAD_DLL_DEBUG_EVENT): 493 aModule = event.get_module() 494 self.modFileName = event.get_filename() 495 if not self.modFileName: 496 self.modFileName = aModule.get_filename() 497 self.lpBaseOfDll = event.get_module_base() 498 if not self.lpBaseOfDll: 499 self.lpBaseOfDll = aModule.get_base() 500 501 # Get some information for exception events. 502 # To get the remaining information call fetch_extra_data(). 503 elif self.eventCode == win32.EXCEPTION_DEBUG_EVENT: 504 505 # Exception information. 506 self.exceptionCode = event.get_exception_code() 507 self.exceptionName = event.get_exception_name() 508 self.exceptionDescription = event.get_exception_description() 509 self.exceptionAddress = event.get_exception_address() 510 self.firstChance = event.is_first_chance() 511 self.exceptionLabel = process.get_label_at_address( 512 self.exceptionAddress) 513 if self.exceptionCode in (win32.EXCEPTION_ACCESS_VIOLATION, 514 win32.EXCEPTION_GUARD_PAGE, 515 win32.EXCEPTION_IN_PAGE_ERROR): 516 self.faultType = event.get_fault_type() 517 self.faultAddress = event.get_fault_address() 518 self.faultLabel = process.get_label_at_address( 519 self.faultAddress) 520 elif self.exceptionCode in (win32.EXCEPTION_BREAKPOINT, 521 win32.EXCEPTION_SINGLE_STEP): 522 self.isOurBreakpoint = hasattr(event, 'breakpoint') \ 523 and event.breakpoint 524 self.isSystemBreakpoint = \ 525 process.is_system_defined_breakpoint(self.exceptionAddress) 526 527 # Stack trace. 528 try: 529 self.stackTracePretty = thread.get_stack_trace_with_labels() 530 except Exception, e: 531 warnings.warn( 532 "Cannot get stack trace with labels, reason: %s" % str(e), 533 CrashWarning) 534 try: 535 self.stackTrace = thread.get_stack_trace() 536 stackTracePC = [ ra for (_,ra,_) in self.stackTrace ] 537 self.stackTracePC = tuple(stackTracePC) 538 stackTraceLabels = [ process.get_label_at_address(ra) \ 539 for ra in self.stackTracePC ] 540 self.stackTraceLabels = tuple(stackTraceLabels) 541 except Exception, e: 542 warnings.warn("Cannot get stack trace, reason: %s" % str(e), 543 CrashWarning)
544
545 - def fetch_extra_data(self, event, takeMemorySnapshot = 0):
546 """ 547 Fetch extra data from the L{Event} object. 548 549 @note: Since this method may take a little longer to run, it's best to 550 call it only after you've determined the crash is interesting and 551 you want to save it. 552 553 @type event: L{Event} 554 @param event: Event object for crash. 555 556 @type takeMemorySnapshot: int 557 @param takeMemorySnapshot: 558 Memory snapshot behavior: 559 - C{0} to take no memory information (default). 560 - C{1} to take only the memory map. 561 See L{Process.get_memory_map}. 562 - C{2} to take a full memory snapshot. 563 See L{Process.take_memory_snapshot}. 564 - C{3} to take a live memory snapshot. 565 See L{Process.generate_memory_snapshot}. 566 """ 567 568 # Get the process and thread, we'll use them below. 569 process = event.get_process() 570 thread = event.get_thread() 571 572 # Get the command line for the target process. 573 try: 574 self.commandLine = process.get_command_line() 575 except Exception, e: 576 warnings.warn("Cannot get command line, reason: %s" % str(e), 577 CrashWarning) 578 579 # Get the environment variables for the target process. 580 try: 581 self.environmentData = process.get_environment_data() 582 self.environment = process.parse_environment_data( 583 self.environmentData) 584 except Exception, e: 585 warnings.warn("Cannot get environment, reason: %s" % str(e), 586 CrashWarning) 587 588 # Data pointed to by registers. 589 self.registersPeek = thread.peek_pointers_in_registers() 590 591 # Module where execution is taking place. 592 aModule = process.get_module_at_address(self.pc) 593 if aModule is not None: 594 self.modFileName = aModule.get_filename() 595 self.lpBaseOfDll = aModule.get_base() 596 597 # Contents of the stack frame. 598 try: 599 self.stackRange = thread.get_stack_range() 600 except Exception, e: 601 warnings.warn("Cannot get stack range, reason: %s" % str(e), 602 CrashWarning) 603 try: 604 self.stackFrame = thread.get_stack_frame() 605 stackFrame = self.stackFrame 606 except Exception, e: 607 self.stackFrame = thread.peek_stack_data() 608 stackFrame = self.stackFrame[:64] 609 if stackFrame: 610 self.stackPeek = process.peek_pointers_in_data(stackFrame) 611 612 # Code being executed. 613 self.faultCode = thread.peek_code_bytes() 614 try: 615 self.faultDisasm = thread.disassemble_around_pc(32) 616 except Exception, e: 617 warnings.warn("Cannot disassemble, reason: %s" % str(e), 618 CrashWarning) 619 620 # For memory related exceptions, get the memory contents 621 # of the location that caused the exception to be raised. 622 if self.eventCode == win32.EXCEPTION_DEBUG_EVENT: 623 if self.pc != self.exceptionAddress and self.exceptionCode in ( 624 win32.EXCEPTION_ACCESS_VIOLATION, 625 win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 626 win32.EXCEPTION_DATATYPE_MISALIGNMENT, 627 win32.EXCEPTION_IN_PAGE_ERROR, 628 win32.EXCEPTION_STACK_OVERFLOW, 629 win32.EXCEPTION_GUARD_PAGE, 630 ): 631 self.faultMem = process.peek(self.exceptionAddress, 64) 632 if self.faultMem: 633 self.faultPeek = process.peek_pointers_in_data( 634 self.faultMem) 635 636 # TODO: maybe add names and versions of DLLs and EXE? 637 638 # Take a snapshot of the process memory. Additionally get the 639 # memory contents if requested. 640 if takeMemorySnapshot == 1: 641 self.memoryMap = process.get_memory_map() 642 mappedFilenames = process.get_mapped_filenames(self.memoryMap) 643 for mbi in self.memoryMap: 644 mbi.filename = mappedFilenames.get(mbi.BaseAddress, None) 645 mbi.content = None 646 elif takeMemorySnapshot == 2: 647 self.memoryMap = process.take_memory_snapshot() 648 elif takeMemorySnapshot == 3: 649 self.memoryMap = process.generate_memory_snapshot()
650 651 @property
652 - def pc(self):
653 """ 654 Value of the program counter register. 655 656 @rtype: int 657 """ 658 try: 659 return self.registers['Eip'] # i386 660 except KeyError: 661 return self.registers['Rip'] # amd64
662 663 @property
664 - def sp(self):
665 """ 666 Value of the stack pointer register. 667 668 @rtype: int 669 """ 670 try: 671 return self.registers['Esp'] # i386 672 except KeyError: 673 return self.registers['Rsp'] # amd64
674 675 @property
676 - def fp(self):
677 """ 678 Value of the frame pointer register. 679 680 @rtype: int 681 """ 682 try: 683 return self.registers['Ebp'] # i386 684 except KeyError: 685 return self.registers['Rbp'] # amd64
686
687 - def __str__(self):
688 return self.fullReport()
689
690 - def key(self):
691 """ 692 Alias of L{signature}. Deprecated since WinAppDbg 1.5. 693 """ 694 warnings.warn("Crash.key() method was deprecated in WinAppDbg 1.5", 695 DeprecationWarning) 696 return self.signature
697 698 @property
699 - def signature(self):
700 if self.labelPC: 701 pc = self.labelPC 702 else: 703 pc = self.pc 704 if self.stackTraceLabels: 705 trace = self.stackTraceLabels 706 else: 707 trace = self.stackTracePC 708 return ( 709 self.arch, 710 self.eventCode, 711 self.exceptionCode, 712 pc, 713 trace, 714 self.debugString, 715 )
716 # TODO 717 # add the name and version of the binary where the crash happened? 718
719 - def isExploitable(self):
720 """ 721 Guess how likely is it that the bug causing the crash can be leveraged 722 into an exploitable vulnerability. 723 724 @note: Don't take this as an equivalent of a real exploitability 725 analysis, that can only be done by a human being! This is only 726 a guideline, useful for example to sort crashes - placing the most 727 interesting ones at the top. 728 729 @see: The heuristics are similar to those of the B{!exploitable} 730 extension for I{WinDBG}, which can be downloaded from here: 731 732 U{http://www.codeplex.com/msecdbg} 733 734 @rtype: tuple( str, str, str ) 735 @return: The first element of the tuple is the result of the analysis, 736 being one of the following: 737 738 - Not an exception 739 - Not exploitable 740 - Not likely exploitable 741 - Unknown 742 - Probably exploitable 743 - Exploitable 744 745 The second element of the tuple is a code to identify the matched 746 heuristic rule. 747 748 The third element of the tuple is a description string of the 749 reason behind the result. 750 """ 751 752 # Terminal rules 753 754 if self.eventCode != win32.EXCEPTION_DEBUG_EVENT: 755 return ("Not an exception", "NotAnException", "The event is not an exception.") 756 757 if self.stackRange and self.pc is not None and self.stackRange[0] <= self.pc < self.stackRange[1]: 758 return ("Exploitable", "StackCodeExecution", "Code execution from the stack is considered exploitable.") 759 760 # This rule is NOT from !exploitable 761 if self.stackRange and self.sp is not None and not (self.stackRange[0] <= self.sp < self.stackRange[1]): 762 return ("Exploitable", "StackPointerCorruption", "Stack pointer corruption is considered exploitable.") 763 764 if self.exceptionCode == win32.EXCEPTION_ILLEGAL_INSTRUCTION: 765 return ("Exploitable", "IllegalInstruction", "An illegal instruction exception indicates that the attacker controls execution flow.") 766 767 if self.exceptionCode == win32.EXCEPTION_PRIV_INSTRUCTION: 768 return ("Exploitable", "PrivilegedInstruction", "A privileged instruction exception indicates that the attacker controls execution flow.") 769 770 if self.exceptionCode == win32.EXCEPTION_GUARD_PAGE: 771 return ("Exploitable", "GuardPage", "A guard page violation indicates a stack overflow has occured, and the stack of another thread was reached (possibly the overflow length is not controlled by the attacker).") 772 773 if self.exceptionCode == win32.STATUS_STACK_BUFFER_OVERRUN: 774 return ("Exploitable", "GSViolation", "An overrun of a protected stack buffer has been detected. This is considered exploitable, and must be fixed.") 775 776 if self.exceptionCode == win32.STATUS_HEAP_CORRUPTION: 777 return ("Exploitable", "HeapCorruption", "Heap Corruption has been detected. This is considered exploitable, and must be fixed.") 778 779 if self.exceptionCode == win32.EXCEPTION_ACCESS_VIOLATION: 780 nearNull = self.faultAddress is None or MemoryAddresses.align_address_to_page_start(self.faultAddress) == 0 781 controlFlow = self.__is_control_flow() 782 blockDataMove = self.__is_block_data_move() 783 if self.faultType == win32.EXCEPTION_EXECUTE_FAULT: 784 if nearNull: 785 return ("Probably exploitable", "DEPViolation", "User mode DEP access violations are probably exploitable if near NULL.") 786 else: 787 return ("Exploitable", "DEPViolation", "User mode DEP access violations are exploitable.") 788 elif self.faultType == win32.EXCEPTION_WRITE_FAULT: 789 if nearNull: 790 return ("Probably exploitable", "WriteAV", "User mode write access violations that are near NULL are probably exploitable.") 791 else: 792 return ("Exploitable", "WriteAV", "User mode write access violations that are not near NULL are exploitable.") 793 elif self.faultType == win32.EXCEPTION_READ_FAULT: 794 if self.faultAddress == self.pc: 795 if nearNull: 796 return ("Probably exploitable", "ReadAVonIP", "Access violations at the instruction pointer are probably exploitable if near NULL.") 797 else: 798 return ("Exploitable", "ReadAVonIP", "Access violations at the instruction pointer are exploitable if not near NULL.") 799 if controlFlow: 800 if nearNull: 801 return ("Probably exploitable", "ReadAVonControlFlow", "Access violations near null in control flow instructions are considered probably exploitable.") 802 else: 803 return ("Exploitable", "ReadAVonControlFlow", "Access violations not near null in control flow instructions are considered exploitable.") 804 if blockDataMove: 805 return ("Probably exploitable", "ReadAVonBlockMove", "This is a read access violation in a block data move, and is therefore classified as probably exploitable.") 806 807 # Rule: Tainted information used to control branch addresses is considered probably exploitable 808 # Rule: Tainted information used to control the target of a later write is probably exploitable 809 810 # Non terminal rules 811 812 # XXX TODO add rule to check if code is in writeable memory (probably exploitable) 813 814 # XXX TODO maybe we should be returning a list of tuples instead? 815 816 result = ("Unknown", "Unknown", "Exploitability unknown.") 817 818 if self.exceptionCode == win32.EXCEPTION_ACCESS_VIOLATION: 819 if self.faultType == win32.EXCEPTION_READ_FAULT: 820 if nearNull: 821 result = ("Not likely exploitable", "ReadAVNearNull", "This is a user mode read access violation near null, and is probably not exploitable.") 822 823 elif self.exceptionCode == win32.EXCEPTION_INT_DIVIDE_BY_ZERO: 824 result = ("Not likely exploitable", "DivideByZero", "This is an integer divide by zero, and is probably not exploitable.") 825 826 elif self.exceptionCode == win32.EXCEPTION_FLT_DIVIDE_BY_ZERO: 827 result = ("Not likely exploitable", "DivideByZero", "This is a floating point divide by zero, and is probably not exploitable.") 828 829 elif self.exceptionCode in (win32.EXCEPTION_BREAKPOINT, win32.STATUS_WX86_BREAKPOINT): 830 result = ("Unknown", "Breakpoint", "While a breakpoint itself is probably not exploitable, it may also be an indication that an attacker is testing a target. In either case breakpoints should not exist in production code.") 831 832 # Rule: If the stack contains unknown symbols in user mode, call that out 833 # Rule: Tainted information used to control the source of a later block move unknown, but called out explicitly 834 # Rule: Tainted information used as an argument to a function is an unknown risk, but called out explicitly 835 # Rule: Tainted information used to control branch selection is an unknown risk, but called out explicitly 836 837 return result
838
839 - def __is_control_flow(self):
840 """ 841 Private method to tell if the instruction pointed to by the program 842 counter is a control flow instruction. 843 844 Currently only works for x86 and amd64 architectures. 845 """ 846 jump_instructions = ( 847 'jmp', 'jecxz', 'jcxz', 848 'ja', 'jnbe', 'jae', 'jnb', 'jb', 'jnae', 'jbe', 'jna', 'jc', 'je', 849 'jz', 'jnc', 'jne', 'jnz', 'jnp', 'jpo', 'jp', 'jpe', 'jg', 'jnle', 850 'jge', 'jnl', 'jl', 'jnge', 'jle', 'jng', 'jno', 'jns', 'jo', 'js' 851 ) 852 call_instructions = ( 'call', 'ret', 'retn' ) 853 loop_instructions = ( 'loop', 'loopz', 'loopnz', 'loope', 'loopne' ) 854 control_flow_instructions = call_instructions + loop_instructions + \ 855 jump_instructions 856 isControlFlow = False 857 instruction = None 858 if self.pc is not None and self.faultDisasm: 859 for disasm in self.faultDisasm: 860 if disasm[0] == self.pc: 861 instruction = disasm[2].lower().strip() 862 break 863 if instruction: 864 for x in control_flow_instructions: 865 if x in instruction: 866 isControlFlow = True 867 break 868 return isControlFlow
869
870 - def __is_block_data_move(self):
871 """ 872 Private method to tell if the instruction pointed to by the program 873 counter is a block data move instruction. 874 875 Currently only works for x86 and amd64 architectures. 876 """ 877 block_data_move_instructions = ('movs', 'stos', 'lods') 878 isBlockDataMove = False 879 instruction = None 880 if self.pc is not None and self.faultDisasm: 881 for disasm in self.faultDisasm: 882 if disasm[0] == self.pc: 883 instruction = disasm[2].lower().strip() 884 break 885 if instruction: 886 for x in block_data_move_instructions: 887 if x in instruction: 888 isBlockDataMove = True 889 break 890 return isBlockDataMove
891
892 - def briefReport(self):
893 """ 894 @rtype: str 895 @return: Short description of the event. 896 """ 897 if self.exceptionCode is not None: 898 if self.exceptionCode == win32.EXCEPTION_BREAKPOINT: 899 if self.isOurBreakpoint: 900 what = "Breakpoint hit" 901 elif self.isSystemBreakpoint: 902 what = "System breakpoint hit" 903 else: 904 what = "Assertion failed" 905 elif self.exceptionDescription: 906 what = self.exceptionDescription 907 elif self.exceptionName: 908 what = self.exceptionName 909 else: 910 what = "Exception %s" % \ 911 HexDump.integer(self.exceptionCode, self.bits) 912 if self.firstChance: 913 chance = 'first' 914 else: 915 chance = 'second' 916 if self.exceptionLabel: 917 where = self.exceptionLabel 918 elif self.exceptionAddress: 919 where = HexDump.address(self.exceptionAddress, self.bits) 920 elif self.labelPC: 921 where = self.labelPC 922 else: 923 where = HexDump.address(self.pc, self.bits) 924 msg = "%s (%s chance) at %s" % (what, chance, where) 925 elif self.debugString is not None: 926 if self.labelPC: 927 where = self.labelPC 928 else: 929 where = HexDump.address(self.pc, self.bits) 930 msg = "Debug string from %s: %r" % (where, self.debugString) 931 else: 932 if self.labelPC: 933 where = self.labelPC 934 else: 935 where = HexDump.address(self.pc, self.bits) 936 msg = "%s (%s) at %s" % ( 937 self.eventName, 938 HexDump.integer(self.eventCode, self.bits), 939 where 940 ) 941 return msg
942
943 - def fullReport(self, bShowNotes = True):
944 """ 945 @type bShowNotes: bool 946 @param bShowNotes: C{True} to show the user notes, C{False} otherwise. 947 948 @rtype: str 949 @return: Long description of the event. 950 """ 951 msg = self.briefReport() 952 msg += '\n' 953 954 if self.bits == 32: 955 width = 16 956 else: 957 width = 8 958 959 if self.eventCode == win32.EXCEPTION_DEBUG_EVENT: 960 (exploitability, expcode, expdescription) = self.isExploitable() 961 msg += '\nSecurity risk level: %s\n' % exploitability 962 msg += ' %s\n' % expdescription 963 964 if bShowNotes and self.notes: 965 msg += '\nNotes:\n' 966 msg += self.notesReport() 967 968 if self.commandLine: 969 msg += '\nCommand line: %s\n' % self.commandLine 970 971 if self.environment: 972 msg += '\nEnvironment:\n' 973 msg += self.environmentReport() 974 975 if not self.labelPC: 976 base = HexDump.address(self.lpBaseOfDll, self.bits) 977 if self.modFileName: 978 fn = PathOperations.pathname_to_filename(self.modFileName) 979 msg += '\nRunning in %s (%s)\n' % (fn, base) 980 else: 981 msg += '\nRunning in module at %s\n' % base 982 983 if self.registers: 984 msg += '\nRegisters:\n' 985 msg += CrashDump.dump_registers(self.registers) 986 if self.registersPeek: 987 msg += '\n' 988 msg += CrashDump.dump_registers_peek(self.registers, 989 self.registersPeek, 990 width = width) 991 992 if self.faultDisasm: 993 msg += '\nCode disassembly:\n' 994 msg += CrashDump.dump_code(self.faultDisasm, self.pc, 995 bits = self.bits) 996 997 if self.stackTrace: 998 msg += '\nStack trace:\n' 999 if self.stackTracePretty: 1000 msg += CrashDump.dump_stack_trace_with_labels( 1001 self.stackTracePretty, 1002 bits = self.bits) 1003 else: 1004 msg += CrashDump.dump_stack_trace(self.stackTrace, 1005 bits = self.bits) 1006 1007 if self.stackFrame: 1008 if self.stackPeek: 1009 msg += '\nStack pointers:\n' 1010 msg += CrashDump.dump_stack_peek(self.stackPeek, width = width) 1011 msg += '\nStack dump:\n' 1012 msg += HexDump.hexblock(self.stackFrame, self.sp, 1013 bits = self.bits, width = width) 1014 1015 if self.faultCode and not self.modFileName: 1016 msg += '\nCode dump:\n' 1017 msg += HexDump.hexblock(self.faultCode, self.pc, 1018 bits = self.bits, width = width) 1019 1020 if self.faultMem: 1021 if self.faultPeek: 1022 msg += '\nException address pointers:\n' 1023 msg += CrashDump.dump_data_peek(self.faultPeek, 1024 self.exceptionAddress, 1025 bits = self.bits, 1026 width = width) 1027 msg += '\nException address dump:\n' 1028 msg += HexDump.hexblock(self.faultMem, self.exceptionAddress, 1029 bits = self.bits, width = width) 1030 1031 if self.memoryMap: 1032 msg += '\nMemory map:\n' 1033 mappedFileNames = dict() 1034 for mbi in self.memoryMap: 1035 if hasattr(mbi, 'filename') and mbi.filename: 1036 mappedFileNames[mbi.BaseAddress] = mbi.filename 1037 msg += CrashDump.dump_memory_map(self.memoryMap, mappedFileNames, 1038 bits = self.bits) 1039 1040 if not msg.endswith('\n\n'): 1041 if not msg.endswith('\n'): 1042 msg += '\n' 1043 msg += '\n' 1044 return msg
1045
1046 - def environmentReport(self):
1047 """ 1048 @rtype: str 1049 @return: The process environment variables, 1050 merged and formatted for a report. 1051 """ 1052 msg = '' 1053 if self.environment: 1054 for key, value in self.environment.iteritems(): 1055 msg += ' %s=%s\n' % (key, value) 1056 return msg
1057
1058 - def notesReport(self):
1059 """ 1060 @rtype: str 1061 @return: All notes, merged and formatted for a report. 1062 """ 1063 msg = '' 1064 if self.notes: 1065 for n in self.notes: 1066 n = n.strip('\n') 1067 if '\n' in n: 1068 n = n.strip('\n') 1069 msg += ' * %s\n' % n.pop(0) 1070 for x in n: 1071 msg += ' %s\n' % x 1072 else: 1073 msg += ' * %s\n' % n 1074 return msg
1075
1076 - def addNote(self, msg):
1077 """ 1078 Add a note to the crash event. 1079 1080 @type msg: str 1081 @param msg: Note text. 1082 """ 1083 self.notes.append(msg)
1084
1085 - def clearNotes(self):
1086 """ 1087 Clear the notes of this crash event. 1088 """ 1089 self.notes = list()
1090
1091 - def getNotes(self):
1092 """ 1093 Get the list of notes of this crash event. 1094 1095 @rtype: list( str ) 1096 @return: List of notes. 1097 """ 1098 return self.notes
1099
1100 - def iterNotes(self):
1101 """ 1102 Iterate the notes of this crash event. 1103 1104 @rtype: listiterator 1105 @return: Iterator of the list of notes. 1106 """ 1107 return self.notes.__iter__()
1108
1109 - def hasNotes(self):
1110 """ 1111 @rtype: bool 1112 @return: C{True} if there are notes for this crash event. 1113 """ 1114 return bool( self.notes )
1115
1116 #============================================================================== 1117 1118 -class CrashContainer (object):
1119 """ 1120 Old crash dump persistencer using a DBM database. 1121 Doesn't support duplicate crashes. 1122 1123 @warning: 1124 DBM database support is provided for backwards compatibility with older 1125 versions of WinAppDbg. New applications should not use this class. 1126 Also, DBM databases in Python suffer from multiple problems that can 1127 easily be avoided by switching to a SQL database. 1128 1129 @see: If you really must use a DBM database, try the standard C{shelve} 1130 module instead: U{http://docs.python.org/library/shelve.html} 1131 1132 @group Marshalling configuration: 1133 optimizeKeys, optimizeValues, compressKeys, compressValues, escapeKeys, 1134 escapeValues, binaryKeys, binaryValues 1135 1136 @type optimizeKeys: bool 1137 @cvar optimizeKeys: Ignored by the current implementation. 1138 1139 Up to WinAppDbg 1.4 this setting caused the database keys to be 1140 optimized when pickled with the standard C{pickle} module. 1141 1142 But with a DBM database backend that causes inconsistencies, since the 1143 same key can be serialized into multiple optimized pickles, thus losing 1144 uniqueness. 1145 1146 @type optimizeValues: bool 1147 @cvar optimizeValues: C{True} to optimize the marshalling of keys, C{False} 1148 otherwise. Only used with the C{pickle} module, ignored when using the 1149 more secure C{cerealizer} module. 1150 1151 @type compressKeys: bool 1152 @cvar compressKeys: C{True} to compress keys when marshalling, C{False} 1153 to leave them uncompressed. 1154 1155 @type compressValues: bool 1156 @cvar compressValues: C{True} to compress values when marshalling, C{False} 1157 to leave them uncompressed. 1158 1159 @type escapeKeys: bool 1160 @cvar escapeKeys: C{True} to escape keys when marshalling, C{False} 1161 to leave them uncompressed. 1162 1163 @type escapeValues: bool 1164 @cvar escapeValues: C{True} to escape values when marshalling, C{False} 1165 to leave them uncompressed. 1166 1167 @type binaryKeys: bool 1168 @cvar binaryKeys: C{True} to marshall keys to binary format (the Python 1169 C{buffer} type), C{False} to use text marshalled keys (C{str} type). 1170 1171 @type binaryValues: bool 1172 @cvar binaryValues: C{True} to marshall values to binary format (the Python 1173 C{buffer} type), C{False} to use text marshalled values (C{str} type). 1174 """ 1175 1176 optimizeKeys = False 1177 optimizeValues = True 1178 compressKeys = False 1179 compressValues = True 1180 escapeKeys = False 1181 escapeValues = False 1182 binaryKeys = False 1183 binaryValues = False 1184
1185 - def __init__(self, filename = None, allowRepeatedKeys = False):
1186 """ 1187 @type filename: str 1188 @param filename: (Optional) File name for crash database. 1189 If no filename is specified, the container is volatile. 1190 1191 Volatile containers are stored only in memory and 1192 destroyed when they go out of scope. 1193 1194 @type allowRepeatedKeys: bool 1195 @param allowRepeatedKeys: 1196 Currently not supported, always use C{False}. 1197 """ 1198 if allowRepeatedKeys: 1199 raise NotImplementedError() 1200 self.__filename = filename 1201 if filename: 1202 global anydbm 1203 if not anydbm: 1204 import anydbm 1205 self.__db = anydbm.open(filename, 'c') 1206 self.__keys = dict([ (self.unmarshall_key(mk), mk) 1207 for mk in self.__db.keys() ]) 1208 else: 1209 self.__db = dict() 1210 self.__keys = dict()
1211
1212 - def remove_key(self, key):
1213 """ 1214 Removes the given key from the set of known keys. 1215 1216 @type key: L{Crash} key. 1217 @param key: Key to remove. 1218 """ 1219 del self.__keys[key]
1220
1221 - def marshall_key(self, key):
1222 """ 1223 Marshalls a Crash key to be used in the database. 1224 1225 @see: L{__init__} 1226 1227 @type key: L{Crash} key. 1228 @param key: Key to convert. 1229 1230 @rtype: str or buffer 1231 @return: Converted key. 1232 """ 1233 if key in self.__keys: 1234 return self.__keys[key] 1235 skey = pickle.dumps(key, protocol = 0) 1236 if self.compressKeys: 1237 skey = zlib.compress(skey, zlib.Z_BEST_COMPRESSION) 1238 if self.escapeKeys: 1239 skey = skey.encode('hex') 1240 if self.binaryKeys: 1241 skey = buffer(skey) 1242 self.__keys[key] = skey 1243 return skey
1244
1245 - def unmarshall_key(self, key):
1246 """ 1247 Unmarshalls a Crash key read from the database. 1248 1249 @type key: str or buffer 1250 @param key: Key to convert. 1251 1252 @rtype: L{Crash} key. 1253 @return: Converted key. 1254 """ 1255 key = str(key) 1256 if self.escapeKeys: 1257 key = key.decode('hex') 1258 if self.compressKeys: 1259 key = zlib.decompress(key) 1260 key = pickle.loads(key) 1261 return key
1262
1263 - def marshall_value(self, value, storeMemoryMap = False):
1264 """ 1265 Marshalls a Crash object to be used in the database. 1266 By default the C{memoryMap} member is B{NOT} stored here. 1267 1268 @warning: Setting the C{storeMemoryMap} argument to C{True} can lead to 1269 a severe performance penalty! 1270 1271 @type value: L{Crash} 1272 @param value: Object to convert. 1273 1274 @type storeMemoryMap: bool 1275 @param storeMemoryMap: C{True} to store the memory map, C{False} 1276 otherwise. 1277 1278 @rtype: str 1279 @return: Converted object. 1280 """ 1281 if hasattr(value, 'memoryMap'): 1282 crash = value 1283 memoryMap = crash.memoryMap 1284 try: 1285 crash.memoryMap = None 1286 if storeMemoryMap and memoryMap is not None: 1287 # convert the generator to a list 1288 crash.memoryMap = list(memoryMap) 1289 if self.optimizeValues: 1290 value = pickle.dumps(crash, protocol = HIGHEST_PROTOCOL) 1291 value = optimize(value) 1292 else: 1293 value = pickle.dumps(crash, protocol = 0) 1294 finally: 1295 crash.memoryMap = memoryMap 1296 del memoryMap 1297 del crash 1298 if self.compressValues: 1299 value = zlib.compress(value, zlib.Z_BEST_COMPRESSION) 1300 if self.escapeValues: 1301 value = value.encode('hex') 1302 if self.binaryValues: 1303 value = buffer(value) 1304 return value
1305
1306 - def unmarshall_value(self, value):
1307 """ 1308 Unmarshalls a Crash object read from the database. 1309 1310 @type value: str 1311 @param value: Object to convert. 1312 1313 @rtype: L{Crash} 1314 @return: Converted object. 1315 """ 1316 value = str(value) 1317 if self.escapeValues: 1318 value = value.decode('hex') 1319 if self.compressValues: 1320 value = zlib.decompress(value) 1321 value = pickle.loads(value) 1322 return value
1323 1324 # The interface is meant to be similar to a Python set. 1325 # However it may not be necessary to implement all of the set methods. 1326 # Other methods like get, has_key, iterkeys and itervalues 1327 # are dictionary-like. 1328
1329 - def __len__(self):
1330 """ 1331 @rtype: int 1332 @return: Count of known keys. 1333 """ 1334 return len(self.__keys)
1335
1336 - def __bool__(self):
1337 """ 1338 @rtype: bool 1339 @return: C{False} if there are no known keys. 1340 """ 1341 return bool(self.__keys)
1342
1343 - def __contains__(self, crash):
1344 """ 1345 @type crash: L{Crash} 1346 @param crash: Crash object. 1347 1348 @rtype: bool 1349 @return: 1350 C{True} if a Crash object with the same key is in the container. 1351 """ 1352 return self.has_key( crash.key() )
1353
1354 - def has_key(self, key):
1355 """ 1356 @type key: L{Crash} key. 1357 @param key: Key to find. 1358 1359 @rtype: bool 1360 @return: C{True} if the key is present in the set of known keys. 1361 """ 1362 return key in self.__keys
1363
1364 - def iterkeys(self):
1365 """ 1366 @rtype: iterator 1367 @return: Iterator of known L{Crash} keys. 1368 """ 1369 return self.__keys.iterkeys()
1370
1371 - class __CrashContainerIterator (object):
1372 """ 1373 Iterator of Crash objects. Returned by L{CrashContainer.__iter__}. 1374 """ 1375
1376 - def __init__(self, container):
1377 """ 1378 @type container: L{CrashContainer} 1379 @param container: Crash set to iterate. 1380 """ 1381 # It's important to keep a reference to the CrashContainer, 1382 # rather than it's underlying database. 1383 # Otherwise the destructor of CrashContainer may close the 1384 # database while we're still iterating it. 1385 # 1386 # TODO: lock the database when iterating it. 1387 # 1388 self.__container = container 1389 self.__keys_iter = container.iterkeys()
1390
1391 - def next(self):
1392 """ 1393 @rtype: L{Crash} 1394 @return: A B{copy} of a Crash object in the L{CrashContainer}. 1395 @raise StopIteration: No more items left. 1396 """ 1397 key = self.__keys_iter.next() 1398 return self.__container.get(key)
1399
1400 - def __del__(self):
1401 "Class destructor. Closes the database when this object is destroyed." 1402 try: 1403 if self.__filename: 1404 self.__db.close() 1405 except: 1406 pass
1407
1408 - def __iter__(self):
1409 """ 1410 @see: L{itervalues} 1411 @rtype: iterator 1412 @return: Iterator of the contained L{Crash} objects. 1413 """ 1414 return self.itervalues()
1415
1416 - def itervalues(self):
1417 """ 1418 @rtype: iterator 1419 @return: Iterator of the contained L{Crash} objects. 1420 1421 @warning: A B{copy} of each object is returned, 1422 so any changes made to them will be lost. 1423 1424 To preserve changes do the following: 1425 1. Keep a reference to the object. 1426 2. Delete the object from the set. 1427 3. Modify the object and add it again. 1428 """ 1429 return self.__CrashContainerIterator(self)
1430
1431 - def add(self, crash):
1432 """ 1433 Adds a new crash to the container. 1434 If the crash appears to be already known, it's ignored. 1435 1436 @see: L{Crash.key} 1437 1438 @type crash: L{Crash} 1439 @param crash: Crash object to add. 1440 """ 1441 if crash not in self: 1442 key = crash.key() 1443 skey = self.marshall_key(key) 1444 data = self.marshall_value(crash, storeMemoryMap = True) 1445 self.__db[skey] = data
1446
1447 - def __delitem__(self, key):
1448 """ 1449 Removes a crash from the container. 1450 1451 @type key: L{Crash} unique key. 1452 @param key: Key of the crash to get. 1453 """ 1454 skey = self.marshall_key(key) 1455 del self.__db[skey] 1456 self.remove_key(key)
1457
1458 - def remove(self, crash):
1459 """ 1460 Removes a crash from the container. 1461 1462 @type crash: L{Crash} 1463 @param crash: Crash object to remove. 1464 """ 1465 del self[ crash.key() ]
1466
1467 - def get(self, key):
1468 """ 1469 Retrieves a crash from the container. 1470 1471 @type key: L{Crash} unique key. 1472 @param key: Key of the crash to get. 1473 1474 @rtype: L{Crash} object. 1475 @return: Crash matching the given key. 1476 1477 @see: L{iterkeys} 1478 @warning: A B{copy} of each object is returned, 1479 so any changes made to them will be lost. 1480 1481 To preserve changes do the following: 1482 1. Keep a reference to the object. 1483 2. Delete the object from the set. 1484 3. Modify the object and add it again. 1485 """ 1486 skey = self.marshall_key(key) 1487 data = self.__db[skey] 1488 crash = self.unmarshall_value(data) 1489 return crash
1490
1491 - def __getitem__(self, key):
1492 """ 1493 Retrieves a crash from the container. 1494 1495 @type key: L{Crash} unique key. 1496 @param key: Key of the crash to get. 1497 1498 @rtype: L{Crash} object. 1499 @return: Crash matching the given key. 1500 1501 @see: L{iterkeys} 1502 @warning: A B{copy} of each object is returned, 1503 so any changes made to them will be lost. 1504 1505 To preserve changes do the following: 1506 1. Keep a reference to the object. 1507 2. Delete the object from the set. 1508 3. Modify the object and add it again. 1509 """ 1510 return self.get(key)
1511
1512 #============================================================================== 1513 1514 -class CrashDictionary(object):
1515 """ 1516 Dictionary-like persistence interface for L{Crash} objects. 1517 1518 Currently the only implementation is through L{sql.CrashDAO}. 1519 """ 1520
1521 - def __init__(self, url, creator = None, allowRepeatedKeys = True):
1522 """ 1523 @type url: str 1524 @param url: Connection URL of the crash database. 1525 See L{sql.CrashDAO.__init__} for more details. 1526 1527 @type creator: callable 1528 @param creator: (Optional) Callback function that creates the SQL 1529 database connection. 1530 1531 Normally it's not necessary to use this argument. However in some 1532 odd cases you may need to customize the database connection, for 1533 example when using the integrated authentication in MSSQL. 1534 1535 @type allowRepeatedKeys: bool 1536 @param allowRepeatedKeys: 1537 If C{True} all L{Crash} objects are stored. 1538 1539 If C{False} any L{Crash} object with the same signature as a 1540 previously existing object will be ignored. 1541 """ 1542 global sql 1543 if sql is None: 1544 import sql 1545 self._allowRepeatedKeys = allowRepeatedKeys 1546 self._dao = sql.CrashDAO(url, creator)
1547
1548 - def add(self, crash):
1549 """ 1550 Adds a new crash to the container. 1551 1552 @note: 1553 When the C{allowRepeatedKeys} parameter of the constructor 1554 is set to C{False}, duplicated crashes are ignored. 1555 1556 @see: L{Crash.key} 1557 1558 @type crash: L{Crash} 1559 @param crash: Crash object to add. 1560 """ 1561 self._dao.add(crash, self._allowRepeatedKeys)
1562
1563 - def get(self, key):
1564 """ 1565 Retrieves a crash from the container. 1566 1567 @type key: L{Crash} signature. 1568 @param key: Heuristic signature of the crash to get. 1569 1570 @rtype: L{Crash} object. 1571 @return: Crash matching the given signature. If more than one is found, 1572 retrieve the newest one. 1573 1574 @see: L{iterkeys} 1575 @warning: A B{copy} of each object is returned, 1576 so any changes made to them will be lost. 1577 1578 To preserve changes do the following: 1579 1. Keep a reference to the object. 1580 2. Delete the object from the set. 1581 3. Modify the object and add it again. 1582 """ 1583 found = self._dao.find(signature=key, limit=1, order=-1) 1584 if not found: 1585 raise KeyError(key) 1586 return found[0]
1587
1588 - def __iter__(self):
1589 """ 1590 @rtype: iterator 1591 @return: Iterator of the contained L{Crash} objects. 1592 """ 1593 offset = 0 1594 limit = 10 1595 while 1: 1596 found = self._dao.find(offset=offset, limit=limit) 1597 if not found: 1598 break 1599 offset += len(found) 1600 for crash in found: 1601 yield crash
1602
1603 - def itervalues(self):
1604 """ 1605 @rtype: iterator 1606 @return: Iterator of the contained L{Crash} objects. 1607 """ 1608 return self.__iter__()
1609
1610 - def iterkeys(self):
1611 """ 1612 @rtype: iterator 1613 @return: Iterator of the contained L{Crash} heuristic signatures. 1614 """ 1615 for crash in self: 1616 yield crash.signature # FIXME this gives repeated results!
1617
1618 - def __contains__(self, crash):
1619 """ 1620 @type crash: L{Crash} 1621 @param crash: Crash object. 1622 1623 @rtype: bool 1624 @return: C{True} if the Crash object is in the container. 1625 """ 1626 return self._dao.count(signature=crash.signature) > 0
1627
1628 - def has_key(self, key):
1629 """ 1630 @type key: L{Crash} signature. 1631 @param key: Heuristic signature of the crash to get. 1632 1633 @rtype: bool 1634 @return: C{True} if a matching L{Crash} object is in the container. 1635 """ 1636 return self._dao.count(signature=key) > 0
1637
1638 - def __len__(self):
1639 """ 1640 @rtype: int 1641 @return: Count of L{Crash} elements in the container. 1642 """ 1643 return self._dao.count()
1644
1645 - def __bool__(self):
1646 """ 1647 @rtype: bool 1648 @return: C{False} if the container is empty. 1649 """ 1650 return bool( len(self) )
1651
1652 -class CrashTable(CrashDictionary):
1653 """ 1654 Old crash dump persistencer using a SQLite database. 1655 1656 @warning: 1657 Superceded by L{CrashDictionary} since WinAppDbg 1.5. 1658 New applications should not use this class. 1659 """ 1660
1661 - def __init__(self, location = None, allowRepeatedKeys = True):
1662 """ 1663 @type location: str 1664 @param location: (Optional) Location of the crash database. 1665 If the location is a filename, it's an SQLite database file. 1666 1667 If no location is specified, the container is volatile. 1668 Volatile containers are stored only in memory and 1669 destroyed when they go out of scope. 1670 1671 @type allowRepeatedKeys: bool 1672 @param allowRepeatedKeys: 1673 If C{True} all L{Crash} objects are stored. 1674 1675 If C{False} any L{Crash} object with the same signature as a 1676 previously existing object will be ignored. 1677 """ 1678 warnings.warn( 1679 "The %s class is deprecated since WinAppDbg 1.5." % self.__class__, 1680 DeprecationWarning) 1681 if location: 1682 url = "sqlite:///%s" % location 1683 else: 1684 url = "sqlite://" 1685 super(CrashTable, self).__init__(url, allowRepeatedKeys)
1686
1687 -class CrashTableMSSQL (CrashDictionary):
1688 """ 1689 Old crash dump persistencer using a Microsoft SQL Server database. 1690 1691 @warning: 1692 Superceded by L{CrashDictionary} since WinAppDbg 1.5. 1693 New applications should not use this class. 1694 """ 1695
1696 - def __init__(self, location = None, allowRepeatedKeys = True):
1697 """ 1698 @type location: str 1699 @param location: Location of the crash database. 1700 It must be an ODBC connection string. 1701 1702 @type allowRepeatedKeys: bool 1703 @param allowRepeatedKeys: 1704 If C{True} all L{Crash} objects are stored. 1705 1706 If C{False} any L{Crash} object with the same signature as a 1707 previously existing object will be ignored. 1708 """ 1709 warnings.warn( 1710 "The %s class is deprecated since WinAppDbg 1.5." % self.__class__, 1711 DeprecationWarning) 1712 import urllib 1713 url = "mssql+pyodbc:///?odbc_connect=" + urllib.quote_plus(location) 1714 super(CrashTableMSSQL, self).__init__(url, allowRepeatedKeys)
1715
1716 -class VolatileCrashContainer (CrashTable):
1717 """ 1718 Old in-memory crash dump storage. 1719 1720 @warning: 1721 Superceded by L{CrashDictionary} since WinAppDbg 1.5. 1722 New applications should not use this class. 1723 """ 1724
1725 - def __init__(self, allowRepeatedKeys = True):
1726 """ 1727 Volatile containers are stored only in memory and 1728 destroyed when they go out of scope. 1729 1730 @type allowRepeatedKeys: bool 1731 @param allowRepeatedKeys: 1732 If C{True} all L{Crash} objects are stored. 1733 1734 If C{False} any L{Crash} object with the same key as a 1735 previously existing object will be ignored. 1736 """ 1737 super(VolatileCrashContainer, self).__init__( 1738 allowRepeatedKeys=allowRepeatedKeys)
1739
1740 -class DummyCrashContainer(object):
1741 """ 1742 Fakes a database of volatile Crash objects, 1743 trying to mimic part of it's interface, but 1744 doesn't actually store anything. 1745 1746 Normally applications don't need to use this. 1747 1748 @see: L{CrashDictionary} 1749 """ 1750
1751 - def __init__(self, allowRepeatedKeys = True):
1752 """ 1753 Fake containers don't store L{Crash} objects, but they implement the 1754 interface properly. 1755 1756 @type allowRepeatedKeys: bool 1757 @param allowRepeatedKeys: 1758 Mimics the duplicate filter behavior found in real containers. 1759 """ 1760 self.__keys = set() 1761 self.__count = 0 1762 self.__allowRepeatedKeys = allowRepeatedKeys
1763
1764 - def __contains__(self, crash):
1765 """ 1766 @type crash: L{Crash} 1767 @param crash: Crash object. 1768 1769 @rtype: bool 1770 @return: C{True} if the Crash object is in the container. 1771 """ 1772 return crash.signature in self.__keys
1773
1774 - def __len__(self):
1775 """ 1776 @rtype: int 1777 @return: Count of L{Crash} elements in the container. 1778 """ 1779 if self.__allowRepeatedKeys: 1780 return self.__count 1781 return len( self.__keys )
1782
1783 - def __bool__(self):
1784 """ 1785 @rtype: bool 1786 @return: C{False} if the container is empty. 1787 """ 1788 return bool( len(self) )
1789
1790 - def add(self, crash):
1791 """ 1792 Adds a new crash to the container. 1793 1794 @note: 1795 When the C{allowRepeatedKeys} parameter of the constructor 1796 is set to C{False}, duplicated crashes are ignored. 1797 1798 @see: L{Crash.key} 1799 1800 @type crash: L{Crash} 1801 @param crash: Crash object to add. 1802 """ 1803 self.__keys.add( crash.signature ) 1804 self.__count += 1
1805
1806 - def get(self, key):
1807 """ 1808 This method is not supported. 1809 """ 1810 raise NotImplementedError()
1811
1812 - def has_key(self, key):
1813 """ 1814 @type key: L{Crash} signature. 1815 @param key: Heuristic signature of the crash to get. 1816 1817 @rtype: bool 1818 @return: C{True} if a matching L{Crash} object is in the container. 1819 """ 1820 return self.__keys.has_key( key )
1821
1822 - def iterkeys(self):
1823 """ 1824 @rtype: iterator 1825 @return: Iterator of the contained L{Crash} object keys. 1826 1827 @see: L{get} 1828 @warning: A B{copy} of each object is returned, 1829 so any changes made to them will be lost. 1830 1831 To preserve changes do the following: 1832 1. Keep a reference to the object. 1833 2. Delete the object from the set. 1834 3. Modify the object and add it again. 1835 """ 1836 return iter(self.__keys)
1837 1838 #============================================================================== 1839 # Register the Crash class with the secure serializer. 1840 1841 try: 1842 cerealizer.register(Crash) 1843 cerealizer.register(win32.MemoryBasicInformation) 1844 except NameError: 1845 pass 1846