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