1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 """
29 Crash logging module.
30 """
31
32 __revision__ = "$Id: crash.py 549 2009-12-13 23:33:54Z qvasimodo $"
33
34 __all__ = [
35
36 'Crash',
37
38
39 'CrashContainer',
40
41
42 'CrashTable',
43
44
45 'VolatileCrashContainer',
46
47
48 'DummyCrashContainer',
49 ]
50
51 from system import MemoryAddresses, PathOperations
52 from textio import HexDump, CrashDump
53 import win32
54
55 import os
56 import time
57 import zlib
58 import traceback
59
60 try:
61 import cPickle as pickle
62 except ImportError:
63 import pickle
64
65 try:
66 from pickletools import optimize
67 except ImportError:
70
71
72 anydbm = None
73 sqlite = None
74
75
76
77 -class Crash (object):
78 """
79 Represents a crash, bug, or another interesting event in the debugee.
80
81 @group Key:
82 key
83
84 @group Report:
85 briefReport, fullReport, notesReport
86
87 @group Notes:
88 addNote, getNotes, iterNotes, hasNotes, clearNotes
89
90 @type timeStamp: float
91 @ivar timeStamp: Timestamp as returned by time.time().
92
93 @type notes: list( str )
94 @ivar notes: List of strings, each string is a note.
95
96 @type eventCode: int
97 @ivar eventCode: Event code as defined by the Win32 API.
98
99 @type eventName: str
100 @ivar eventName: Event code user-friendly name.
101
102 @type pid: int
103 @ivar pid: Process global ID.
104
105 @type tid: int
106 @ivar tid: Thread global ID.
107
108 @type registers: dict( str S{->} int )
109 @ivar registers: Dictionary mapping register names to their values.
110
111 @type registersPeek: None or dict( str S{->} str )
112 @ivar registersPeek: Dictionary mapping register names to the data they point to.
113
114 C{None} if unapplicable or unable to retrieve.
115
116 @type labelPC: None or str
117 @ivar labelPC: Label pointing to the program counter.
118
119 C{None} or invalid if unapplicable or unable to retrieve.
120
121 @type debugString: None or str
122 @ivar debugString: Debug string sent by the debugee.
123
124 C{None} if unapplicable or unable to retrieve.
125
126 @type exceptionCode: None or int
127 @ivar exceptionCode: Exception code as defined by the Win32 API.
128
129 C{None} if unapplicable or unable to retrieve.
130
131 @type exceptionName: None or str
132 @ivar exceptionName: Exception code user-friendly name.
133
134 C{None} if unapplicable or unable to retrieve.
135
136 @type exceptionAddress: None or int
137 @ivar exceptionAddress: Memory address where the exception occured.
138
139 C{None} if unapplicable or unable to retrieve.
140
141 @type exceptionLabel: None or str
142 @ivar exceptionLabel: Label pointing to the exception address.
143
144 C{None} or invalid if unapplicable or unable to retrieve.
145
146 @type faultType: None or int
147 @ivar faultType: Access violation type.
148 Only applicable to memory faults.
149 Should be one of the following constants:
150
151 - L{win32.ACCESS_VIOLATION_TYPE_READ}
152 - L{win32.ACCESS_VIOLATION_TYPE_WRITE}
153 - L{win32.ACCESS_VIOLATION_TYPE_DEP}
154
155 C{None} if unapplicable or unable to retrieve.
156
157 @type faultAddress: None or int
158 @ivar faultAddress: Access violation memory address.
159 Only applicable to memory faults.
160
161 C{None} if unapplicable or unable to retrieve.
162
163 @type faultLabel: None or str
164 @ivar faultLabel: Label pointing to the access violation memory address.
165 Only applicable to memory faults.
166
167 C{None} if unapplicable or unable to retrieve.
168
169 @type firstChance: None or bool
170 @ivar firstChance:
171 C{True} for first chance exceptions, C{False} for second chance.
172
173 C{None} if unapplicable or unable to retrieve.
174
175 @type isOurBreakpoint: bool
176 @ivar isOurBreakpoint:
177 C{True} for breakpoints defined by the L{Debug} class,
178 C{False} otherwise.
179
180 C{None} if unapplicable.
181
182 @type isSystemBreakpoint: bool
183 @ivar isSystemBreakpoint:
184 C{True} for known system-defined breakpoints,
185 C{False} otherwise.
186
187 C{None} if unapplicable.
188
189 @type modFileName: None or str
190 @ivar modFileName: File name of module where the program counter points to.
191
192 C{None} or invalid if unapplicable or unable to retrieve.
193
194 @type lpBaseOfDll: None or int
195 @ivar lpBaseOfDll: Base of module where the program counter points to.
196
197 C{None} if unapplicable or unable to retrieve.
198
199 @type stackTrace: None or tuple of tuple( int, int, str )
200 @ivar stackTrace:
201 Stack trace of the current thread as a tuple of
202 ( frame pointer, return address, module filename ).
203
204 C{None} or empty if unapplicable or unable to retrieve.
205
206 @type stackTracePretty: None or tuple of tuple( int, str )
207 @ivar stackTracePretty:
208 Stack trace of the current thread as a tuple of
209 ( frame pointer, return location ).
210
211 C{None} or empty if unapplicable or unable to retrieve.
212
213 @type stackTracePC: None or tuple( int... )
214 @ivar stackTracePC: Tuple of return addresses in the stack trace.
215
216 C{None} or empty if unapplicable or unable to retrieve.
217
218 @type stackTraceLabels: None or tuple( str... )
219 @ivar stackTraceLabels:
220 Tuple of labels pointing to the return addresses in the stack trace.
221
222 C{None} or empty if unapplicable or unable to retrieve.
223
224 @type stackRange: tuple( int, int )
225 @ivar stackRange:
226 Stack beginning and end pointers, in memory addresses order.
227
228 C{None} if unapplicable or unable to retrieve.
229
230 @type stackFrame: None or str
231 @ivar stackFrame: Data pointed to by the stack pointer.
232
233 C{None} or empty if unapplicable or unable to retrieve.
234
235 @type stackPeek: None or dict( int S{->} str )
236 @ivar stackPeek: Dictionary mapping stack offsets to the data they point to.
237
238 C{None} or empty if unapplicable or unable to retrieve.
239
240 @type faultCode: None or str
241 @ivar faultCode: Data pointed to by the program counter.
242
243 C{None} or empty if unapplicable or unable to retrieve.
244
245 @type faultMem: None or str
246 @ivar faultMem: Data pointed to by the exception address.
247
248 C{None} or empty if unapplicable or unable to retrieve.
249
250 @type faultPeek: None or dict( intS{->} str )
251 @ivar faultPeek: Dictionary mapping guessed pointers at L{faultMem} to the data they point to.
252
253 C{None} or empty if unapplicable or unable to retrieve.
254
255 @type faultDisasm: None or tuple of tuple( long, int, str, str )
256 @ivar faultDisasm: Dissassembly around the program counter.
257
258 C{None} or empty if unapplicable or unable to retrieve.
259
260 @type memoryMap: None or list of L{win32.MemoryBasicInformation} objects.
261 @ivar memoryMap: Memory snapshot of the program. May contain the actual
262 data from the entire process memory if requested.
263 See L{fetch_extra_data} for more details.
264
265 C{None} or empty if unapplicable or unable to retrieve.
266 """
267
269 """
270 @type event: L{Event}
271 @param event: Event object for crash.
272 """
273
274
275 self.timeStamp = time.time()
276
277
278 self.notes = list()
279
280
281 process = event.get_process()
282 thread = event.get_thread()
283
284
285 self.eventCode = event.get_code()
286 self.eventName = event.get_event_name()
287 self.pid = event.get_pid()
288 self.tid = event.get_tid()
289 self.registers = dict(thread.get_context())
290 self.labelPC = process.get_label_at_address(self.pc)
291
292
293 self.registersPeek = None
294 self.debugString = None
295 self.exceptionCode = None
296 self.exceptionName = None
297 self.exceptionAddress = None
298 self.faultType = None
299 self.faultAddress = None
300 self.faultLabel = None
301 self.firstChance = None
302 self.isOurBreakpoint = None
303 self.isSystemBreakpoint = None
304 self.modFileName = None
305 self.lpBaseOfDll = None
306 self.exceptionLabel = None
307 self.stackLimits = None
308 self.stackTrace = None
309 self.stackTracePC = None
310 self.stackTraceLabels = None
311 self.stackTracePretty = None
312 self.stackRange = None
313 self.stackFrame = None
314 self.stackPeek = None
315 self.faultCode = None
316 self.faultMem = None
317 self.faultPeek = None
318 self.faultDisasm = None
319 self.memoryMap = None
320
321
322 if self.eventCode == win32.OUTPUT_DEBUG_STRING_EVENT:
323 self.debugString = event.get_debug_string()
324
325
326
327
328 elif self.eventCode in (win32.CREATE_PROCESS_DEBUG_EVENT,
329 win32.EXIT_PROCESS_DEBUG_EVENT,
330 win32.LOAD_DLL_DEBUG_EVENT,
331 win32.UNLOAD_DLL_DEBUG_EVENT):
332 aModule = event.get_module()
333 self.modFileName = event.get_filename()
334 if not self.modFileName:
335 self.modFileName = aModule.get_filename()
336 self.lpBaseOfDll = event.get_module_base()
337 if not self.lpBaseOfDll:
338 self.lpBaseOfDll = aModule.get_base()
339
340
341
342 elif self.eventCode == win32.EXCEPTION_DEBUG_EVENT:
343
344
345 self.exceptionCode = event.get_exception_code()
346 self.exceptionName = event.get_exception_name()
347 self.exceptionDescription = event.get_exception_description()
348 self.exceptionAddress = event.get_exception_address()
349 self.firstChance = event.is_first_chance()
350 self.exceptionLabel = process.get_label_at_address(
351 self.exceptionAddress)
352 if self.exceptionCode in (win32.EXCEPTION_ACCESS_VIOLATION,
353 win32.EXCEPTION_GUARD_PAGE,
354 win32.EXCEPTION_IN_PAGE_ERROR):
355 self.faultType = event.get_fault_type()
356 self.faultAddress = event.get_fault_address()
357 self.faultLabel = process.get_label_at_address(
358 self.faultAddress)
359 elif self.exceptionCode in (win32.EXCEPTION_BREAKPOINT,
360 win32.EXCEPTION_SINGLE_STEP):
361 self.isOurBreakpoint = hasattr(event, 'breakpoint') \
362 and event.breakpoint
363 self.isSystemBreakpoint = \
364 process.is_system_defined_breakpoint(self.exceptionAddress)
365
366
367 try:
368 self.stackTracePretty = thread.get_stack_trace_with_labels()
369 except Exception, e:
370 pass
371 try:
372 self.stackTrace = thread.get_stack_trace()
373 stackTracePC = [ ra for (_,ra,_) in self.stackTrace ]
374 self.stackTracePC = tuple(stackTracePC)
375 stackTraceLabels = [ process.get_label_at_address(ra) \
376 for ra in self.stackTracePC ]
377 self.stackTraceLabels = tuple(stackTraceLabels)
378 except Exception, e:
379 pass
380
382 """
383 Fetch extra data from the L{Event} object.
384
385 @note: This is only needed for exceptions. Since this method may take
386 a little longer to run, it's best to call it only after you've
387 determined the crash is interesting and you want to save it.
388
389 @type event: L{Event}
390 @param event: Event object for crash.
391
392 @type takeMemorySnapshot: int
393 @param takeMemorySnapshot:
394 Memory snapshot behavior:
395 - C{0} to take no memory information (default).
396 - C{1} to take only the memory map.
397 - C{2} to take a full memory snapshot.
398 """
399
400
401 process = event.get_process()
402 thread = event.get_thread()
403
404
405 if self.eventCode == win32.EXCEPTION_DEBUG_EVENT:
406
407
408 self.registersPeek = thread.peek_pointers_in_registers()
409
410
411 aModule = process.get_module_at_address(self.pc)
412 if aModule is not None:
413 self.modFileName = aModule.get_filename()
414 self.lpBaseOfDll = aModule.get_base()
415
416
417 try:
418 self.stackRange = thread.get_stack_range()
419 except Exception, e:
420 pass
421 try:
422 self.stackFrame = thread.get_stack_frame()
423 stackFrame = self.stackFrame
424 except Exception, e:
425 self.stackFrame = thread.peek_stack_data()
426 stackFrame = self.stackFrame[:64]
427 if stackFrame:
428 self.stackPeek = process.peek_pointers_in_data(stackFrame)
429
430
431 self.faultCode = thread.peek_code_bytes()
432 try:
433 self.faultDisasm = thread.disassemble_around_pc(32)
434 except Exception, e:
435 pass
436
437
438
439 if self.pc != self.exceptionAddress and self.exceptionCode in (
440 win32.EXCEPTION_ACCESS_VIOLATION,
441 win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
442 win32.EXCEPTION_DATATYPE_MISALIGNMENT,
443 win32.EXCEPTION_IN_PAGE_ERROR,
444 win32.EXCEPTION_STACK_OVERFLOW,
445 win32.EXCEPTION_GUARD_PAGE,
446 ):
447 self.faultMem = process.peek(self.exceptionAddress, 64)
448 if self.faultMem:
449 self.faultPeek = process.peek_pointers_in_data(self.faultMem)
450
451
452
453 if takeMemorySnapshot == 2:
454 self.memoryMap = process.take_memory_snapshot()
455 elif takeMemorySnapshot == 1:
456 self.memoryMap = process.get_memory_map()
457 mappedFilenames = process.get_mapped_filenames(self.memoryMap)
458 for mbi in self.memoryMap:
459 mbi.filename = mappedFilenames.get(mbi.BaseAddress, None)
460 mbi.content = None
461
462 @property
464 """
465 Value of the program counter register.
466
467 @rtype: int
468 """
469 try:
470 return self.registers['Eip']
471 except KeyError:
472 return self.registers['Rip']
473
474 @property
476 """
477 Value of the stack pointer register.
478
479 @rtype: int
480 """
481 try:
482 return self.registers['Esp']
483 except KeyError:
484 return self.registers['Rsp']
485
486 @property
488 """
489 Value of the frame pointer register.
490
491 @rtype: int
492 """
493 try:
494 return self.registers['Ebp']
495 except KeyError:
496 return self.registers['Rbp']
497
500
502 """
503 Generates an approximately unique key for the Crash object.
504
505 This key can be used as an heuristic to determine if two crashes were
506 caused by the same software error. Ideally it should be treated as an
507 opaque object.
508
509 @see: U{http://apps.sourceforge.net/trac/winappdbg/wiki/CrashKey}
510
511 @rtype: (opaque)
512 @return: Crash unique key.
513 """
514 if self.labelPC:
515 eip = self.labelPC
516 else:
517 eip = self.pc
518 if self.stackTraceLabels:
519 trace = self.stackTraceLabels
520 else:
521 trace = self.stackTracePC
522 return (
523 self.eventCode,
524 self.exceptionCode,
525 eip,
526 trace,
527 self.debugString,
528 )
529
531 """
532 Guess how likely is it that the bug causing the crash can be leveraged
533 into an exploitable vulnerability.
534
535 @note: Don't take this as an equivalent of a real exploitability
536 analysis, that can only be done by a human being! This is only
537 a guideline, useful for example to sort crashes - placing the most
538 interesting ones at the top.
539
540 @see: The heuristics are similar to those of the B{!exploitable}
541 extension for I{WinDBG}, which can be downloaded from here:
542
543 U{http://www.codeplex.com/msecdbg}
544
545 @rtype: tuple( str, str )
546 @return: The first element of the tuple is the result of the analysis,
547 being one of the following:
548
549 - Not an exception
550 - Not exploitable
551 - Not likely exploitable
552 - Unknown
553 - Probably exploitable
554 - Exploitable
555
556 The second element of the tuple is a code to identify the matched
557 heuristic rule.
558
559 The second element of the tuple is a description string of the
560 reason behind the result.
561 """
562
563
564
565 if self.eventCode != win32.EXCEPTION_DEBUG_EVENT:
566 return ("Not an exception", "NotAnException", "The event is not an exception.")
567
568 if self.stackRange and self.pc is not None and self.stackRange[0] <= self.pc < self.stackRange[1]:
569 return ("Exploitable", "StackCodeExecution", "Code execution from the stack is considered exploitable.")
570
571
572 if self.stackRange and self.sp is not None and not (self.stackRange[0] <= self.sp < self.stackRange[1]):
573 return ("Exploitable", "StackPointerCorruption", "Stack pointer corruption is considered exploitable.")
574
575
576
577 if self.exceptionCode == win32.EXCEPTION_ILLEGAL_INSTRUCTION:
578 return ("Exploitable", "IllegalInstruction", "An illegal instruction exception indicates that the attacker controls execution flow.")
579
580 if self.exceptionCode == win32.EXCEPTION_PRIV_INSTRUCTION:
581 return ("Exploitable", "PrivilegedInstruction", "A privileged instruction exception indicates that the attacker controls execution flow.")
582
583 if self.exceptionCode == win32.EXCEPTION_GUARD_PAGE:
584 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).")
585
586 if self.exceptionCode == win32.STATUS_STACK_BUFFER_OVERRUN:
587 return ("Exploitable", "GSViolation", "An overrun of a protected stack buffer has been detected. This is considered exploitable, and must be fixed.")
588
589 if self.exceptionCode == win32.STATUS_HEAP_CORRUPTION:
590 return ("Exploitable", "HeapCorruption", "Heap Corruption has been detected. This is considered exploitable, and must be fixed.")
591
592 if self.exceptionCode == win32.EXCEPTION_ACCESS_VIOLATION:
593 nearNull = self.faultAddress is None or MemoryAddresses.align_address_to_page_start(self.faultAddress) == win32.NULL
594 controlFlow = self.__is_control_flow()
595 blockDataMove = self.__is_block_data_move()
596 if self.faultType == win32.EXCEPTION_EXECUTE_FAULT:
597 if nearNull:
598 return ("Probably exploitable", "DEPViolation", "User mode DEP access violations are probably exploitable if near NULL.")
599 else:
600 return ("Exploitable", "DEPViolation", "User mode DEP access violations are exploitable.")
601 elif self.faultType == win32.EXCEPTION_WRITE_FAULT:
602 if nearNull:
603 return ("Probably exploitable", "WriteAV", "User mode write access violations that are near NULL are probably exploitable.")
604 else:
605 return ("Exploitable", "WriteAV", "User mode write access violations that are not near NULL are exploitable.")
606 elif self.faultType == win32.EXCEPTION_READ_FAULT:
607 if self.faultAddress == self.pc:
608 if nearNull:
609 return ("Probably exploitable", "ReadAVonIP", "Access violations at the instruction pointer are probably exploitable if near NULL.")
610 else:
611 return ("Exploitable", "ReadAVonIP", "Access violations at the instruction pointer are exploitable if not near NULL.")
612 if controlFlow:
613 if nearNull:
614 return ("Probably exploitable", "ReadAVonControlFlow", "Access violations near null in control flow instructions are considered probably exploitable.")
615 else:
616 return ("Exploitable", "ReadAVonControlFlow", "Access violations not near null in control flow instructions are considered exploitable.")
617 if blockDataMove:
618 return ("Probably exploitable", "ReadAVonBlockMove", "This is a read access violation in a block data move, and is therefore classified as probably exploitable.")
619
620
621
622
623
624
625
626
627 result = ("Unknown", "Unknown", "Exploitability unknown.")
628
629 if self.exceptionCode == win32.EXCEPTION_ACCESS_VIOLATION:
630 if self.faultType == win32.EXCEPTION_READ_FAULT:
631 if nearNull:
632 result = ("Not likely exploitable", "ReadAVNearNull", "This is a user mode read access violation near null, and is probably not exploitable.")
633
634 elif self.exceptionCode == win32.EXCEPTION_INT_DIVIDE_BY_ZERO:
635 result = ("Not likely exploitable", "DivideByZero", "This is an integer divide by zero, and is probably not exploitable.")
636
637 elif self.exceptionCode == win32.EXCEPTION_FLT_DIVIDE_BY_ZERO:
638 result = ("Not likely exploitable", "DivideByZero", "This is a floating point divide by zero, and is probably not exploitable.")
639
640 elif self.exceptionCode in (win32.EXCEPTION_BREAKPOINT, win32.STATUS_WX86_BREAKPOINT):
641 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.")
642
643
644
645
646
647
648
649
650
651 return result
652
654 jump_instructions = (
655 'jmp', 'jecxz', 'jcxz',
656 'ja', 'jnbe', 'jae', 'jnb', 'jb', 'jnae', 'jbe', 'jna', 'jc', 'je',
657 'jz', 'jnc', 'jne', 'jnz', 'jnp', 'jpo', 'jp', 'jpe', 'jg', 'jnle',
658 'jge', 'jnl', 'jl', 'jnge', 'jle', 'jng', 'jno', 'jns', 'jo', 'js'
659 )
660 call_instructions = ( 'call', 'ret', 'retn' )
661 loop_instructions = ( 'loop', 'loopz', 'loopnz', 'loope', 'loopne' )
662 control_flow_instructions = call_instructions + loop_instructions + \
663 jump_instructions
664 isControlFlow = False
665 instruction = None
666 if self.pc is not None and self.faultDisasm:
667 for disasm in self.faultDisasm:
668 if disasm[0] == self.pc:
669 instruction = disasm[2].lower().strip()
670 break
671 if instruction:
672 for x in control_flow_instructions:
673 if x in instruction:
674 isControlFlow = True
675 break
676 return isControlFlow
677
679 block_data_move_instructions = ('movs', 'stos', 'lods')
680 isBlockDataMove = False
681 instruction = None
682 if self.pc is not None and self.faultDisasm:
683 for disasm in self.faultDisasm:
684 if disasm[0] == self.pc:
685 instruction = disasm[2].lower().strip()
686 break
687 if instruction:
688 for x in block_data_move_instructions:
689 if x in instruction:
690 isBlockDataMove = True
691 break
692 return isBlockDataMove
693
695 """
696 @rtype: str
697 @return: Short description of the event.
698 """
699 if self.exceptionCode is not None:
700 if self.exceptionCode == win32.EXCEPTION_BREAKPOINT:
701 if self.isOurBreakpoint:
702 what = "Breakpoint hit"
703 elif self.isSystemBreakpoint:
704 what = "System breakpoint hit"
705 else:
706 what = "Assertion failed"
707 elif self.exceptionDescription:
708 what = self.exceptionDescription
709 elif self.exceptionName:
710 what = self.exceptionName
711 else:
712 what = "Exception %s" % HexDump.integer(self.exceptionCode)
713 if self.firstChance:
714 chance = 'first'
715 else:
716 chance = 'second'
717 if self.exceptionLabel:
718 where = self.exceptionLabel
719 elif self.exceptionAddress:
720 where = HexDump.address(self.exceptionAddress)
721 elif self.labelPC:
722 where = self.labelPC
723 else:
724 where = HexDump.address(self.pc)
725 msg = "%s (%s chance) at %s" % (what, chance, where)
726 elif self.debugString is not None:
727 if self.labelPC:
728 where = self.labelPC
729 else:
730 where = HexDump.address(self.pc)
731 msg = "Debug string from %s: %r" % (where, self.debugString)
732 else:
733 if self.labelPC:
734 where = self.labelPC
735 else:
736 where = HexDump.address(self.pc)
737 msg = "%s (%s) at %s" % (
738 self.eventName,
739 HexDump.integer(self.eventCode),
740 where
741 )
742 return msg
743
745 """
746 @type bShowNotes: bool
747 @param bShowNotes: C{True} to show the user notes, C{False} otherwise.
748
749 @rtype: str
750 @return: Long description of the event.
751 """
752 msg = self.briefReport()
753 msg += '\n'
754
755 if win32.sizeof(win32.LPVOID) == 4:
756 width = 16
757 else:
758 width = 8
759
760 if self.eventCode == win32.EXCEPTION_DEBUG_EVENT:
761 (exploitability, expcode, expdescription) = self.isExploitable()
762 msg += '\nSecurity risk level: %s\n' % exploitability
763 msg += ' %s\n' % expdescription
764
765 if bShowNotes and self.notes:
766 msg += '\nNotes:\n'
767 msg += self.notesReport()
768
769 if not self.labelPC:
770 base = HexDump.address(self.lpBaseOfDll)
771 if self.modFileName:
772 fn = PathOperations.pathname_to_filename(self.modFileName)
773 msg += '\nRunning in %s (%s)\n' % (fn, base)
774 else:
775 msg += '\nRunning in module at %s\n' % base
776
777 if self.registers:
778 msg += '\nRegisters:\n'
779 msg += CrashDump.dump_registers(self.registers)
780 if self.registersPeek:
781 msg += '\n'
782 msg += CrashDump.dump_registers_peek(self.registers,
783 self.registersPeek,
784 width = width)
785
786 if self.faultDisasm:
787 msg += '\nCode disassembly:\n'
788 msg += CrashDump.dump_code(self.faultDisasm, self.pc)
789
790 if self.stackTrace:
791 msg += '\nStack trace:\n'
792 if self.stackTracePretty:
793 msg += CrashDump.dump_stack_trace_with_labels(
794 self.stackTracePretty)
795 else:
796 msg += CrashDump.dump_stack_trace(self.stackTrace)
797
798 if self.stackFrame:
799 if self.stackPeek:
800 msg += '\nStack pointers:\n'
801 msg += CrashDump.dump_stack_peek(self.stackPeek, width = width)
802 msg += '\nStack dump:\n'
803 msg += HexDump.hexblock(self.stackFrame, self.sp, width = width)
804
805 if self.faultCode and not self.modFileName:
806 msg += '\nCode dump:\n'
807 msg += HexDump.hexblock(self.faultCode, self.pc, width = width)
808
809 if self.faultMem:
810 if self.faultPeek:
811 msg += '\nException address pointers:\n'
812 msg += CrashDump.dump_data_peek(self.faultPeek,
813 self.exceptionAddress,
814 width = width)
815 msg += '\nException address dump:\n'
816 msg += HexDump.hexblock(self.faultMem, self.exceptionAddress,
817 width = width)
818
819 if self.memoryMap:
820 msg += '\nMemory map:\n'
821 mappedFileNames = dict()
822 for mbi in self.memoryMap:
823 if hasattr(mbi, 'filename') and mbi.filename:
824 mappedFileNames[mbi.BaseAddress] = mbi.filename
825 msg += CrashDump.dump_memory_map(self.memoryMap, mappedFileNames)
826
827 if not msg.endswith('\n\n'):
828 if not msg.endswith('\n'):
829 msg += '\n'
830 msg += '\n'
831 return msg
832
834 """
835 @rtype: str
836 @return: All notes, merged and formatted for a report.
837 """
838 msg = ''
839 if self.notes:
840 for n in self.notes:
841 n = n.strip('\n')
842 if '\n' in n:
843 n = n.strip('\n')
844 msg += ' * %s\n' % n.pop(0)
845 for x in n:
846 msg += ' %s\n' % x
847 else:
848 msg += ' * %s\n' % n
849 return msg
850
852 """
853 Add a note to the crash event.
854
855 @type msg: str
856 @param msg: Note text.
857 """
858 self.notes.append(msg)
859
861 """
862 Clear the notes of this crash event.
863 """
864 self.notes = list()
865
867 """
868 Get the list of notes of this crash event.
869
870 @rtype: list( str )
871 @return: List of notes.
872 """
873 return self.notes
874
876 """
877 Iterate the notes of this crash event.
878
879 @rtype: listiterator
880 @return: Iterator of the list of notes.
881 """
882 return self.notes.__iter__()
883
885 """
886 @rtype: bool
887 @return: C{True} if there are notes for this crash event.
888 """
889 return bool( self.notes )
890
894 """
895 Manages a database of persistent Crash objects, trying to avoid duplicates.
896
897 Uses a DBM database file for persistency.
898
899 @see: L{Crash.key}
900 """
901
902
903
904
905
906
908 """
909 Iterator of Crash objects. Returned by L{CrashContainer.__iter__}.
910 """
911
913 """
914 @type container: L{CrashContainer}
915 @param container: Crash set to iterate.
916 """
917
918
919
920
921
922
923
924 self.__container = container
925 self.__keys_iter = container.iterkeys()
926
928 """
929 @rtype: L{Crash}
930 @return: A B{copy} of a Crash object in the L{CrashContainer}.
931 @raise StopIteration: No more items left.
932 """
933 key = self.__keys_iter.next()
934 return self.__container.get(key)
935
936 - def __init__(self, filename = None, allowRepeatedKeys = False):
937 """
938 @type filename: str
939 @param filename: (Optional) File name for crash database.
940 If no filename is specified, the container is volatile.
941
942 Volatile containers are stored only in memory and
943 destroyed when they go out of scope.
944 """
945
946
947
948
949
950
951
952 if allowRepeatedKeys:
953 raise NotImplementedError
954 self.__filename = filename
955 if filename:
956 global anydbm
957 if not anydbm:
958 import anydbm
959 self.__db = anydbm.open(filename, 'c')
960 self.__keys = dict([ (self.__unmarshall_key(mk), mk) \
961 for mk in self.__db.keys() ])
962 else:
963 self.__db = dict()
964 self.__keys = dict()
965
967 try:
968 if self.__filename:
969 self.__db.close()
970 except:
971 pass
972
974 """
975 @type crash: L{Crash}
976 @param crash: Crash object.
977
978 @rtype: bool
979 @return: C{True} if the Crash object is in the container.
980 """
981 return self.__keys.has_key( crash.key() )
982
984 """
985 @see: L{itervalues}
986 @rtype: iterator
987 @return: Iterator of the contained L{Crash} objects.
988 """
989 return self.itervalues()
990
992 """
993 @rtype: int
994 @return: Count of L{Crash} elements in the container.
995 """
996 return len(self.__keys)
997
999 """
1000 @rtype: bool
1001 @return: C{False} if the container is empty.
1002 """
1003 return bool(self.__keys)
1004
1006 """
1007 @type key: L{Crash} unique key.
1008 @param key: Key of the crash to get.
1009
1010 @rtype: bool
1011 @return: C{True} if a matching L{Crash} object is in the container.
1012 """
1013 return key in self.__keys
1014
1016 """
1017 @rtype: iterator
1018 @return: Iterator of the contained L{Crash} object keys.
1019
1020 @see: L{get}
1021 @warning: A B{copy} of each object is returned,
1022 so any changes made to them will be lost.
1023
1024 To preserve changes do the following:
1025 1. Keep a reference to the object.
1026 2. Delete the object from the set.
1027 3. Modify the object and add it again.
1028 """
1029 return self.__keys.iterkeys()
1030
1032 """
1033 @rtype: iterator
1034 @return: Iterator of the contained L{Crash} objects.
1035
1036 @warning: A B{copy} of each object is returned,
1037 so any changes made to them will be lost.
1038
1039 To preserve changes do the following:
1040 1. Keep a reference to the object.
1041 2. Delete the object from the set.
1042 3. Modify the object and add it again.
1043 """
1044 return self.__CrashContainerIterator(self)
1045
1046 - def add(self, crash):
1047 """
1048 Adds a new crash to the container.
1049 If the crash appears to be already known, it's ignored.
1050
1051 @see: L{Crash.key}
1052
1053 @type crash: L{Crash}
1054 @param crash: Crash object to add.
1055 """
1056 if crash not in self:
1057 key = crash.key()
1058 skey = self.__marshall_key(key)
1059 data = self.__marshall_value(crash)
1060 self.__db[skey] = data
1061 self.__keys[key] = skey
1062
1064 """
1065 Removes a crash from the container.
1066
1067 @type crash: L{Crash}
1068 @param crash: Crash object to remove.
1069 """
1070 key = crash.key()
1071 skey = self.__keys[key]
1072 del self.__db[skey]
1073 del self.__keys[key]
1074
1075 - def get(self, key):
1076 """
1077 Retrieves a crash from the container.
1078
1079 @type key: L{Crash} unique key.
1080 @param key: Key of the crash to get.
1081
1082 @rtype: L{Crash} object.
1083 @return: Crash matching the given key.
1084
1085 @see: L{iterkeys}
1086 @warning: A B{copy} of each object is returned,
1087 so any changes made to them will be lost.
1088
1089 To preserve changes do the following:
1090 1. Keep a reference to the object.
1091 2. Delete the object from the set.
1092 3. Modify the object and add it again.
1093 """
1094 skey = self.__keys[key]
1095 data = self.__db[skey]
1096 crash = self.__unmarshall_value(data)
1097 return crash
1098
1100 """
1101 Marshalls a Crash key to be used in the database.
1102
1103 @type key: (opaque object)
1104 @param key: Key to convert.
1105
1106 @rtype: str
1107 @return: Converted key.
1108 """
1109 if key in self.__keys:
1110 return self.__keys[key]
1111 key = pickle.dumps(key, protocol = pickle.HIGHEST_PROTOCOL)
1112 key = optimize(key)
1113 return key
1114
1116 """
1117 Unmarshalls a Crash key read from the database.
1118
1119 @type key: str
1120 @param key: Key to convert.
1121
1122 @rtype: (opaque object)
1123 @return: Converted key.
1124 """
1125 return pickle.loads(key)
1126
1128 """
1129 Marshalls a Crash object to be used in the database.
1130
1131 @type value: L{Crash}
1132 @param value: Object to convert.
1133
1134 @rtype: str
1135 @return: Converted object.
1136 """
1137 value = pickle.dumps(value, protocol = pickle.HIGHEST_PROTOCOL)
1138 value = optimize(value)
1139 return zlib.compress(value, zlib.Z_BEST_COMPRESSION)
1140
1142 """
1143 Unmarshalls a Crash object read from the database.
1144
1145 @type value: str
1146 @param value: Object to convert.
1147
1148 @rtype: L{Crash}
1149 @return: Converted object.
1150 """
1151 value = zlib.decompress(value)
1152 return pickle.loads(value)
1153
1157 """
1158 Manages a database of persistent Crash objects, trying to avoid duplicates
1159 only when requested.
1160
1161 Uses a SQLite database file for persistency.
1162
1163 @see: L{Crash.key}
1164 """
1165
1166 __table_definition = (
1167 "CREATE TABLE Crashes ("
1168
1169
1170 "id INTEGER PRIMARY KEY,"
1171
1172
1173
1174 "timeStamp TIMESTAMP,"
1175 "key BLOB,"
1176 "pickle BLOB,"
1177
1178
1179 "isExploitable TEXT,"
1180 "isExploitableRule TEXT,"
1181
1182
1183 "eventCode INTEGER,"
1184 "pid INTEGER,"
1185 "tid INTEGER,"
1186 "pc INTEGER,"
1187 "sp INTEGER,"
1188 "fp INTEGER,"
1189 "labelPC TEXT,"
1190
1191
1192 "exceptionCode INTEGER,"
1193 "exceptionAddress INTEGER,"
1194 "exceptionLabel TEXT,"
1195 "firstChance INTEGER,"
1196 "faultType INTEGER,"
1197 "faultAddress INTEGER,"
1198 "faultLabel TEXT,"
1199 "faultDisasm TEXT,"
1200 "stackTrace TEXT,"
1201
1202
1203 "notes TEXT"
1204 ")"
1205 )
1206 __insert_row = (
1207 "INSERT INTO Crashes VALUES (null%s)"
1208 % (',?' * (len(__table_definition.split(',')) - 1))
1209 )
1210
1211 __select_pickle = "SELECT pickle FROM Crashes"
1212 __select_key = "SELECT key FROM Crashes"
1213 __select_count = "SELECT COUNT(*) FROM Crashes"
1214
1215 __memory_table_definition = (
1216 "CREATE TABLE Memory ("
1217 "id INTEGER PRIMARY KEY,"
1218 "Crash INTEGER,"
1219 "Address INTEGER,"
1220 "Size INTEGER,"
1221 "State TEXT,"
1222 "Access TEXT,"
1223 "Type TEXT,"
1224 "File TEXT,"
1225 "Data BLOB"
1226 ")"
1227 )
1228 __memory_insert_row = (
1229 "INSERT INTO Memory VALUES "
1230 "(null, ?, ?, ?, ?, ?, ?, ?, ?)"
1231 )
1232
1234 timeStamp = time.asctime( time.gmtime( crash.timeStamp ) )
1235 key = self.__marshall_key(crash.key())
1236 pickle = self.__marshall_value(crash)
1237 isExploitable, isExploitableRule, _ = crash.isExploitable()
1238 eventCode = crash.eventCode
1239 pid = crash.pid
1240 tid = crash.tid
1241 pc = crash.pc
1242 sp = crash.sp
1243 fp = crash.fp
1244 labelPC = crash.labelPC
1245 exceptionCode = crash.exceptionCode
1246 exceptionAddress = crash.exceptionAddress
1247 exceptionLabel = crash.exceptionLabel
1248 firstChance = crash.firstChance
1249 faultType = crash.faultType
1250 faultAddress = crash.faultAddress
1251 faultLabel = crash.faultLabel
1252 faultDisasm = CrashDump.dump_code(crash.faultDisasm, crash.pc)
1253 stackTrace = CrashDump.dump_stack_trace_with_labels(
1254 crash.stackTracePretty)
1255 notes = crash.notesReport()
1256 return (
1257 timeStamp,
1258 key,
1259 pickle,
1260 isExploitable,
1261 isExploitableRule,
1262 eventCode,
1263 pid,
1264 tid,
1265 pc,
1266 sp,
1267 fp,
1268 labelPC,
1269 exceptionCode,
1270 exceptionAddress,
1271 exceptionLabel,
1272 firstChance,
1273 faultType,
1274 faultAddress,
1275 faultLabel,
1276 faultDisasm,
1277 stackTrace,
1278 notes,
1279 )
1280
1360
1361 - def __init__(self, location = None, allowRepeatedKeys = True):
1362 """
1363 @type location: str
1364 @param location: (Optional) Location of the crash database.
1365 If no location is specified, the container is volatile.
1366
1367 If the location is a filename, it's an SQLite database file.
1368
1369 Volatile containers are stored only in memory and
1370 destroyed when they go out of scope.
1371
1372 @type allowRepeatedKeys: bool
1373 @param allowRepeatedKeys:
1374 If C{True} all L{Crash} objects are stored.
1375
1376 If C{False} any L{Crash} object with the same key as a
1377 previously existing object will be ignored.
1378 """
1379
1380
1381 global sqlite
1382 if sqlite is None:
1383 try:
1384 import sqlite3 as sqlite
1385 except ImportError:
1386 from pysqlite2 import dbapi2 as sqlite
1387
1388
1389 if not location:
1390 location = ':memory:'
1391 self.__location = location
1392
1393
1394 self.__db = sqlite.connect(self.__location)
1395 self.__cursor = self.__db.cursor()
1396
1397
1398 try:
1399 self.__cursor.execute(self.__table_definition)
1400 self.__cursor.execute(self.__memory_table_definition)
1401 self.__db.commit()
1402 except Exception:
1403 pass
1404
1405
1406 self.__allowRepeatedKeys = allowRepeatedKeys
1407 self.__keys = dict()
1408 self.__cursor.execute(self.__select_key)
1409 for row in self.__cursor:
1410 marshalled_key = row[0]
1411 unmarshalled_key = self.__unmarshall_key(marshalled_key)
1412 self.__keys[unmarshalled_key] = marshalled_key
1413
1414
1415 self.__cursor.execute(self.__select_count)
1416 count = 0
1417 for row in self.__cursor:
1418 count = long(row[0])
1419 self.__count = count
1420
1421 - def add(self, crash):
1422 """
1423 Adds a new crash to the container.
1424
1425 @note:
1426 When the C{allowRepeatedKeys} parameter of the constructor
1427 is set to C{False}, duplicated crashes are ignored.
1428
1429 @see: L{Crash.key}
1430
1431 @type crash: L{Crash}
1432 @param crash: Crash object to add.
1433 """
1434
1435
1436
1437 key = crash.key()
1438 if self.__allowRepeatedKeys or key not in self.__keys:
1439 self.__keys[key] = self.__marshall_key(key)
1440
1441
1442 self.__cursor.execute(self.__insert_row,
1443 self.__get_row_values(crash))
1444
1445
1446 if hasattr(crash, 'memoryMap') and crash.memoryMap:
1447 cid = self.__cursor.lastrowid
1448 for mbi in crash.memoryMap:
1449 self.__cursor.execute(self.__memory_insert_row,
1450 self.__memory_get_row_values(cid, mbi))
1451
1452
1453 self.__db.commit()
1454
1455
1456 self.__count += 1
1457
1468
1470 """
1471 @type crash: L{Crash}
1472 @param crash: Crash object.
1473
1474 @rtype: bool
1475 @return: C{True} if the Crash object is in the container.
1476 """
1477 return self.__keys.has_key( crash.key() )
1478
1480 """
1481 @rtype: int
1482 @return: Count of L{Crash} elements in the container.
1483 """
1484 return self.__count
1485
1487 """
1488 @rtype: bool
1489 @return: C{False} if the container is empty.
1490 """
1491
1492
1493 return bool(self.__keys)
1494
1496 """
1497 Marshalls a Crash key to be used in the database.
1498
1499 @type key: (opaque object)
1500 @param key: Key to convert.
1501
1502 @rtype: BLOB
1503 @return: Converted key.
1504 """
1505 if key in self.__keys:
1506 return self.__keys[key]
1507 key = pickle.dumps(key, protocol = pickle.HIGHEST_PROTOCOL)
1508 key = optimize(key)
1509 key = sqlite.Binary(key)
1510 return key
1511
1513 """
1514 Unmarshalls a Crash key read from the database.
1515
1516 @type key: str
1517 @param key: Key to convert.
1518
1519 @rtype: (opaque object)
1520 @return: Converted key.
1521 """
1522 key = str(key)
1523 key = pickle.loads(key)
1524 return key
1525
1527 """
1528 Marshalls a Crash object to be used in the database.
1529 The C{memoryMap} member is B{NOT} stored here.
1530
1531 @type value: L{Crash}
1532 @param value: Object to convert.
1533
1534 @rtype: BLOB
1535 @return: Converted object.
1536 """
1537 crash = value
1538 memoryMap = crash.memoryMap
1539 try:
1540 crash.memoryMap = None
1541 value = pickle.dumps(value, protocol = pickle.HIGHEST_PROTOCOL)
1542 finally:
1543 crash.memoryMap = memoryMap
1544 del memoryMap
1545 del crash
1546 value = optimize(value)
1547 value = zlib.compress(value, zlib.Z_BEST_COMPRESSION)
1548 value = sqlite.Binary(value)
1549 return value
1550
1552 """
1553 Unmarshalls a Crash object read from the database.
1554
1555 @type value: str
1556 @param value: Object to convert.
1557
1558 @rtype: L{Crash}
1559 @return: Converted object.
1560 """
1561 value = str(value)
1562 value = zlib.decompress(value)
1563 value = pickle.loads(value)
1564 return value
1565
1569 """
1570 Manages a database of volatile Crash objects,
1571 trying to avoid duplicates if requested.
1572
1573 @see: L{Crash.key}
1574 """
1575
1576 - def __init__(self, allowRepeatedKeys = True):
1577 """
1578 Volatile containers are stored only in memory and
1579 destroyed when they go out of scope.
1580
1581 @type allowRepeatedKeys: bool
1582 @param allowRepeatedKeys:
1583 If C{True} all L{Crash} objects are stored.
1584
1585 If C{False} any L{Crash} object with the same key as a
1586 previously existing object will be ignored.
1587 """
1588 super(VolatileCrashContainer, self).__init__()
1589 self.__allowRepeatedKeys = allowRepeatedKeys
1590 self.__dict = dict()
1591 self.__set = set()
1592
1594 """
1595 @type crash: L{Crash}
1596 @param crash: Crash object.
1597
1598 @rtype: bool
1599 @return: C{True} if the Crash object is in the container.
1600 """
1601 return self.__dict.has_key( crash.key() )
1602
1604 """
1605 @see: L{itervalues}
1606 @rtype: iterator
1607 @return: Iterator of the contained L{Crash} objects.
1608 """
1609 return self.itervalues()
1610
1612 """
1613 @rtype: int
1614 @return: Count of L{Crash} elements in the container.
1615 """
1616 return len(self.__set)
1617
1619 """
1620 @rtype: bool
1621 @return: C{False} if the container is empty.
1622 """
1623 return bool(len(self))
1624
1625 - def add(self, crash):
1626 """
1627 Adds a new crash to the container.
1628
1629 @note:
1630 When the C{allowRepeatedKeys} parameter of the constructor
1631 is set to C{False}, duplicated crashes are ignored.
1632
1633 @see: L{Crash.key}
1634
1635 @type crash: L{Crash}
1636 @param crash: Crash object to add.
1637 """
1638 key = crash.key()
1639 if self.__allowRepeatedKeys or key not in self.__dict:
1640 self.__dict[key] = crash
1641 self.__set.add(crash)
1642
1644 """
1645 @type key: L{Crash} unique key.
1646 @param key: Key of the crash to get.
1647
1648 @rtype: bool
1649 @return: C{True} if a matching L{Crash} object is in the container.
1650 """
1651 return self.__dict.has_key(key)
1652
1654 """
1655 @rtype: iterator
1656 @return: Iterator of the contained L{Crash} object keys.
1657
1658 @see: L{get}
1659 @warning: A B{copy} of each object is returned,
1660 so any changes made to them will be lost.
1661
1662 To preserve changes do the following:
1663 1. Keep a reference to the object.
1664 2. Delete the object from the set.
1665 3. Modify the object and add it again.
1666 """
1667 return self.__dict.iterkeys()
1668
1670 """
1671 @rtype: iterator
1672 @return: Iterator of the contained L{Crash} objects.
1673
1674 @warning: A B{copy} of each object is returned,
1675 so any changes made to them will be lost.
1676
1677 To preserve changes do the following:
1678 1. Keep a reference to the object.
1679 2. Delete the object from the set.
1680 3. Modify the object and add it again.
1681 """
1682 return iter(self.__set)
1683
1687 """
1688 Fakes a database of volatile Crash objects,
1689 trying to mimic part of it's interface.
1690
1691 @see: L{Crash.key}
1692 """
1693
1694 - def __init__(self, allowRepeatedKeys = True):
1695 """
1696 Fake containers don't store L{Crash} objects, but they implement the
1697 interface properly.
1698
1699 @type allowRepeatedKeys: bool
1700 @param allowRepeatedKeys:
1701 If C{True} the len() of the container returns the total number
1702 of L{Crash} objects added.
1703
1704 If C{False} the len() of the container returns the total number
1705 of L{Crash} objects keys added.
1706 """
1707 super(DummyCrashContainer, self).__init__()
1708 self.__keys = set()
1709 self.__count = 0
1710 self.__allowRepeatedKeys = allowRepeatedKeys
1711
1713 """
1714 @type crash: L{Crash}
1715 @param crash: Crash object.
1716
1717 @rtype: bool
1718 @return: C{True} if the Crash object is in the container.
1719 """
1720 return crash.key() in self.__keys
1721
1723 """
1724 @rtype: int
1725 @return: Count of L{Crash} elements in the container.
1726 """
1727 if self.__allowRepeatedKeys:
1728 return self.__count
1729 return len(self.__keys)
1730
1732 """
1733 @rtype: bool
1734 @return: C{False} if the container is empty.
1735 """
1736 return bool(len(self))
1737
1738 - def add(self, crash):
1739 """
1740 Adds a new crash to the container.
1741
1742 @note:
1743 When the C{allowRepeatedKeys} parameter of the constructor
1744 is set to C{False}, duplicated crashes are ignored.
1745
1746 @see: L{Crash.key}
1747
1748 @type crash: L{Crash}
1749 @param crash: Crash object to add.
1750 """
1751 self.__keys.add( crash.key() )
1752 self.__count += 1
1753
1755 """
1756 @type key: L{Crash} unique key.
1757 @param key: Key of the crash to get.
1758
1759 @rtype: bool
1760 @return: C{True} if a matching L{Crash} object is in the container.
1761 """
1762 return self.__dict.has_key(key)
1763
1765 """
1766 @rtype: iterator
1767 @return: Iterator of the contained L{Crash} object keys.
1768
1769 @see: L{get}
1770 @warning: A B{copy} of each object is returned,
1771 so any changes made to them will be lost.
1772
1773 To preserve changes do the following:
1774 1. Keep a reference to the object.
1775 2. Delete the object from the set.
1776 3. Modify the object and add it again.
1777 """
1778 return iter(self.__dict)
1779