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 @group Crash reporting:
32 Crash, CrashContainer, CrashTable, CrashTableMSSQL,
33 VolatileCrashContainer, DummyCrashContainer
34 """
35
36 __revision__ = "$Id: crash.py 744 2010-07-11 20:47:15Z qvasimodo $"
37
38 __all__ = [
39
40
41 'Crash',
42
43
44 'CrashContainer',
45
46
47 'CrashTable',
48
49
50 'CrashTableMSSQL',
51
52
53 'VolatileCrashContainer',
54
55
56 'DummyCrashContainer',
57
58 ]
59
60 from system import MemoryAddresses, PathOperations
61 from textio import HexDump, CrashDump
62 import win32
63
64 import os
65 import time
66 import zlib
67 import pprint
68 import traceback
69
70
71 anydbm = None
72 sqlite = None
73 pyodbc = None
74
75
76
77
78 try:
79 import cerealizer as pickle
84
85
86
87
88
89
90
91 except ImportError:
92
93
94 try:
95 import cPickle as pickle
96
97
98 except ImportError:
99 import pickle
100
101
102 try:
103 from pickletools import optimize
104 except ImportError:
107
111 """
112 Base class for Container types. The code implemented here deals with
113 marshalling and unmarshalling of Crash objects and keys, and it's
114 independent of the database used later.
115
116 @type optimizeKeys: bool
117 @cvar optimizeKeys: C{True} to optimize the marshalling of keys, C{False}
118 otherwise. Only used with the C{pickle} module, ignored when using the
119 more secure C{cerealizer} module.
120
121 @type optimizeValues: bool
122 @cvar optimizeValues: C{True} to optimize the marshalling of keys, C{False}
123 otherwise. Only used with the C{pickle} module, ignored when using the
124 more secure C{cerealizer} module.
125
126 @type compressKeys: bool
127 @cvar compressKeys: C{True} to compress keys when marshalling, C{False}
128 to leave them uncompressed.
129
130 @type compressValues: bool
131 @cvar compressValues: C{True} to compress values when marshalling, C{False}
132 to leave them uncompressed.
133
134 @type escapeKeys: bool
135 @cvar escapeKeys: C{True} to escape keys when marshalling, C{False}
136 to leave them uncompressed.
137
138 @type escapeValues: bool
139 @cvar escapeValues: C{True} to escape values when marshalling, C{False}
140 to leave them uncompressed.
141
142 @type binaryKeys: bool
143 @cvar binaryKeys: C{True} to marshall keys to binary format (the Python
144 C{buffer} type), C{False} to use text marshalled keys (C{str} type).
145
146 @type binaryValues: bool
147 @cvar binaryValues: C{True} to marshall values to binary format (the Python
148 C{buffer} type), C{False} to use text marshalled values (C{str} type).
149
150 @see: L{CrashContainer}, L{CrashTable}, L{VolatileCrashContainer},
151 L{DummyCrashContainer}
152 """
153
154 optimizeKeys = False
155 optimizeValues = False
156 compressKeys = False
157 compressValues = False
158 escapeKeys = False
159 escapeValues = False
160 binaryKeys = False
161 binaryValues = False
162
163 - def __init__(self, initialKeys = None):
164 """
165 @type initialKeys: dict( key S{->} marshalled key )
166 @param initialKeys:
167 (Optional) Use this dictionary (by reference) to add and match keys
168 to their marshalled counterparts.
169 """
170 if initialKeys is None:
171 self.__keys = dict()
172 else:
173 self.__keys = initialKeys
174
176 """
177 @rtype: int
178 @return: Count of known keys.
179 """
180 return len(self.__keys)
181
183 """
184 @rtype: bool
185 @return: C{False} if there are no known keys.
186 """
187 return bool(self.__keys)
188
190 """
191 @type crash: L{Crash}
192 @param crash: Crash object.
193
194 @rtype: bool
195 @return:
196 C{True} if a Crash object with the same key is in the container.
197 """
198 return self.has_key( crash.key() )
199
201 """
202 @type key: L{Crash} key.
203 @param key: Key to find.
204
205 @rtype: bool
206 @return: C{True} if the key is present in the set of known keys.
207 """
208 return key in self.__keys
209
211 """
212 @rtype: iterator
213 @return: Iterator of known L{Crash} keys.
214 """
215 return self.__keys.iterkeys()
216
218 """
219 Removes the given key from the set of known keys.
220
221 @type key: L{Crash} key.
222 @param key: Key to remove.
223 """
224 del self.__keys[key]
225
227 """
228 Marshalls a Crash key to be used in the database.
229
230 @see: L{__init__}
231
232 @type key: L{Crash} key.
233 @param key: Key to convert.
234
235 @rtype: str or buffer
236 @return: Converted key.
237 """
238 if key in self.__keys:
239 return self.__keys[key]
240 if self.optimizeKeys:
241
242 skey = pickle.dumps(key, protocol = pickle.HIGHEST_PROTOCOL)
243 skey = optimize(skey)
244 else:
245
246 skey = pickle.dumps(key, protocol = 0)
247 if self.compressKeys:
248 skey = zlib.compress(skey, zlib.Z_BEST_COMPRESSION)
249 if self.escapeKeys:
250 skey = skey.encode('hex')
251 if self.binaryKeys:
252 skey = buffer(skey)
253 self.__keys[key] = skey
254 return skey
255
257 """
258 Unmarshalls a Crash key read from the database.
259
260 @type key: str or buffer
261 @param key: Key to convert.
262
263 @rtype: L{Crash} key.
264 @return: Converted key.
265 """
266 key = str(key)
267 if self.escapeKeys:
268 key = key.decode('hex')
269 if self.compressKeys:
270 key = zlib.decompress(key)
271 key = pickle.loads(key)
272 return key
273
275 """
276 Marshalls a Crash object to be used in the database.
277 The C{memoryMap} member is B{NOT} stored here.
278
279 @warning: Setting the C{storeMemoryMap} argument to C{True} can lead to
280 a severe performance penalty!
281
282 @type value: L{Crash}
283 @param value: Object to convert.
284
285 @type storeMemoryMap: bool
286 @param storeMemoryMap: C{True} to store the memory map, C{False}
287 otherwise.
288
289 @rtype: str
290 @return: Converted object.
291 """
292 if hasattr(value, 'memoryMap'):
293 crash = value
294 memoryMap = crash.memoryMap
295 try:
296 crash.memoryMap = None
297 if storeMemoryMap and memoryMap is not None:
298
299 crash.memoryMap = list(memoryMap)
300 if self.optimizeValues:
301 value = pickle.dumps(crash, protocol = pickle.HIGHEST_PROTOCOL)
302 value = optimize(value)
303 else:
304 value = pickle.dumps(crash, protocol = 0)
305 finally:
306 crash.memoryMap = memoryMap
307 del memoryMap
308 del crash
309 if self.compressValues:
310 value = zlib.compress(value, zlib.Z_BEST_COMPRESSION)
311 if self.escapeValues:
312 value = value.encode('hex')
313 if self.binaryValues:
314 value = buffer(value)
315 return value
316
318 """
319 Unmarshalls a Crash object read from the database.
320
321 @type value: str
322 @param value: Object to convert.
323
324 @rtype: L{Crash}
325 @return: Converted object.
326 """
327 value = str(value)
328 if self.escapeValues:
329 value = value.decode('hex')
330 if self.compressValues:
331 value = zlib.decompress(value)
332 value = pickle.loads(value)
333 return value
334
338 """
339 Manages a database of persistent Crash objects, trying to avoid duplicates.
340
341 Uses a DBM database file for persistency.
342
343 @see: L{Crash.key}
344 """
345
346 optimizeKeys = False
347 optimizeValues = True
348 compressKeys = False
349 compressValues = True
350 escapeKeys = False
351 escapeValues = False
352 binaryKeys = False
353 binaryValues = False
354
355
356
357
358
359
361 """
362 Iterator of Crash objects. Returned by L{CrashContainer.__iter__}.
363 """
364
366 """
367 @type container: L{CrashContainer}
368 @param container: Crash set to iterate.
369 """
370
371
372
373
374
375
376
377 self.__container = container
378 self.__keys_iter = container.iterkeys()
379
381 """
382 @rtype: L{Crash}
383 @return: A B{copy} of a Crash object in the L{CrashContainer}.
384 @raise StopIteration: No more items left.
385 """
386 key = self.__keys_iter.next()
387 return self.__container.get(key)
388
389 - def __init__(self, filename = None, allowRepeatedKeys = False):
390 """
391 @type filename: str
392 @param filename: (Optional) File name for crash database.
393 If no filename is specified, the container is volatile.
394
395 Volatile containers are stored only in memory and
396 destroyed when they go out of scope.
397
398 @type allowRepeatedKeys: bool
399 @param allowRepeatedKeys:
400 Currently not supported, always use C{False}.
401 """
402 if allowRepeatedKeys:
403 raise NotImplementedError
404 self.__filename = filename
405 if filename:
406 global anydbm
407 if not anydbm:
408 import anydbm
409 self.__db = anydbm.open(filename, 'c')
410 keys = dict([ (self.unmarshall_key(mk), mk)
411 for mk in self.__db.keys() ])
412 ContainerBase.__init__(self, keys)
413 else:
414 self.__db = dict()
415 ContainerBase.__init__(self)
416
418 "Class destructor. Closes the database when this object is destroyed."
419 try:
420 if self.__filename:
421 self.__db.close()
422 except:
423 pass
424
426 """
427 @see: L{itervalues}
428 @rtype: iterator
429 @return: Iterator of the contained L{Crash} objects.
430 """
431 return self.itervalues()
432
434 """
435 @rtype: iterator
436 @return: Iterator of the contained L{Crash} objects.
437
438 @warning: A B{copy} of each object is returned,
439 so any changes made to them will be lost.
440
441 To preserve changes do the following:
442 1. Keep a reference to the object.
443 2. Delete the object from the set.
444 3. Modify the object and add it again.
445 """
446 return self.__CrashContainerIterator(self)
447
448 - def add(self, crash):
449 """
450 Adds a new crash to the container.
451 If the crash appears to be already known, it's ignored.
452
453 @see: L{Crash.key}
454
455 @type crash: L{Crash}
456 @param crash: Crash object to add.
457 """
458
459
460 if crash not in self:
461 key = crash.key()
462 skey = self.marshall_key(key)
463 data = self.marshall_value(crash, storeMemoryMap = True)
464 self.__db[skey] = data
465
467 """
468 Removes a crash from the container.
469
470 @type crash: L{Crash}
471 @param crash: Crash object to remove.
472 """
473 key = crash.key()
474 skey = self.marshall_key(key)
475 del self.__db[skey]
476 self.remove_key(key)
477
478 - def get(self, key):
479 """
480 Retrieves a crash from the container.
481
482 @type key: L{Crash} unique key.
483 @param key: Key of the crash to get.
484
485 @rtype: L{Crash} object.
486 @return: Crash matching the given key.
487
488 @see: L{iterkeys}
489 @warning: A B{copy} of each object is returned,
490 so any changes made to them will be lost.
491
492 To preserve changes do the following:
493 1. Keep a reference to the object.
494 2. Delete the object from the set.
495 3. Modify the object and add it again.
496 """
497 skey = self.marshall_key(key)
498 data = self.__db[skey]
499 crash = self.unmarshall_value(data)
500 return crash
501
505 """
506 Manages a database of persistent Crash objects, trying to avoid duplicates
507 only when requested.
508
509 Uses an SQLite database file for persistency.
510
511 @see: L{Crash.key}
512 """
513
514 optimizeKeys = True
515 optimizeValues = True
516 compressKeys = True
517 compressValues = True
518 escapeKeys = False
519 escapeValues = False
520 binaryKeys = True
521 binaryValues = True
522
523
524
525
526
527
528
529
530 _table_definition = (
531 "CREATE TABLE Crashes ("
532
533
534 "id INTEGER PRIMARY KEY,"
535
536
537
538 "timeStamp TEXT,"
539 "key BLOB,"
540 "pickle BLOB,"
541
542
543 "isExploitable TEXT,"
544 "isExploitableRule TEXT,"
545
546
547 "eventCode INTEGER,"
548 "pid INTEGER,"
549 "tid INTEGER,"
550 "pc INTEGER,"
551 "sp INTEGER,"
552 "fp INTEGER,"
553 "labelPC TEXT,"
554
555
556 "exceptionCode INTEGER,"
557 "exceptionAddress INTEGER,"
558 "exceptionLabel TEXT,"
559 "firstChance INTEGER,"
560 "faultType INTEGER,"
561 "faultAddress INTEGER,"
562 "faultLabel TEXT,"
563 "faultDisasm TEXT,"
564 "stackTrace TEXT,"
565
566
567 "commandLine TEXT,"
568 "environment TEXT,"
569 "notes TEXT"
570 ")"
571 )
572 _insert_row = (
573 "INSERT INTO Crashes VALUES (null, %s)"
574 % ','.join(['?'] * (len(_table_definition.split(',')) - 1))
575 )
576
577 _select_pickle = "SELECT pickle FROM Crashes"
578 _select_key = "SELECT key FROM Crashes"
579 _select_count = "SELECT COUNT(*) FROM Crashes"
580
581 _memory_table_definition = (
582 "CREATE TABLE Memory ("
583 "id INTEGER PRIMARY KEY,"
584 "Crash INTEGER,"
585 "Address INTEGER,"
586 "Size INTEGER,"
587 "State TEXT,"
588 "Access TEXT,"
589 "Type TEXT,"
590 "File TEXT,"
591 "Data BLOB"
592 ")"
593 )
594 _memory_insert_row = (
595 "INSERT INTO Memory VALUES (null, %s)"
596 % ','.join(['?'] * (len(_memory_table_definition.split(',')) - 1))
597 )
598
599
600
601
602
604 """
605 Private method, do not use.
606 """
607 timeStamp = time.asctime( time.gmtime( crash.timeStamp ) )
608 key = self.marshall_key( crash.key() )
609 pickle = self.marshall_value(crash)
610 isExploitable, isExploitableRule, _ = crash.isExploitable()
611 eventCode = crash.eventCode
612 pid = crash.pid
613 tid = crash.tid
614 pc = crash.pc
615 sp = crash.sp
616 fp = crash.fp
617 labelPC = crash.labelPC
618 exceptionCode = crash.exceptionCode
619 exceptionAddress = crash.exceptionAddress
620 exceptionLabel = crash.exceptionLabel
621 firstChance = crash.firstChance
622 faultType = crash.faultType
623 faultAddress = crash.faultAddress
624 faultLabel = crash.faultLabel
625 faultDisasm = CrashDump.dump_code(crash.faultDisasm, crash.pc)
626 stackTrace = CrashDump.dump_stack_trace_with_labels(
627 crash.stackTracePretty)
628 commandLine = crash.commandLine
629 if type(crash.environmentData) == type(u''):
630 environment = u'\0'.join(crash.environmentData) + u'\0'
631 else:
632 environment = '\0'.join(crash.environmentData) + '\0'
633 notes = crash.notesReport()
634 return (
635 timeStamp,
636 key,
637 pickle,
638 isExploitable,
639 isExploitableRule,
640 eventCode,
641 pid,
642 tid,
643 pc,
644 sp,
645 fp,
646 labelPC,
647 exceptionCode,
648 exceptionAddress,
649 exceptionLabel,
650 firstChance,
651 faultType,
652 faultAddress,
653 faultLabel,
654 faultDisasm,
655 stackTrace,
656 commandLine,
657 environment,
658 notes,
659 )
660
662 """
663 Private method, do not use.
664 """
665
666
667 if mbi.State == win32.MEM_RESERVE:
668 State = "Reserved"
669 elif mbi.State == win32.MEM_COMMIT:
670 State = "Commited"
671 elif mbi.State == win32.MEM_FREE:
672 State = "Free"
673 else:
674 State = "Unknown"
675
676
677 if mbi.State != win32.MEM_COMMIT:
678 Protect = ""
679 else:
680 if mbi.Protect & win32.PAGE_NOACCESS:
681 Protect = "--- "
682 elif mbi.Protect & win32.PAGE_READONLY:
683 Protect = "R-- "
684 elif mbi.Protect & win32.PAGE_READWRITE:
685 Protect = "RW- "
686 elif mbi.Protect & win32.PAGE_WRITECOPY:
687 Protect = "RC- "
688 elif mbi.Protect & win32.PAGE_EXECUTE:
689 Protect = "--X "
690 elif mbi.Protect & win32.PAGE_EXECUTE_READ:
691 Protect = "R-- "
692 elif mbi.Protect & win32.PAGE_EXECUTE_READWRITE:
693 Protect = "RW- "
694 elif mbi.Protect & win32.PAGE_EXECUTE_WRITECOPY:
695 Protect = "RCX "
696 else:
697 Protect = "??? "
698 if mbi.Protect & win32.PAGE_GUARD:
699 Protect += "G"
700 else:
701 Protect += "-"
702 if mbi.Protect & win32.PAGE_NOCACHE:
703 Protect += "N"
704 else:
705 Protect += "-"
706 if mbi.Protect & win32.PAGE_WRITECOMBINE:
707 Protect += "W"
708 else:
709 Protect += "-"
710
711
712 if mbi.Type == win32.MEM_IMAGE:
713 Type = "Image"
714 elif mbi.Type == win32.MEM_MAPPED:
715 Type = "Mapped"
716 elif mbi.Type == win32.MEM_PRIVATE:
717 Type = "Private"
718 elif mbi.Type == 0:
719 Type = ""
720 else:
721 Type = "Unknown"
722
723
724
725
726
727
728
729
730
731 content = mbi.content
732 if not content:
733 content = ''
734 else:
735 content = zlib.compress(content, zlib.Z_BEST_COMPRESSION)
736 content = buffer(content)
737
738
739 return (
740 CrashID,
741 mbi.BaseAddress,
742 mbi.RegionSize,
743 State,
744 Protect,
745 Type,
746 mbi.filename,
747 content,
748 )
749
750 - def __init__(self, location = None, allowRepeatedKeys = True):
751 """
752 @type location: str
753 @param location: (Optional) Location of the crash database.
754 If the location is a filename, it's an SQLite database file.
755
756 If no location is specified, the container is volatile.
757 Volatile containers are stored only in memory and
758 destroyed when they go out of scope.
759
760 @type allowRepeatedKeys: bool
761 @param allowRepeatedKeys:
762 If C{True} all L{Crash} objects are stored.
763
764 If C{False} any L{Crash} object with the same key as a
765 previously existing object will be ignored.
766 """
767
768
769 location, dbtype, db, cursor = self._connect(location)
770
771
772 self._location = location
773 self._dbtype = dbtype
774 self._db = db
775 self._cursor = cursor
776
777
778 try:
779 self._cursor.execute(self._table_definition)
780 self._cursor.execute(self._memory_table_definition)
781 self._db.commit()
782 except Exception:
783
784 pass
785
786
787 self._allowRepeatedKeys = allowRepeatedKeys
788 keys = dict()
789 self._cursor.execute(self._select_key)
790 for row in self._cursor:
791 marshalled_key = row[0]
792 unmarshalled_key = self.unmarshall_key(marshalled_key)
793 keys[unmarshalled_key] = marshalled_key
794 ContainerBase.__init__(self, keys)
795
796
797 self._cursor.execute(self._select_count)
798 count = 0
799 for row in self._cursor:
800 count = long(row[0])
801 self._count = count
802
804 """
805 Open the given SQLite file.
806
807 @note: This is a private method and you shouldn't need to call it.
808
809 @type location: str
810 @param location: (Optional) Location of the crash database.
811 If the location is a filename, it's an SQLite database file.
812
813 If no location is specified, the container is volatile.
814 Volatile containers are stored only in memory and
815 destroyed when they go out of scope.
816
817 @rtype: tuple( str, str, database, cursor )
818 @return:
819 Tuple of location, database type, database object, cursor object.
820 """
821
822
823 global sqlite
824 if not sqlite:
825 try:
826 import sqlite3 as sqlite
827 except ImportError:
828 from pysqlite2 import sqlite
829
830
831 if location is None:
832 location = ':memory:'
833 db = sqlite.connect(location)
834 cursor = db.cursor()
835
836
837 return location, 'sqlite', db, cursor
838
839 - def add(self, crash):
840 """
841 Adds a new crash to the container.
842
843 @note:
844 When the C{allowRepeatedKeys} parameter of the constructor
845 is set to C{False}, duplicated crashes are ignored.
846
847 @see: L{Crash.key}
848
849 @type crash: L{Crash}
850 @param crash: Crash object to add.
851 """
852
853
854 if self._allowRepeatedKeys or crash not in self:
855
856
857 row_values = self._get_row_values(crash)
858 self._cursor.execute(self._insert_row, row_values)
859
860
861 if hasattr(crash, 'memoryMap') and crash.memoryMap:
862 cid = self._cursor.lastrowid
863 for mbi in crash.memoryMap:
864 row_values = self._memory_get_row_values(cid, mbi)
865 self._cursor.execute(self._memory_insert_row, row_values)
866
867
868
869 try:
870 self._db.commit()
871 except Exception:
872 self._db.rollback()
873 raise
874
875
876 self._count += 1
877
888
890 """
891 @rtype: int
892 @return: Count of L{Crash} elements in the container.
893 """
894 return self._count
895
899 """
900 Manages a database of persistent Crash objects, trying to avoid duplicates
901 only when requested.
902
903 Uses a Microsoft SQL database for persistency.
904
905 @see: L{Crash.key}
906 """
907
908 optimizeKeys = True
909 optimizeValues = True
910 compressKeys = True
911 compressValues = True
912 escapeKeys = False
913 escapeValues = False
914 binaryKeys = True
915 binaryValues = True
916
917 _table_definition = (
918 "CREATE TABLE Crashes ("
919
920
921 "id INTEGER IDENTITY(1,1) PRIMARY KEY CLUSTERED,"
922
923
924
925 "timeStamp TEXT NOT NULL,"
926 "pickled_key IMAGE NOT NULL,"
927 "pickled_obj IMAGE NOT NULL,"
928
929
930 "isExploitable TEXT,"
931 "isExploitableRule TEXT,"
932
933
934 "eventCode BIGINT,"
935 "pid BIGINT,"
936 "tid BIGINT,"
937 "pc BIGINT,"
938 "sp BIGINT,"
939 "fp BIGINT,"
940 "labelPC TEXT,"
941
942
943 "exceptionCode BIGINT,"
944 "exceptionAddress BIGINT,"
945 "exceptionLabel TEXT,"
946 "firstChance INTEGER,"
947 "faultType INTEGER,"
948 "faultAddress BIGINT,"
949 "faultLabel TEXT,"
950 "faultDisasm TEXT,"
951 "stackTrace TEXT,"
952
953
954 "notes TEXT"
955 ")"
956 )
957 _insert_row = (
958 "INSERT INTO Crashes VALUES (%s)"
959 % ', '.join(list('?' * 22))
960 )
961
962 _select_pickle = "SELECT pickled_obj FROM Crashes"
963 _select_key = "SELECT pickled_key FROM Crashes"
964 _select_count = "SELECT COUNT(*) FROM Crashes"
965
966 _memory_table_definition = (
967 "CREATE TABLE Memory ("
968 "id INTEGER IDENTITY(1,1) PRIMARY KEY CLUSTERED,"
969 "Crash INTEGER,"
970 "Address BIGINT NOT NULL,"
971 "Size BIGINT NOT NULL,"
972 "State TEXT NOT NULL,"
973 "Access TEXT NOT NULL,"
974 "Type TEXT NOT NULL,"
975 "Filename TEXT,"
976 "Data IMAGE"
977 ")"
978 )
979 _memory_insert_row = (
980 "INSERT INTO Memory VALUES (%s)"
981 % ', '.join(list('?' * 8))
982 )
983
985 """
986 Connect to a remote SQL database using the given connection string.
987
988 @note: This is a private method and you shouldn't need to call it.
989
990 @type connectionString: str
991 @param connectionString: (Optional) ODBC connection string.
992
993 @rtype: tuple( str, str, database, cursor )
994 @return:
995 Tuple of location, database type, database object, cursor object.
996 """
997
998
999 global pyodbc
1000 if not pyodbc:
1001 import pyodbc
1002
1003
1004 db = pyodbc.connect(connectionString, autocommit=True)
1005 cursor = db.cursor()
1006
1007
1008 return connectionString, 'odbc', db, cursor
1009
1013 """
1014 Manages a database of volatile Crash objects,
1015 trying to avoid duplicates if requested.
1016
1017 @see: L{Crash.key}
1018 """
1019
1020 - def __init__(self, allowRepeatedKeys = True):
1021 """
1022 Volatile containers are stored only in memory and
1023 destroyed when they go out of scope.
1024
1025 @type allowRepeatedKeys: bool
1026 @param allowRepeatedKeys:
1027 If C{True} all L{Crash} objects are stored.
1028
1029 If C{False} any L{Crash} object with the same key as a
1030 previously existing object will be ignored.
1031 """
1032 super(VolatileCrashContainer, self).__init__()
1033 self.__allowRepeatedKeys = allowRepeatedKeys
1034 self.__dict = dict()
1035 self.__set = set()
1036
1038 """
1039 @type crash: L{Crash}
1040 @param crash: Crash object.
1041
1042 @rtype: bool
1043 @return: C{True} if the Crash object is in the container.
1044 """
1045 return self._dict.has_key( crash.key() )
1046
1048 """
1049 @see: L{itervalues}
1050 @rtype: iterator
1051 @return: Iterator of the contained L{Crash} objects.
1052 """
1053 return self.itervalues()
1054
1056 """
1057 @rtype: int
1058 @return: Count of L{Crash} elements in the container.
1059 """
1060 return len(self.__set)
1061
1063 """
1064 @rtype: bool
1065 @return: C{False} if the container is empty.
1066 """
1067 return bool(len(self))
1068
1069 - def add(self, crash):
1070 """
1071 Adds a new crash to the container.
1072
1073 @note:
1074 When the C{allowRepeatedKeys} parameter of the constructor
1075 is set to C{False}, duplicated crashes are ignored.
1076
1077 @see: L{Crash.key}
1078
1079 @type crash: L{Crash}
1080 @param crash: Crash object to add.
1081 """
1082 key = crash.key()
1083 if self.__allowRepeatedKeys or key not in self.__dict:
1084 self.__dict[key] = crash
1085 self.__set.add(crash)
1086
1088 """
1089 @type key: L{Crash} unique key.
1090 @param key: Key of the crash to get.
1091
1092 @rtype: bool
1093 @return: C{True} if a matching L{Crash} object is in the container.
1094 """
1095 return self.__dict.has_key(key)
1096
1098 """
1099 @rtype: iterator
1100 @return: Iterator of the contained L{Crash} object keys.
1101
1102 @see: L{get}
1103 @warning: A B{copy} of each object is returned,
1104 so any changes made to them will be lost.
1105
1106 To preserve changes do the following:
1107 1. Keep a reference to the object.
1108 2. Delete the object from the set.
1109 3. Modify the object and add it again.
1110 """
1111 return self.__dict.iterkeys()
1112
1114 """
1115 @rtype: iterator
1116 @return: Iterator of the contained L{Crash} objects.
1117
1118 @warning: A B{copy} of each object is returned,
1119 so any changes made to them will be lost.
1120
1121 To preserve changes do the following:
1122 1. Keep a reference to the object.
1123 2. Delete the object from the set.
1124 3. Modify the object and add it again.
1125 """
1126 return iter(self.__set)
1127
1131 """
1132 Fakes a database of volatile Crash objects,
1133 trying to mimic part of it's interface, but
1134 doesn't actually store anything.
1135
1136 @see: L{Crash.key}
1137 """
1138
1139 - def __init__(self, allowRepeatedKeys = True):
1140 """
1141 Fake containers don't store L{Crash} objects, but they implement the
1142 interface properly.
1143
1144 @type allowRepeatedKeys: bool
1145 @param allowRepeatedKeys:
1146 If C{True} the len() of the container returns the total number
1147 of L{Crash} objects added.
1148
1149 If C{False} the len() of the container returns the total number
1150 of L{Crash} objects keys added.
1151 """
1152 super(DummyCrashContainer, self).__init__()
1153 self.__keys = set()
1154 self.__count = 0
1155 self.__allowRepeatedKeys = allowRepeatedKeys
1156
1158 """
1159 @type crash: L{Crash}
1160 @param crash: Crash object.
1161
1162 @rtype: bool
1163 @return: C{True} if the Crash object is in the container.
1164 """
1165 return crash.key() in self.__keys
1166
1168 """
1169 @rtype: int
1170 @return: Count of L{Crash} elements in the container.
1171 """
1172 if self.__allowRepeatedKeys:
1173 return self.__count
1174 return len(self.__keys)
1175
1177 """
1178 @rtype: bool
1179 @return: C{False} if the container is empty.
1180 """
1181 return bool(len(self))
1182
1183 - def add(self, crash):
1184 """
1185 Adds a new crash to the container.
1186
1187 @note:
1188 When the C{allowRepeatedKeys} parameter of the constructor
1189 is set to C{False}, duplicated crashes are ignored.
1190
1191 @see: L{Crash.key}
1192
1193 @type crash: L{Crash}
1194 @param crash: Crash object to add.
1195 """
1196 self.__keys.add( crash.key() )
1197 self.__count += 1
1198
1200 """
1201 @type key: L{Crash} unique key.
1202 @param key: Key of the crash to get.
1203
1204 @rtype: bool
1205 @return: C{True} if a matching L{Crash} object is in the container.
1206 """
1207 return self.__dict.has_key(key)
1208
1210 """
1211 @rtype: iterator
1212 @return: Iterator of the contained L{Crash} object keys.
1213
1214 @see: L{get}
1215 @warning: A B{copy} of each object is returned,
1216 so any changes made to them will be lost.
1217
1218 To preserve changes do the following:
1219 1. Keep a reference to the object.
1220 2. Delete the object from the set.
1221 3. Modify the object and add it again.
1222 """
1223 return iter(self.__dict)
1224
1225
1226
1227 -class Crash (object):
1228 """
1229 Represents a crash, bug, or another interesting event in the debugee.
1230
1231 @group Key:
1232 key
1233
1234 @group Report:
1235 briefReport, fullReport, notesReport, isExploitable
1236
1237 @group Notes:
1238 addNote, getNotes, iterNotes, hasNotes, clearNotes, notes
1239
1240 @group Miscellaneous:
1241 fetch_extra_data
1242
1243 @group Basic information:
1244 timeStamp, eventCode, eventName, pid, tid, registers, labelPC
1245
1246 @group Optional information:
1247 debugString,
1248 modFileName,
1249 lpBaseOfDll,
1250 exceptionCode,
1251 exceptionName,
1252 exceptionDescription,
1253 exceptionAddress,
1254 exceptionLabel,
1255 firstChance,
1256 faultType,
1257 faultAddress,
1258 faultLabel,
1259 isOurBreakpoint,
1260 isSystemBreakpoint,
1261 stackTrace,
1262 stackTracePC,
1263 stackTraceLabels,
1264 stackTracePretty
1265
1266 @group Extra information:
1267 commandLine,
1268 registersPeek,
1269 stackRange,
1270 stackFrame,
1271 stackPeek,
1272 faultCode,
1273 faultMem,
1274 faultPeek,
1275 faultDisasm,
1276 memoryMap
1277
1278 @type timeStamp: float
1279 @ivar timeStamp: Timestamp as returned by time.time().
1280
1281 @type notes: list( str )
1282 @ivar notes: List of strings, each string is a note.
1283
1284 @type eventCode: int
1285 @ivar eventCode: Event code as defined by the Win32 API.
1286
1287 @type eventName: str
1288 @ivar eventName: Event code user-friendly name.
1289
1290 @type pid: int
1291 @ivar pid: Process global ID.
1292
1293 @type tid: int
1294 @ivar tid: Thread global ID.
1295
1296 @type commandLine: None or str
1297 @ivar commandLine: Command line for the target process.
1298
1299 C{None} if unapplicable or unable to retrieve.
1300
1301 @type environmentData: None or list of str
1302 @ivar environmentData: Environment data for the target process.
1303
1304 C{None} if unapplicable or unable to retrieve.
1305
1306 @type environment: None or dict( str S{->} str )
1307 @ivar environment: Environment variables for the target process.
1308
1309 C{None} if unapplicable or unable to retrieve.
1310
1311 @type registers: dict( str S{->} int )
1312 @ivar registers: Dictionary mapping register names to their values.
1313
1314 @type registersPeek: None or dict( str S{->} str )
1315 @ivar registersPeek: Dictionary mapping register names to the data they point to.
1316
1317 C{None} if unapplicable or unable to retrieve.
1318
1319 @type labelPC: None or str
1320 @ivar labelPC: Label pointing to the program counter.
1321
1322 C{None} or invalid if unapplicable or unable to retrieve.
1323
1324 @type debugString: None or str
1325 @ivar debugString: Debug string sent by the debugee.
1326
1327 C{None} if unapplicable or unable to retrieve.
1328
1329 @type exceptionCode: None or int
1330 @ivar exceptionCode: Exception code as defined by the Win32 API.
1331
1332 C{None} if unapplicable or unable to retrieve.
1333
1334 @type exceptionName: None or str
1335 @ivar exceptionName: Exception code user-friendly name.
1336
1337 C{None} if unapplicable or unable to retrieve.
1338
1339 @type exceptionDescription: None or str
1340 @ivar exceptionDescription: Exception description.
1341
1342 C{None} if unapplicable or unable to retrieve.
1343
1344 @type exceptionAddress: None or int
1345 @ivar exceptionAddress: Memory address where the exception occured.
1346
1347 C{None} if unapplicable or unable to retrieve.
1348
1349 @type exceptionLabel: None or str
1350 @ivar exceptionLabel: Label pointing to the exception address.
1351
1352 C{None} or invalid if unapplicable or unable to retrieve.
1353
1354 @type faultType: None or int
1355 @ivar faultType: Access violation type.
1356 Only applicable to memory faults.
1357 Should be one of the following constants:
1358
1359 - L{win32.ACCESS_VIOLATION_TYPE_READ}
1360 - L{win32.ACCESS_VIOLATION_TYPE_WRITE}
1361 - L{win32.ACCESS_VIOLATION_TYPE_DEP}
1362
1363 C{None} if unapplicable or unable to retrieve.
1364
1365 @type faultAddress: None or int
1366 @ivar faultAddress: Access violation memory address.
1367 Only applicable to memory faults.
1368
1369 C{None} if unapplicable or unable to retrieve.
1370
1371 @type faultLabel: None or str
1372 @ivar faultLabel: Label pointing to the access violation memory address.
1373 Only applicable to memory faults.
1374
1375 C{None} if unapplicable or unable to retrieve.
1376
1377 @type firstChance: None or bool
1378 @ivar firstChance:
1379 C{True} for first chance exceptions, C{False} for second chance.
1380
1381 C{None} if unapplicable or unable to retrieve.
1382
1383 @type isOurBreakpoint: bool
1384 @ivar isOurBreakpoint:
1385 C{True} for breakpoints defined by the L{Debug} class,
1386 C{False} otherwise.
1387
1388 C{None} if unapplicable.
1389
1390 @type isSystemBreakpoint: bool
1391 @ivar isSystemBreakpoint:
1392 C{True} for known system-defined breakpoints,
1393 C{False} otherwise.
1394
1395 C{None} if unapplicable.
1396
1397 @type modFileName: None or str
1398 @ivar modFileName: File name of module where the program counter points to.
1399
1400 C{None} or invalid if unapplicable or unable to retrieve.
1401
1402 @type lpBaseOfDll: None or int
1403 @ivar lpBaseOfDll: Base of module where the program counter points to.
1404
1405 C{None} if unapplicable or unable to retrieve.
1406
1407 @type stackTrace: None or tuple of tuple( int, int, str )
1408 @ivar stackTrace:
1409 Stack trace of the current thread as a tuple of
1410 ( frame pointer, return address, module filename ).
1411
1412 C{None} or empty if unapplicable or unable to retrieve.
1413
1414 @type stackTracePretty: None or tuple of tuple( int, str )
1415 @ivar stackTracePretty:
1416 Stack trace of the current thread as a tuple of
1417 ( frame pointer, return location ).
1418
1419 C{None} or empty if unapplicable or unable to retrieve.
1420
1421 @type stackTracePC: None or tuple( int... )
1422 @ivar stackTracePC: Tuple of return addresses in the stack trace.
1423
1424 C{None} or empty if unapplicable or unable to retrieve.
1425
1426 @type stackTraceLabels: None or tuple( str... )
1427 @ivar stackTraceLabels:
1428 Tuple of labels pointing to the return addresses in the stack trace.
1429
1430 C{None} or empty if unapplicable or unable to retrieve.
1431
1432 @type stackRange: tuple( int, int )
1433 @ivar stackRange:
1434 Stack beginning and end pointers, in memory addresses order.
1435
1436 C{None} if unapplicable or unable to retrieve.
1437
1438 @type stackFrame: None or str
1439 @ivar stackFrame: Data pointed to by the stack pointer.
1440
1441 C{None} or empty if unapplicable or unable to retrieve.
1442
1443 @type stackPeek: None or dict( int S{->} str )
1444 @ivar stackPeek: Dictionary mapping stack offsets to the data they point to.
1445
1446 C{None} or empty if unapplicable or unable to retrieve.
1447
1448 @type faultCode: None or str
1449 @ivar faultCode: Data pointed to by the program counter.
1450
1451 C{None} or empty if unapplicable or unable to retrieve.
1452
1453 @type faultMem: None or str
1454 @ivar faultMem: Data pointed to by the exception address.
1455
1456 C{None} or empty if unapplicable or unable to retrieve.
1457
1458 @type faultPeek: None or dict( intS{->} str )
1459 @ivar faultPeek: Dictionary mapping guessed pointers at L{faultMem} to the data they point to.
1460
1461 C{None} or empty if unapplicable or unable to retrieve.
1462
1463 @type faultDisasm: None or tuple of tuple( long, int, str, str )
1464 @ivar faultDisasm: Dissassembly around the program counter.
1465
1466 C{None} or empty if unapplicable or unable to retrieve.
1467
1468 @type memoryMap: None or list of L{win32.MemoryBasicInformation} objects.
1469 @ivar memoryMap: Memory snapshot of the program. May contain the actual
1470 data from the entire process memory if requested.
1471 See L{fetch_extra_data} for more details.
1472
1473 C{None} or empty if unapplicable or unable to retrieve.
1474 """
1475
1477 """
1478 @type event: L{Event}
1479 @param event: Event object for crash.
1480 """
1481
1482
1483 self.timeStamp = time.time()
1484
1485
1486 self.notes = list()
1487
1488
1489 process = event.get_process()
1490 thread = event.get_thread()
1491
1492
1493 self.eventCode = event.get_event_code()
1494 self.eventName = event.get_event_name()
1495 self.pid = event.get_pid()
1496 self.tid = event.get_tid()
1497 self.registers = dict(thread.get_context())
1498 self.labelPC = process.get_label_at_address(self.pc)
1499
1500
1501 self.commandLine = None
1502 self.environment = None
1503 self.environmentData = None
1504 self.registersPeek = None
1505 self.debugString = None
1506 self.modFileName = None
1507 self.lpBaseOfDll = None
1508 self.exceptionCode = None
1509 self.exceptionName = None
1510 self.exceptionDescription = None
1511 self.exceptionAddress = None
1512 self.exceptionLabel = None
1513 self.firstChance = None
1514 self.faultType = None
1515 self.faultAddress = None
1516 self.faultLabel = None
1517 self.isOurBreakpoint = None
1518 self.isSystemBreakpoint = None
1519 self.stackTrace = None
1520 self.stackTracePC = None
1521 self.stackTraceLabels = None
1522 self.stackTracePretty = None
1523 self.stackRange = None
1524 self.stackFrame = None
1525 self.stackPeek = None
1526 self.faultCode = None
1527 self.faultMem = None
1528 self.faultPeek = None
1529 self.faultDisasm = None
1530 self.memoryMap = None
1531
1532
1533 if self.eventCode == win32.OUTPUT_DEBUG_STRING_EVENT:
1534 self.debugString = event.get_debug_string()
1535
1536
1537
1538
1539 elif self.eventCode in (win32.CREATE_PROCESS_DEBUG_EVENT,
1540 win32.EXIT_PROCESS_DEBUG_EVENT,
1541 win32.LOAD_DLL_DEBUG_EVENT,
1542 win32.UNLOAD_DLL_DEBUG_EVENT):
1543 aModule = event.get_module()
1544 self.modFileName = event.get_filename()
1545 if not self.modFileName:
1546 self.modFileName = aModule.get_filename()
1547 self.lpBaseOfDll = event.get_module_base()
1548 if not self.lpBaseOfDll:
1549 self.lpBaseOfDll = aModule.get_base()
1550
1551
1552
1553 elif self.eventCode == win32.EXCEPTION_DEBUG_EVENT:
1554
1555
1556 self.exceptionCode = event.get_exception_code()
1557 self.exceptionName = event.get_exception_name()
1558 self.exceptionDescription = event.get_exception_description()
1559 self.exceptionAddress = event.get_exception_address()
1560 self.firstChance = event.is_first_chance()
1561 self.exceptionLabel = process.get_label_at_address(
1562 self.exceptionAddress)
1563 if self.exceptionCode in (win32.EXCEPTION_ACCESS_VIOLATION,
1564 win32.EXCEPTION_GUARD_PAGE,
1565 win32.EXCEPTION_IN_PAGE_ERROR):
1566 self.faultType = event.get_fault_type()
1567 self.faultAddress = event.get_fault_address()
1568 self.faultLabel = process.get_label_at_address(
1569 self.faultAddress)
1570 elif self.exceptionCode in (win32.EXCEPTION_BREAKPOINT,
1571 win32.EXCEPTION_SINGLE_STEP):
1572 self.isOurBreakpoint = hasattr(event, 'breakpoint') \
1573 and event.breakpoint
1574 self.isSystemBreakpoint = \
1575 process.is_system_defined_breakpoint(self.exceptionAddress)
1576
1577
1578 try:
1579 self.stackTracePretty = thread.get_stack_trace_with_labels()
1580 except Exception, e:
1581 pass
1582 try:
1583 self.stackTrace = thread.get_stack_trace()
1584 stackTracePC = [ ra for (_,ra,_) in self.stackTrace ]
1585 self.stackTracePC = tuple(stackTracePC)
1586 stackTraceLabels = [ process.get_label_at_address(ra) \
1587 for ra in self.stackTracePC ]
1588 self.stackTraceLabels = tuple(stackTraceLabels)
1589 except Exception, e:
1590 pass
1591
1593 """
1594 Fetch extra data from the L{Event} object.
1595
1596 @note: This is only needed for exceptions. Since this method may take
1597 a little longer to run, it's best to call it only after you've
1598 determined the crash is interesting and you want to save it.
1599
1600 @type event: L{Event}
1601 @param event: Event object for crash.
1602
1603 @type takeMemorySnapshot: int
1604 @param takeMemorySnapshot:
1605 Memory snapshot behavior:
1606 - C{0} to take no memory information (default).
1607 - C{1} to take only the memory map.
1608 See L{Process.get_memory_map}.
1609 - C{2} to take a full memory snapshot.
1610 See L{Process.take_memory_snapshot}.
1611 - C{3} to take a full memory snapshot generator.
1612 See L{Process.generate_memory_snapshot}.
1613 """
1614
1615
1616 process = event.get_process()
1617 thread = event.get_thread()
1618
1619
1620 try:
1621 self.commandLine = process.get_command_line()
1622 except Exception:
1623
1624 pass
1625
1626
1627 try:
1628 self.environmentData = process.get_environment_data()
1629 self.environment = process.parse_environment_data(
1630 self.environmentData)
1631 except Exception:
1632
1633 pass
1634
1635
1636 if self.eventCode == win32.EXCEPTION_DEBUG_EVENT:
1637
1638
1639 self.registersPeek = thread.peek_pointers_in_registers()
1640
1641
1642 aModule = process.get_module_at_address(self.pc)
1643 if aModule is not None:
1644 self.modFileName = aModule.get_filename()
1645 self.lpBaseOfDll = aModule.get_base()
1646
1647
1648 try:
1649 self.stackRange = thread.get_stack_range()
1650 except Exception, e:
1651 pass
1652 try:
1653 self.stackFrame = thread.get_stack_frame()
1654 stackFrame = self.stackFrame
1655 except Exception, e:
1656 self.stackFrame = thread.peek_stack_data()
1657 stackFrame = self.stackFrame[:64]
1658 if stackFrame:
1659 self.stackPeek = process.peek_pointers_in_data(stackFrame)
1660
1661
1662 self.faultCode = thread.peek_code_bytes()
1663 try:
1664 self.faultDisasm = thread.disassemble_around_pc(32)
1665 except Exception, e:
1666
1667 pass
1668
1669
1670
1671 if self.pc != self.exceptionAddress and self.exceptionCode in (
1672 win32.EXCEPTION_ACCESS_VIOLATION,
1673 win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
1674 win32.EXCEPTION_DATATYPE_MISALIGNMENT,
1675 win32.EXCEPTION_IN_PAGE_ERROR,
1676 win32.EXCEPTION_STACK_OVERFLOW,
1677 win32.EXCEPTION_GUARD_PAGE,
1678 ):
1679 self.faultMem = process.peek(self.exceptionAddress, 64)
1680 if self.faultMem:
1681 self.faultPeek = process.peek_pointers_in_data(self.faultMem)
1682
1683
1684
1685 if takeMemorySnapshot == 1:
1686 self.memoryMap = process.get_memory_map()
1687 mappedFilenames = process.get_mapped_filenames(self.memoryMap)
1688 for mbi in self.memoryMap:
1689 mbi.filename = mappedFilenames.get(mbi.BaseAddress, None)
1690 mbi.content = None
1691 elif takeMemorySnapshot == 2:
1692 self.memoryMap = process.take_memory_snapshot()
1693 elif takeMemorySnapshot == 3:
1694 self.memoryMap = process.generate_memory_snapshot()
1695
1696 @property
1698 """
1699 Value of the program counter register.
1700
1701 @rtype: int
1702 """
1703 try:
1704 return self.registers['Eip']
1705 except KeyError:
1706 return self.registers['Rip']
1707
1708 @property
1710 """
1711 Value of the stack pointer register.
1712
1713 @rtype: int
1714 """
1715 try:
1716 return self.registers['Esp']
1717 except KeyError:
1718 return self.registers['Rsp']
1719
1720 @property
1722 """
1723 Value of the frame pointer register.
1724
1725 @rtype: int
1726 """
1727 try:
1728 return self.registers['Ebp']
1729 except KeyError:
1730 return self.registers['Rbp']
1731
1734
1736 """
1737 Generates an approximately unique key for the Crash object.
1738
1739 This key can be used as an heuristic to determine if two crashes were
1740 caused by the same software error. Ideally it should be treated as an
1741 opaque object.
1742
1743 @rtype: (opaque)
1744 @return: Crash unique key.
1745 """
1746 if self.labelPC:
1747 eip = self.labelPC
1748 else:
1749 eip = self.pc
1750 if self.stackTraceLabels:
1751 trace = self.stackTraceLabels
1752 else:
1753 trace = self.stackTracePC
1754 return (
1755 self.eventCode,
1756 self.exceptionCode,
1757 eip,
1758 trace,
1759 self.debugString,
1760 )
1761
1763 """
1764 Guess how likely is it that the bug causing the crash can be leveraged
1765 into an exploitable vulnerability.
1766
1767 @note: Don't take this as an equivalent of a real exploitability
1768 analysis, that can only be done by a human being! This is only
1769 a guideline, useful for example to sort crashes - placing the most
1770 interesting ones at the top.
1771
1772 @see: The heuristics are similar to those of the B{!exploitable}
1773 extension for I{WinDBG}, which can be downloaded from here:
1774
1775 U{http://www.codeplex.com/msecdbg}
1776
1777 @rtype: tuple( str, str )
1778 @return: The first element of the tuple is the result of the analysis,
1779 being one of the following:
1780
1781 - Not an exception
1782 - Not exploitable
1783 - Not likely exploitable
1784 - Unknown
1785 - Probably exploitable
1786 - Exploitable
1787
1788 The second element of the tuple is a code to identify the matched
1789 heuristic rule.
1790
1791 The second element of the tuple is a description string of the
1792 reason behind the result.
1793 """
1794
1795
1796
1797 if self.eventCode != win32.EXCEPTION_DEBUG_EVENT:
1798 return ("Not an exception", "NotAnException", "The event is not an exception.")
1799
1800 if self.stackRange and self.pc is not None and self.stackRange[0] <= self.pc < self.stackRange[1]:
1801 return ("Exploitable", "StackCodeExecution", "Code execution from the stack is considered exploitable.")
1802
1803
1804 if self.stackRange and self.sp is not None and not (self.stackRange[0] <= self.sp < self.stackRange[1]):
1805 return ("Exploitable", "StackPointerCorruption", "Stack pointer corruption is considered exploitable.")
1806
1807 if self.exceptionCode == win32.EXCEPTION_ILLEGAL_INSTRUCTION:
1808 return ("Exploitable", "IllegalInstruction", "An illegal instruction exception indicates that the attacker controls execution flow.")
1809
1810 if self.exceptionCode == win32.EXCEPTION_PRIV_INSTRUCTION:
1811 return ("Exploitable", "PrivilegedInstruction", "A privileged instruction exception indicates that the attacker controls execution flow.")
1812
1813 if self.exceptionCode == win32.EXCEPTION_GUARD_PAGE:
1814 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).")
1815
1816 if self.exceptionCode == win32.STATUS_STACK_BUFFER_OVERRUN:
1817 return ("Exploitable", "GSViolation", "An overrun of a protected stack buffer has been detected. This is considered exploitable, and must be fixed.")
1818
1819 if self.exceptionCode == win32.STATUS_HEAP_CORRUPTION:
1820 return ("Exploitable", "HeapCorruption", "Heap Corruption has been detected. This is considered exploitable, and must be fixed.")
1821
1822 if self.exceptionCode == win32.EXCEPTION_ACCESS_VIOLATION:
1823 nearNull = self.faultAddress is None or MemoryAddresses.align_address_to_page_start(self.faultAddress) == 0
1824 controlFlow = self.__is_control_flow()
1825 blockDataMove = self.__is_block_data_move()
1826 if self.faultType == win32.EXCEPTION_EXECUTE_FAULT:
1827 if nearNull:
1828 return ("Probably exploitable", "DEPViolation", "User mode DEP access violations are probably exploitable if near NULL.")
1829 else:
1830 return ("Exploitable", "DEPViolation", "User mode DEP access violations are exploitable.")
1831 elif self.faultType == win32.EXCEPTION_WRITE_FAULT:
1832 if nearNull:
1833 return ("Probably exploitable", "WriteAV", "User mode write access violations that are near NULL are probably exploitable.")
1834 else:
1835 return ("Exploitable", "WriteAV", "User mode write access violations that are not near NULL are exploitable.")
1836 elif self.faultType == win32.EXCEPTION_READ_FAULT:
1837 if self.faultAddress == self.pc:
1838 if nearNull:
1839 return ("Probably exploitable", "ReadAVonIP", "Access violations at the instruction pointer are probably exploitable if near NULL.")
1840 else:
1841 return ("Exploitable", "ReadAVonIP", "Access violations at the instruction pointer are exploitable if not near NULL.")
1842 if controlFlow:
1843 if nearNull:
1844 return ("Probably exploitable", "ReadAVonControlFlow", "Access violations near null in control flow instructions are considered probably exploitable.")
1845 else:
1846 return ("Exploitable", "ReadAVonControlFlow", "Access violations not near null in control flow instructions are considered exploitable.")
1847 if blockDataMove:
1848 return ("Probably exploitable", "ReadAVonBlockMove", "This is a read access violation in a block data move, and is therefore classified as probably exploitable.")
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859 result = ("Unknown", "Unknown", "Exploitability unknown.")
1860
1861 if self.exceptionCode == win32.EXCEPTION_ACCESS_VIOLATION:
1862 if self.faultType == win32.EXCEPTION_READ_FAULT:
1863 if nearNull:
1864 result = ("Not likely exploitable", "ReadAVNearNull", "This is a user mode read access violation near null, and is probably not exploitable.")
1865
1866 elif self.exceptionCode == win32.EXCEPTION_INT_DIVIDE_BY_ZERO:
1867 result = ("Not likely exploitable", "DivideByZero", "This is an integer divide by zero, and is probably not exploitable.")
1868
1869 elif self.exceptionCode == win32.EXCEPTION_FLT_DIVIDE_BY_ZERO:
1870 result = ("Not likely exploitable", "DivideByZero", "This is a floating point divide by zero, and is probably not exploitable.")
1871
1872 elif self.exceptionCode in (win32.EXCEPTION_BREAKPOINT, win32.STATUS_WX86_BREAKPOINT):
1873 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.")
1874
1875
1876
1877
1878
1879
1880 return result
1881
1883 """
1884 Private method to tell if the instruction pointed to by the program
1885 counter is a control flow instruction.
1886
1887 Currently only works for x86 and amd64 architectures.
1888 """
1889 jump_instructions = (
1890 'jmp', 'jecxz', 'jcxz',
1891 'ja', 'jnbe', 'jae', 'jnb', 'jb', 'jnae', 'jbe', 'jna', 'jc', 'je',
1892 'jz', 'jnc', 'jne', 'jnz', 'jnp', 'jpo', 'jp', 'jpe', 'jg', 'jnle',
1893 'jge', 'jnl', 'jl', 'jnge', 'jle', 'jng', 'jno', 'jns', 'jo', 'js'
1894 )
1895 call_instructions = ( 'call', 'ret', 'retn' )
1896 loop_instructions = ( 'loop', 'loopz', 'loopnz', 'loope', 'loopne' )
1897 control_flow_instructions = call_instructions + loop_instructions + \
1898 jump_instructions
1899 isControlFlow = False
1900 instruction = None
1901 if self.pc is not None and self.faultDisasm:
1902 for disasm in self.faultDisasm:
1903 if disasm[0] == self.pc:
1904 instruction = disasm[2].lower().strip()
1905 break
1906 if instruction:
1907 for x in control_flow_instructions:
1908 if x in instruction:
1909 isControlFlow = True
1910 break
1911 return isControlFlow
1912
1914 """
1915 Private method to tell if the instruction pointed to by the program
1916 counter is a block data move instruction.
1917
1918 Currently only works for x86 and amd64 architectures.
1919 """
1920 block_data_move_instructions = ('movs', 'stos', 'lods')
1921 isBlockDataMove = False
1922 instruction = None
1923 if self.pc is not None and self.faultDisasm:
1924 for disasm in self.faultDisasm:
1925 if disasm[0] == self.pc:
1926 instruction = disasm[2].lower().strip()
1927 break
1928 if instruction:
1929 for x in block_data_move_instructions:
1930 if x in instruction:
1931 isBlockDataMove = True
1932 break
1933 return isBlockDataMove
1934
1936 """
1937 @rtype: str
1938 @return: Short description of the event.
1939 """
1940 if self.exceptionCode is not None:
1941 if self.exceptionCode == win32.EXCEPTION_BREAKPOINT:
1942 if self.isOurBreakpoint:
1943 what = "Breakpoint hit"
1944 elif self.isSystemBreakpoint:
1945 what = "System breakpoint hit"
1946 else:
1947 what = "Assertion failed"
1948 elif self.exceptionDescription:
1949 what = self.exceptionDescription
1950 elif self.exceptionName:
1951 what = self.exceptionName
1952 else:
1953 what = "Exception %s" % HexDump.integer(self.exceptionCode)
1954 if self.firstChance:
1955 chance = 'first'
1956 else:
1957 chance = 'second'
1958 if self.exceptionLabel:
1959 where = self.exceptionLabel
1960 elif self.exceptionAddress:
1961 where = HexDump.address(self.exceptionAddress)
1962 elif self.labelPC:
1963 where = self.labelPC
1964 else:
1965 where = HexDump.address(self.pc)
1966 msg = "%s (%s chance) at %s" % (what, chance, where)
1967 elif self.debugString is not None:
1968 if self.labelPC:
1969 where = self.labelPC
1970 else:
1971 where = HexDump.address(self.pc)
1972 msg = "Debug string from %s: %r" % (where, self.debugString)
1973 else:
1974 if self.labelPC:
1975 where = self.labelPC
1976 else:
1977 where = HexDump.address(self.pc)
1978 msg = "%s (%s) at %s" % (
1979 self.eventName,
1980 HexDump.integer(self.eventCode),
1981 where
1982 )
1983 return msg
1984
1986 """
1987 @type bShowNotes: bool
1988 @param bShowNotes: C{True} to show the user notes, C{False} otherwise.
1989
1990 @rtype: str
1991 @return: Long description of the event.
1992 """
1993 msg = self.briefReport()
1994 msg += '\n'
1995
1996 if win32.sizeof(win32.LPVOID) == 4:
1997 width = 16
1998 else:
1999 width = 8
2000
2001 if self.eventCode == win32.EXCEPTION_DEBUG_EVENT:
2002 (exploitability, expcode, expdescription) = self.isExploitable()
2003 msg += '\nSecurity risk level: %s\n' % exploitability
2004 msg += ' %s\n' % expdescription
2005
2006 if bShowNotes and self.notes:
2007 msg += '\nNotes:\n'
2008 msg += self.notesReport()
2009
2010 if self.commandLine:
2011 msg += '\nCommand line: %s\n' % self.commandLine
2012
2013 if self.environment:
2014 msg += '\nEnvironment: %s\n' % pprint.pformat(self.environment)
2015
2016 if not self.labelPC:
2017 base = HexDump.address(self.lpBaseOfDll)
2018 if self.modFileName:
2019 fn = PathOperations.pathname_to_filename(self.modFileName)
2020 msg += '\nRunning in %s (%s)\n' % (fn, base)
2021 else:
2022 msg += '\nRunning in module at %s\n' % base
2023
2024 if self.registers:
2025 msg += '\nRegisters:\n'
2026 msg += CrashDump.dump_registers(self.registers)
2027 if self.registersPeek:
2028 msg += '\n'
2029 msg += CrashDump.dump_registers_peek(self.registers,
2030 self.registersPeek,
2031 width = width)
2032
2033 if self.faultDisasm:
2034 msg += '\nCode disassembly:\n'
2035 msg += CrashDump.dump_code(self.faultDisasm, self.pc)
2036
2037 if self.stackTrace:
2038 msg += '\nStack trace:\n'
2039 if self.stackTracePretty:
2040 msg += CrashDump.dump_stack_trace_with_labels(
2041 self.stackTracePretty)
2042 else:
2043 msg += CrashDump.dump_stack_trace(self.stackTrace)
2044
2045 if self.stackFrame:
2046 if self.stackPeek:
2047 msg += '\nStack pointers:\n'
2048 msg += CrashDump.dump_stack_peek(self.stackPeek, width = width)
2049 msg += '\nStack dump:\n'
2050 msg += HexDump.hexblock(self.stackFrame, self.sp, width = width)
2051
2052 if self.faultCode and not self.modFileName:
2053 msg += '\nCode dump:\n'
2054 msg += HexDump.hexblock(self.faultCode, self.pc, width = width)
2055
2056 if self.faultMem:
2057 if self.faultPeek:
2058 msg += '\nException address pointers:\n'
2059 msg += CrashDump.dump_data_peek(self.faultPeek,
2060 self.exceptionAddress,
2061 width = width)
2062 msg += '\nException address dump:\n'
2063 msg += HexDump.hexblock(self.faultMem, self.exceptionAddress,
2064 width = width)
2065
2066 if self.memoryMap:
2067 msg += '\nMemory map:\n'
2068 mappedFileNames = dict()
2069 for mbi in self.memoryMap:
2070 if hasattr(mbi, 'filename') and mbi.filename:
2071 mappedFileNames[mbi.BaseAddress] = mbi.filename
2072 msg += CrashDump.dump_memory_map(self.memoryMap, mappedFileNames)
2073
2074 if not msg.endswith('\n\n'):
2075 if not msg.endswith('\n'):
2076 msg += '\n'
2077 msg += '\n'
2078 return msg
2079
2081 """
2082 @rtype: str
2083 @return: All notes, merged and formatted for a report.
2084 """
2085 msg = ''
2086 if self.notes:
2087 for n in self.notes:
2088 n = n.strip('\n')
2089 if '\n' in n:
2090 n = n.strip('\n')
2091 msg += ' * %s\n' % n.pop(0)
2092 for x in n:
2093 msg += ' %s\n' % x
2094 else:
2095 msg += ' * %s\n' % n
2096 return msg
2097
2099 """
2100 Add a note to the crash event.
2101
2102 @type msg: str
2103 @param msg: Note text.
2104 """
2105 self.notes.append(msg)
2106
2108 """
2109 Clear the notes of this crash event.
2110 """
2111 self.notes = list()
2112
2114 """
2115 Get the list of notes of this crash event.
2116
2117 @rtype: list( str )
2118 @return: List of notes.
2119 """
2120 return self.notes
2121
2123 """
2124 Iterate the notes of this crash event.
2125
2126 @rtype: listiterator
2127 @return: Iterator of the list of notes.
2128 """
2129 return self.notes.__iter__()
2130
2132 """
2133 @rtype: bool
2134 @return: C{True} if there are notes for this crash event.
2135 """
2136 return bool( self.notes )
2137