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 Functions for text input, logging or text output.
30
31 @group Debugging:
32 DebugLog
33
34 @group Crash reporting:
35 CrashDump
36
37 @group Miscellaneous:
38 HexDump,
39 HexInput,
40 HexOutput,
41 Table,
42 Logger
43 """
44
45 __revision__ = "$Id: textio.py 689 2010-06-20 13:56:12Z qvasimodo $"
46
47 __all__ = [
48 'HexDump',
49 'HexInput',
50 'HexOutput',
51 'Table',
52 'CrashDump',
53 'DebugLog',
54 'Logger',
55 ]
56
57 import win32
58
59 import time
60 import struct
61 import traceback
282
286 """
287 Static functions for user output parsing.
288 The counterparts for each method are in the L{HexInput} class.
289
290 @type integer_size: int
291 @cvar integer_size: Size in characters of an outputted integer.
292 This value is platform dependent.
293
294 @type address_size: int
295 @cvar address_size: Size in characters of an outputted address.
296 This value is platform dependent.
297 """
298 @classmethod
300 'Don\'t try to instance this class, it\'s just a namespace!'
301 raise NotImplementedError
302
303 integer_size = len('%x' % (win32.DWORD(-1).value))+2
304 address_size = len('%x' % (win32.SIZE_T(-1).value))+2
305
306 @classmethod
308 """
309 @type integer: int
310 @param integer: Integer.
311
312 @rtype: str
313 @return: Text output.
314 """
315 return ('0x%%.%dx' % (cls.integer_size - 2)) % integer
316
317 @classmethod
319 """
320 @type address: int
321 @param address: Memory address.
322
323 @rtype: str
324 @return: Text output.
325 """
326 return ('0x%%.%dx' % (cls.address_size - 2)) % address
327
328 @staticmethod
330 """
331 Convert binary data to a string of hexadecimal numbers.
332
333 @type data: str
334 @param data: Binary data.
335
336 @rtype: str
337 @return: Hexadecimal representation.
338 """
339 return HexDump.hexadecimal(data, separator = '')
340
341 @classmethod
343 """
344 Write a list of integers to a file.
345 If a file of the same name exists, it's contents are replaced.
346
347 See L{HexInput.integer_list_file} for a description of the file format.
348
349 @type filename: str
350 @param filename: Name of the file to write.
351
352 @type values: list( int )
353 @param values: List of integers to write to the file.
354 """
355 fd = open(filename, 'w')
356 for integer in values:
357 print >> fd, cls.integer(integer)
358 fd.close()
359
360 @classmethod
362 """
363 Write a list of strings to a file.
364 If a file of the same name exists, it's contents are replaced.
365
366 See L{HexInput.string_list_file} for a description of the file format.
367
368 @type filename: str
369 @param filename: Name of the file to write.
370
371 @type values: list( int )
372 @param values: List of strings to write to the file.
373 """
374 fd = open(filename, 'w')
375 for string in values:
376 print >> fd, string
377 fd.close()
378
379 @classmethod
381 """
382 Write a list of mixed values to a file.
383 If a file of the same name exists, it's contents are replaced.
384
385 See L{HexInput.mixed_list_file} for a description of the file format.
386
387 @type filename: str
388 @param filename: Name of the file to write.
389
390 @type values: list( int )
391 @param values: List of mixed values to write to the file.
392 """
393 fd = open(filename, 'w')
394 for original in values:
395 try:
396 parsed = cls.integer(original)
397 except TypeError:
398 parsed = repr(original)
399 print >> fd, parsed
400 fd.close()
401
405 """
406 Static functions for hexadecimal dumps.
407
408 @type integer_size: int
409 @cvar integer_size: Size in characters of an outputted integer.
410 This value is platform dependent.
411
412 @type address_size: int
413 @cvar address_size: Size in characters of an outputted address.
414 This value is platform dependent.
415 """
416 @classmethod
418 'Don\'t try to instance this class, it\'s just a namespace!'
419 raise NotImplementedError
420
421 integer_size = len('%x' % (win32.DWORD(-1).value))
422 address_size = len('%x' % (win32.SIZE_T(-1).value))
423
424 @classmethod
426 """
427 @type integer: int
428 @param integer: Integer.
429
430 @rtype: str
431 @return: Text output.
432 """
433 return ('%%.%dX' % cls.integer_size) % integer
434
435 @classmethod
437 """
438 @type address: int
439 @param address: Memory address.
440
441 @rtype: str
442 @return: Text output.
443 """
444 return ('%%.%dX' % cls.address_size) % address
445
446 @staticmethod
448 """
449 Replace unprintable characters with dots.
450
451 @type data: str
452 @param data: Binary data.
453
454 @rtype: str
455 @return: Printable text.
456 """
457 result = ''
458 for c in data:
459 if 32 < ord(c) < 128:
460 result += c
461 else:
462 result += '.'
463 return result
464
465 @staticmethod
467 """
468 Convert binary data to a string of hexadecimal numbers.
469
470 @type data: str
471 @param data: Binary data.
472
473 @type separator: str
474 @param separator:
475 Separator between the hexadecimal representation of each character.
476
477 @rtype: str
478 @return: Hexadecimal representation.
479 """
480 return separator.join( [ '%.2x' % ord(c) for c in data ] )
481
482 @staticmethod
484 """
485 Convert binary data to a string of hexadecimal WORDs.
486
487 @type data: str
488 @param data: Binary data.
489
490 @type separator: str
491 @param separator:
492 Separator between the hexadecimal representation of each WORD.
493
494 @rtype: str
495 @return: Hexadecimal representation.
496 """
497 if len(data) & 1 != 0:
498 data += '\0'
499 return separator.join( [ '%.4x' % struct.unpack('<H', data[i:i+2])[0] \
500 for i in xrange(0, len(data), 2) ] )
501
502 @staticmethod
504 """
505 Convert binary data to a string of hexadecimal DWORDs.
506
507 @type data: str
508 @param data: Binary data.
509
510 @type separator: str
511 @param separator:
512 Separator between the hexadecimal representation of each DWORD.
513
514 @rtype: str
515 @return: Hexadecimal representation.
516 """
517 if len(data) & 3 != 0:
518 data += '\0' * (4 - (len(data) & 3))
519 return separator.join( [ '%.8x' % struct.unpack('<L', data[i:i+4])[0] \
520 for i in xrange(0, len(data), 4) ] )
521
522 @staticmethod
524 """
525 Convert binary data to a string of hexadecimal QWORDs.
526
527 @type data: str
528 @param data: Binary data.
529
530 @type separator: str
531 @param separator:
532 Separator between the hexadecimal representation of each QWORD.
533
534 @rtype: str
535 @return: Hexadecimal representation.
536 """
537 if len(data) & 7 != 0:
538 data += '\0' * (8 - (len(data) & 7))
539 return separator.join( [ '%.16x' % struct.unpack('<Q', data[i:i+8])[0]\
540 for i in xrange(0, len(data), 8) ] )
541
542 @classmethod
543 - def hexline(cls, data, separator = ' ', width = None):
544 """
545 Dump a line of hexadecimal numbers from binary data.
546
547 @type data: str
548 @param data: Binary data.
549
550 @type separator: str
551 @param separator:
552 Separator between the hexadecimal representation of each character.
553
554 @type width: int
555 @param width:
556 (Optional) Maximum number of characters to convert per text line.
557 This value is also used for padding.
558
559 @rtype: str
560 @return: Multiline output text.
561 """
562 if width is None:
563 fmt = '%s %s'
564 else:
565 fmt = '%%-%ds %%-%ds' % ((len(separator)+2)*width-1, width)
566 return fmt % (cls.hexadecimal(data, separator), cls.printable(data))
567
568 @classmethod
569 - def hexblock(cls, data, address = None, separator = ' ', width = 8):
570 """
571 Dump a block of hexadecimal numbers from binary data.
572 Also show a printable text version of the data.
573
574 @type data: str
575 @param data: Binary data.
576
577 @type address: str
578 @param address: Memory address where the data was read from.
579
580 @type separator: str
581 @param separator:
582 Separator between the hexadecimal representation of each character.
583
584 @type width: int
585 @param width:
586 (Optional) Maximum number of characters to convert per text line.
587
588 @rtype: str
589 @return: Multiline output text.
590 """
591 return cls.hexblock_cb(cls.hexline, data, address, width,
592 cb_kwargs = {'width' : width, 'separator' : separator})
593
594 @classmethod
595 - def hexblock_cb(cls, callback, data, address = None, width = 16,
596 cb_args = (), cb_kwargs = {}):
597 """
598 Dump a block of binary data using a callback function to convert each
599 line of text.
600
601 @type callback: function
602 @param callback: Callback function to convert each line of data.
603
604 @type data: str
605 @param data: Binary data.
606
607 @type address: str
608 @param address:
609 (Optional) Memory address where the data was read from.
610
611 @type cb_args: str
612 @param cb_args:
613 (Optional) Arguments to pass to the callback function.
614
615 @type cb_kwargs: str
616 @param cb_kwargs:
617 (Optional) Keyword arguments to pass to the callback function.
618
619 @type width: int
620 @param width:
621 (Optional) Maximum number of bytes to convert per text line.
622
623 @rtype: str
624 @return: Multiline output text.
625 """
626 result = ''
627 if address is None:
628 for i in xrange(0, len(data), width):
629 result = '%s%s\n' % ( result, \
630 callback(data[i:i+width], *cb_args, **cb_kwargs) )
631 else:
632 for i in xrange(0, len(data), width):
633 result = '%s%s: %s\n' % ( result, cls.address(address), \
634 callback(data[i:i+width], *cb_args, **cb_kwargs) )
635 address += width
636 return result
637
638 @classmethod
639 - def hexblock_byte(cls, data, address = None, separator = ' ', width = 16):
640 """
641 Dump a block of hexadecimal BYTEs from binary data.
642
643 @type data: str
644 @param data: Binary data.
645
646 @type address: str
647 @param address: Memory address where the data was read from.
648
649 @type separator: str
650 @param separator:
651 Separator between the hexadecimal representation of each BYTE.
652
653 @type width: int
654 @param width:
655 (Optional) Maximum number of BYTEs to convert per text line.
656
657 @rtype: str
658 @return: Multiline output text.
659 """
660 return cls.hexblock_cb(cls.hexadecimal, data, address, width,
661 cb_kwargs = {'separator': separator})
662
663 @classmethod
664 - def hexblock_word(cls, data, address = None, separator = ' ', width = 8):
665 """
666 Dump a block of hexadecimal WORDs from binary data.
667
668 @type data: str
669 @param data: Binary data.
670
671 @type address: str
672 @param address: Memory address where the data was read from.
673
674 @type separator: str
675 @param separator:
676 Separator between the hexadecimal representation of each WORD.
677
678 @type width: int
679 @param width:
680 (Optional) Maximum number of WORDs to convert per text line.
681
682 @rtype: str
683 @return: Multiline output text.
684 """
685 return cls.hexblock_cb(cls.hexa_word, data, address, width * 2,
686 cb_kwargs = {'separator': separator})
687
688 @classmethod
689 - def hexblock_dword(cls, data, address = None, separator = ' ', width = 4):
690 """
691 Dump a block of hexadecimal DWORDs from binary data.
692
693 @type data: str
694 @param data: Binary data.
695
696 @type address: str
697 @param address: Memory address where the data was read from.
698
699 @type separator: str
700 @param separator:
701 Separator between the hexadecimal representation of each DWORD.
702
703 @type width: int
704 @param width:
705 (Optional) Maximum number of DWORDs to convert per text line.
706
707 @rtype: str
708 @return: Multiline output text.
709 """
710 return cls.hexblock_cb(cls.hexa_dword, data, address, width * 4,
711 cb_kwargs = {'separator': separator})
712
713 @classmethod
714 - def hexblock_qword(cls, data, address = None, separator = ' ', width = 2):
715 """
716 Dump a block of hexadecimal QWORDs from binary data.
717
718 @type data: str
719 @param data: Binary data.
720
721 @type address: str
722 @param address: Memory address where the data was read from.
723
724 @type separator: str
725 @param separator:
726 Separator between the hexadecimal representation of each QWORD.
727
728 @type width: int
729 @param width:
730 (Optional) Maximum number of QWORDs to convert per text line.
731
732 @rtype: str
733 @return: Multiline output text.
734 """
735 return cls.hexblock_cb(cls.hexa_qword, data, address, width * 8,
736 cb_kwargs = {'separator': separator})
737
738
739
740 -class Table (object):
741 """
742 Text based table. The number of columns and the width of each column
743 is automatically calculated.
744 """
745
747 """
748 @type sep: str
749 @param sep: Separator between cells in each row.
750 """
751 self.__cols = list()
752 self.__width = list()
753 self.__sep = sep
754
756 """
757 Add a row to the table. All items are converted to strings.
758
759 @type row: tuple
760 @keyword row: Each argument is a cell in the table.
761 """
762 row = [ str(item) for item in row ]
763 len_row = [ len(item) for item in row ]
764 width = self.__width
765 len_old = len(width)
766 len_new = len(row)
767 known = min(len_old, len_new)
768 missing = len_new - len_old
769 if missing > 0:
770 width.extend( len_row[ -missing : ] )
771 self.__width = [ max( width[i], len_row[i] ) for i in xrange(len_new) ]
772 self.__cols.append(row)
773
774 - def justify(self, column, direction):
775 """
776 Make the text in a column left or right justified.
777
778 @type column: int
779 @param column: Index of the column.
780
781 @type direction: int
782 @param direction:
783 C{1} to justify left,
784 C{-1} to justify right.
785
786 @raise IndexError: Bad column index.
787 @raise ValueError: Bad direction value.
788 """
789 if direction == -1:
790 self.__width[column] = abs(self.__width[column])
791 elif direction == 1:
792 self.__width[column] = - abs(self.__width[column])
793 else:
794 raise ValueError, "Bad direction value."
795
797 """
798 Get the text output for the table.
799
800 @rtype: str
801 @return: Text output.
802 """
803 return '%s\n' % '\n'.join( self.yieldOutput() )
804
806 """
807 Generate the text output for the table.
808
809 @rtype: generator of str
810 @return: Text output.
811 """
812 width = self.__width
813 if width:
814 num_cols = len(width)
815 fmt = ['%%%ds' % -w for w in width]
816 if width[-1] > 0:
817 fmt[-1] = '%s'
818 fmt = self.__sep.join(fmt)
819 for row in self.__cols:
820 row.extend( [''] * (num_cols - len(row)) )
821 yield fmt % tuple(row)
822
826 """
827 Static functions for crash dumps.
828
829 @type reg_template: str
830 @cvar reg_template: Template for the L{dump_registers} method.
831 """
832 @classmethod
834 'Don\'t try to instance this class, it\'s just a namespace!'
835 raise NotImplementedError
836
837
838 reg_template = {
839 'i386' : (
840 'eax=%(Eax).8x ebx=%(Ebx).8x ecx=%(Ecx).8x edx=%(Edx).8x esi=%(Esi).8x edi=%(Edi).8x\n'
841 'eip=%(Eip).8x esp=%(Esp).8x ebp=%(Ebp).8x %(efl_dump)s\n'
842 'cs=%(SegCs).4x ss=%(SegSs).4x ds=%(SegDs).4x es=%(SegEs).4x fs=%(SegFs).4x gs=%(SegGs).4x efl=%(EFlags).8x\n'
843 ),
844 'amd64' : (
845 'rax=%(Rax).16x rbx=%(Rbx).16x rcx=%(Rcx).16x\n'
846 'rdx=%(Rdx).16x rsi=%(Rsi).16x rdi=%(Rdi).16x\n'
847 'rip=%(Rip).16x rsp=%(Rsp).16x rbp=%(Rbp).16x\n'
848 ' r8=%(R8).16x r9=%(R9).16x r10=%(R10).16x\n'
849 'r11=%(R11).16x r12=%(R12).16x r13=%(R13).16x\n'
850 'r14=%(R14).16x r15=%(R15).16x\n'
851 '%(efl_dump)s\n'
852 'cs=%(SegCs).4x ss=%(SegSs).4x ds=%(SegDs).4x es=%(SegEs).4x fs=%(SegFs).4x gs=%(SegGs).4x efl=%(EFlags).8x\n'
853 ),
854 }
855
856 @staticmethod
858 """
859 Dump the x86 processor flags.
860 The output mimics that of the WinDBG debugger.
861
862 @type efl: int
863 @param efl: Value of the eFlags register.
864
865 @rtype: str
866 @return: Text suitable for logging.
867 """
868 if efl is None:
869 return ''
870 if win32.CONTEXT.arch not in ('i386', 'amd64'):
871 raise NotImplementedError
872 efl_dump = 'iopl=%1d' % ((efl & 0x3000) >> 12)
873 if efl & 0x100000:
874 efl_dump += ' vip'
875 else:
876 efl_dump += ' '
877 if efl & 0x80000:
878 efl_dump += ' vif'
879 else:
880 efl_dump += ' '
881
882 if efl & 0x800:
883 efl_dump += ' ov'
884 else:
885 efl_dump += ' no'
886 if efl & 0x400:
887 efl_dump += ' dn'
888 else:
889 efl_dump += ' up'
890 if efl & 0x200:
891 efl_dump += ' ei'
892 else:
893 efl_dump += ' di'
894
895 if efl & 0x80:
896 efl_dump += ' ng'
897 else:
898 efl_dump += ' pl'
899 if efl & 0x40:
900 efl_dump += ' zr'
901 else:
902 efl_dump += ' nz'
903 if efl & 0x10:
904 efl_dump += ' ac'
905 else:
906 efl_dump += ' na'
907
908 if efl & 0x4:
909 efl_dump += ' pe'
910 else:
911 efl_dump += ' po'
912
913 if efl & 0x1:
914 efl_dump += ' cy'
915 else:
916 efl_dump += ' nc'
917 return efl_dump
918
919 @classmethod
921 """
922 Dump the x86 processor register values.
923 The output mimics that of the WinDBG debugger.
924
925 @type registers: dict( str S{->} int )
926 @param registers: Dictionary mapping register names to their values.
927
928 @rtype: str
929 @return: Text suitable for logging.
930 """
931 if registers is None:
932 return ''
933 arch = win32.CONTEXT.arch
934 if arch not in ('i386', 'amd64'):
935 raise NotImplementedError
936 registers = registers.copy()
937 registers['efl_dump'] = cls.dump_flags( registers['EFlags'] )
938 return cls.reg_template[arch] % registers
939
940 @staticmethod
942 """
943 Dump data pointed to by the given registers, if any.
944
945 @type registers: dict( str S{->} int )
946 @param registers: Dictionary mapping register names to their values.
947 This value is returned by L{Thread.get_context}.
948
949 @type data: dict( str S{->} str )
950 @param data: Dictionary mapping register names to the data they point to.
951 This value is returned by L{Thread.peek_pointers_in_registers}.
952
953 @rtype: str
954 @return: Text suitable for logging.
955 """
956 if None in (registers, data):
957 return ''
958 names = data.keys()
959 names.sort()
960 result = ''
961 for reg_name in names:
962 tag = reg_name.lower()
963 dumped = HexDump.hexline(data[reg_name], separator, width)
964 result += '%s -> %s\n' % (tag, dumped)
965 return result
966
967 @staticmethod
969 """
970 Dump data from pointers guessed within the given binary data.
971
972 @type data: str
973 @param data: Dictionary mapping offsets to the data they point to.
974
975 @type base: int
976 @param base: Base offset.
977
978 @rtype: str
979 @return: Text suitable for logging.
980 """
981 if data is None:
982 return ''
983 pointers = data.keys()
984 pointers.sort()
985 result = ''
986 for offset in pointers:
987 dumped = HexDump.hexline(data[offset], separator, width)
988 result += '%s -> %s' % (HexDump.address(base + offset), dumped)
989 return result
990
991 @staticmethod
993 """
994 Dump data from pointers guessed within the given stack dump.
995
996 @type data: str
997 @param data: Dictionary mapping stack offsets to the data they point to.
998
999 @rtype: str
1000 @return: Text suitable for logging.
1001 """
1002 if data is None:
1003 return ''
1004 pointers = data.keys()
1005 pointers.sort()
1006 result = ''
1007 if pointers:
1008 if win32.CONTEXT.arch == 'i386':
1009 spreg = 'esp'
1010 elif win32.CONTEXT.arch == 'i386':
1011 spreg = 'rsp'
1012 else:
1013 spreg = 'STACK'
1014 tag_fmt = '[%s+0x%%.%dx]' % (spreg, len( '%x' % pointers[-1] ) )
1015 for offset in pointers:
1016 dumped = HexDump.hexline(data[offset], separator, width)
1017 tag = tag_fmt % offset
1018 result += '%s -> %s\n' % (tag, dumped)
1019 return result
1020
1021 @staticmethod
1023 """
1024 Dump a stack trace, as returned by L{Thread.get_stack_trace} with the
1025 C{bUseLabels} parameter set to C{False}.
1026
1027 @type stack_trace: list( int, int, str )
1028 @param stack_trace: Stack trace as a list of tuples of
1029 ( return address, frame pointer, module filename )
1030
1031 @rtype: str
1032 @return: Text suitable for logging.
1033 """
1034 if stack_trace is None:
1035 return ''
1036 table = Table()
1037 table.addRow('Frame', 'Origin', 'Module')
1038 for (fp, ra, mod) in stack_trace:
1039 table.addRow( HexDump.address(fp), HexDump.address(ra), mod )
1040 return table.getOutput()
1041
1042 @staticmethod
1044 """
1045 Dump a stack trace,
1046 as returned by L{Thread.get_stack_trace_with_labels}.
1047
1048 @type stack_trace: list( int, int, str )
1049 @param stack_trace: Stack trace as a list of tuples of
1050 ( return address, frame pointer, module filename )
1051
1052 @rtype: str
1053 @return: Text suitable for logging.
1054 """
1055 if stack_trace is None:
1056 return ''
1057 table = Table()
1058 table.addRow('Frame', 'Origin')
1059 for (fp, label) in stack_trace:
1060 table.addRow( HexDump.address(fp), label )
1061 return table.getOutput()
1062
1063
1064
1065
1066
1067
1068
1069 @staticmethod
1070 - def dump_code(disassembly, pc = None, bLowercase = True):
1071 """
1072 Dump a disassembly. Optionally mark where the program counter is.
1073
1074 @type disassembly: list of tuple( int, int, str, str )
1075 @param disassembly: Disassembly dump as returned by
1076 L{Process.disassemble} or L{Thread.disassemble_around_pc}.
1077
1078 @type pc: int
1079 @param pc: (Optional) Program counter.
1080
1081 @type bLowercase: bool
1082 @param bLowercase: (Optional) If C{True} convert the code to lowercase.
1083
1084 @rtype: str
1085 @return: Text suitable for logging.
1086 """
1087 if disassembly is None:
1088 return ''
1089 table = Table(sep = ' | ')
1090 for (addr, size, code, dump) in disassembly:
1091 if bLowercase:
1092 code = code.lower()
1093 if addr == pc:
1094 addr = ' * %s' % HexDump.address(addr)
1095 else:
1096 addr = ' %s' % HexDump.address(addr)
1097 table.addRow(addr, dump, code)
1098 table.justify(1, 1)
1099 return table.getOutput()
1100
1101 @staticmethod
1102 - def dump_code_line(disassembly_line, bShowAddress = True,
1103 bShowDump = True,
1104 bLowercase = True,
1105 dwDumpWidth = None,
1106 dwCodeWidth = None):
1107 """
1108 Dump a single line of code. To dump a block of code use L{dump_code}.
1109
1110 @type disassembly_line: tuple( int, int, str, str )
1111 @param disassembly_line: Single item of the list returned by
1112 L{Process.disassemble} or L{Thread.disassemble_around_pc}.
1113
1114 @type bShowAddress: bool
1115 @param bShowAddress: (Optional) If C{True} show the memory address.
1116
1117 @type bShowDump: bool
1118 @param bShowDump: (Optional) If C{True} show the hexadecimal dump.
1119
1120 @type bLowercase: bool
1121 @param bLowercase: (Optional) If C{True} convert the code to lowercase.
1122
1123 @type dwDumpWidth: int or None
1124 @param dwDumpWidth: (Optional) Width in characters of the hex dump.
1125
1126 @type dwCodeWidth: int or None
1127 @param dwCodeWidth: (Optional) Width in characters of the code.
1128
1129 @rtype: str
1130 @return: Text suitable for logging.
1131 """
1132 (addr, size, code, dump) = disassembly_line
1133 dump = dump.replace(' ', '')
1134 result = list()
1135 fmt = ''
1136 if bShowAddress:
1137 result.append( HexDump.address(addr) )
1138 fmt += '%%%ds:' % HexDump.address_size
1139 if bShowDump:
1140 result.append(dump)
1141 if dwDumpWidth:
1142 fmt += ' %%-%ds' % dwDumpWidth
1143 else:
1144 fmt += ' %s'
1145 if bLowercase:
1146 code = code.lower()
1147 result.append(code)
1148 if dwCodeWidth:
1149 fmt += ' %%-%ds' % dwCodeWidth
1150 else:
1151 fmt += ' %s'
1152 return fmt % tuple(result)
1153
1154 @staticmethod
1156 """
1157 Dump the memory map of a process. Optionally show the filenames for
1158 memory mapped files as well.
1159
1160 @type memoryMap: list( L{win32.MemoryBasicInformation} )
1161 @param memoryMap: Memory map returned by L{Process.get_memory_map}.
1162
1163 @type mappedFilenames: dict( int S{->} str )
1164 @param mappedFilenames: (Optional) Memory mapped filenames
1165 returned by L{Process.get_mapped_filenames}.
1166
1167 @rtype: str
1168 @return: Text suitable for logging.
1169 """
1170 table = Table()
1171 if mappedFilenames:
1172 table.addRow("Address", "Size", "State", "Access", "Type", "File")
1173 else:
1174 table.addRow("Address", "Size", "State", "Access", "Type")
1175
1176
1177 for mbi in memoryMap:
1178
1179
1180 BaseAddress = HexDump.address(mbi.BaseAddress)
1181 RegionSize = HexDump.address(mbi.RegionSize)
1182
1183
1184 mbiState = mbi.State
1185 if mbiState == win32.MEM_RESERVE:
1186 State = "Reserved"
1187 elif mbiState == win32.MEM_COMMIT:
1188 State = "Commited"
1189 elif mbiState == win32.MEM_FREE:
1190 State = "Free"
1191 else:
1192 State = "Unknown"
1193
1194
1195 if mbiState != win32.MEM_COMMIT:
1196 Protect = ""
1197 else:
1198 mbiProtect = mbi.Protect
1199 if mbiProtect & win32.PAGE_NOACCESS:
1200 Protect = "--- "
1201 elif mbiProtect & win32.PAGE_READONLY:
1202 Protect = "R-- "
1203 elif mbiProtect & win32.PAGE_READWRITE:
1204 Protect = "RW- "
1205 elif mbiProtect & win32.PAGE_WRITECOPY:
1206 Protect = "RC- "
1207 elif mbiProtect & win32.PAGE_EXECUTE:
1208 Protect = "--X "
1209 elif mbiProtect & win32.PAGE_EXECUTE_READ:
1210 Protect = "R-X "
1211 elif mbiProtect & win32.PAGE_EXECUTE_READWRITE:
1212 Protect = "RWX "
1213 elif mbiProtect & win32.PAGE_EXECUTE_WRITECOPY:
1214 Protect = "RCX "
1215 else:
1216 Protect = "??? "
1217 if mbiProtect & win32.PAGE_GUARD:
1218 Protect += "G"
1219 else:
1220 Protect += "-"
1221 if mbiProtect & win32.PAGE_NOCACHE:
1222 Protect += "N"
1223 else:
1224 Protect += "-"
1225 if mbiProtect & win32.PAGE_WRITECOMBINE:
1226 Protect += "W"
1227 else:
1228 Protect += "-"
1229
1230
1231 mbiType = mbi.Type
1232 if mbiType == win32.MEM_IMAGE:
1233 Type = "Image"
1234 elif mbiType == win32.MEM_MAPPED:
1235 Type = "Mapped"
1236 elif mbiType == win32.MEM_PRIVATE:
1237 Type = "Private"
1238 elif mbiType == 0:
1239 Type = ""
1240 else:
1241 Type = "Unknown"
1242
1243
1244 if mappedFilenames:
1245 FileName = mappedFilenames.get(mbi.BaseAddress, '')
1246 table.addRow( BaseAddress, RegionSize, State, Protect, Type, FileName )
1247 else:
1248 table.addRow( BaseAddress, RegionSize, State, Protect, Type )
1249
1250
1251 return table.getOutput()
1252
1256 'Static functions for debug logging.'
1257 @classmethod
1259 'Don\'t try to instance this class, it\'s just a namespace!'
1260 raise NotImplementedError
1261
1262 @staticmethod
1263 - def log_text(text):
1264 """
1265 Log lines of text, inserting a timestamp.
1266
1267 @type text: str
1268 @param text: Text to log.
1269
1270 @rtype: str
1271 @return: Log line.
1272 """
1273 if text.endswith('\n'):
1274 text = text[:-len('\n')]
1275
1276 ltime = time.strftime("%X")
1277 msecs = (time.time() % 1) * 1000
1278 return '[%s.%04d] %s' % (ltime, msecs, text)
1279
1280
1281 @classmethod
1319
1320
1321
1322 -class Logger(object):
1323 """
1324 Logs text to standard output and/or a text file.
1325
1326 @type logfile: str or None
1327 @ivar logfile: Append messahes to this text file.
1328
1329 @type verbose: bool
1330 @ivar verbose: C{True} to print messages to standard output.
1331
1332 @type fd: file
1333 @ivar fd: File object where log messages are printed to.
1334 C{None} if no log file is used.
1335 """
1336
1337 - def __init__(self, logfile = None, verbose = True):
1338 """
1339 @type logfile: str or None
1340 @param logfile: Append messahes to this text file.
1341
1342 @type verbose: bool
1343 @param verbose: C{True} to print messages to standard output.
1344 """
1345 self.verbose = verbose
1346 self.logfile = logfile
1347 if self.logfile:
1348 self.fd = open(self.logfile, 'a+')
1349
1351 """
1352 Shows an error message to standard error
1353 if the log file can't be written to.
1354
1355 Used internally.
1356
1357 @type e: Exception
1358 @param e: Exception raised when trying to write to the log file.
1359 """
1360 from sys import stderr
1361 msg = "Warning, error writing log file %s: %s\n"
1362 msg = msg % (self.logfile, str(e))
1363 stderr.write(DebugLog.log_text(msg))
1364 self.logfile = None
1365 self.fd = None
1366
1368 """
1369 Writes the given text verbatim into the log file (if any)
1370 and/or standard input (if the verbose flag is turned on).
1371
1372 Used internally.
1373
1374 @type text: str
1375 @param text: Text to print.
1376 """
1377 if self.verbose:
1378 print text
1379 if self.logfile:
1380 try:
1381 self.fd.writelines('%s\n' % text)
1382 except IOError, e:
1383 self.__logfile_error(e)
1384
1385 - def log_text(self, text):
1386 """
1387 Log lines of text, inserting a timestamp.
1388
1389 @type text: str
1390 @param text: Text to log.
1391 """
1392 self.__do_log( DebugLog.log_text(text) )
1393
1395 """
1396 Log lines of text associated with a debug event.
1397
1398 @type event: L{Event}
1399 @param event: Event object.
1400
1401 @type text: str
1402 @param text: (Optional) Text to log. If no text is provided the default
1403 is to show a description of the event itself.
1404 """
1405 self.__do_log( DebugLog.log_event(event, text) )
1406
1408 """
1409 Log lines of text associated with the last Python exception.
1410 """
1411 self.__do_log( 'Exception raised: %s' % traceback.format_exc() )
1412