1#!~/.wine/drive_c/Python25/python.exe
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2009-2014, Mario Vilas
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are met:
9#
10#     * Redistributions of source code must retain the above copyright notice,
11#       this list of conditions and the following disclaimer.
12#     * Redistributions in binary form must reproduce the above copyright
13#       notice,this list of conditions and the following disclaimer in the
14#       documentation and/or other materials provided with the distribution.
15#     * Neither the name of the copyright holder nor the names of its
16#       contributors may be used to endorse or promote products derived from
17#       this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29# POSSIBILITY OF SUCH DAMAGE.
30
31"""
32Functions for text input, logging or text output.
33
34@group Helpers:
35    HexDump,
36    HexInput,
37    HexOutput,
38    Color,
39    Table,
40    Logger
41    DebugLog
42    CrashDump
43"""
44
45__revision__ = "$Id$"
46
47__all__ =   [
48                'HexDump',
49                'HexInput',
50                'HexOutput',
51                'Color',
52                'Table',
53                'CrashDump',
54                'DebugLog',
55                'Logger',
56            ]
57
58import sys
59from winappdbg import win32
60from winappdbg import compat
61from winappdbg.util import StaticClass
62
63import re
64import time
65import struct
66import traceback
67
68#------------------------------------------------------------------------------
69
70class HexInput (StaticClass):
71    """
72    Static functions for user input parsing.
73    The counterparts for each method are in the L{HexOutput} class.
74    """
75
76    @staticmethod
77    def integer(token):
78        """
79        Convert numeric strings into integers.
80
81        @type  token: str
82        @param token: String to parse.
83
84        @rtype:  int
85        @return: Parsed integer value.
86        """
87        token = token.strip()
88        neg = False
89        if token.startswith(compat.b('-')):
90            token = token[1:]
91            neg = True
92        if token.startswith(compat.b('0x')):
93            result = int(token, 16)     # hexadecimal
94        elif token.startswith(compat.b('0b')):
95            result = int(token[2:], 2)  # binary
96        elif token.startswith(compat.b('0o')):
97            result = int(token, 8)      # octal
98        else:
99            try:
100                result = int(token)     # decimal
101            except ValueError:
102                result = int(token, 16) # hexadecimal (no "0x" prefix)
103        if neg:
104            result = -result
105        return result
106
107    @staticmethod
108    def address(token):
109        """
110        Convert numeric strings into memory addresses.
111
112        @type  token: str
113        @param token: String to parse.
114
115        @rtype:  int
116        @return: Parsed integer value.
117        """
118        return int(token, 16)
119
120    @staticmethod
121    def hexadecimal(token):
122        """
123        Convert a strip of hexadecimal numbers into binary data.
124
125        @type  token: str
126        @param token: String to parse.
127
128        @rtype:  str
129        @return: Parsed string value.
130        """
131        token = ''.join([ c for c in token if c.isalnum() ])
132        if len(token) % 2 != 0:
133            raise ValueError("Missing characters in hex data")
134        data = ''
135        for i in compat.xrange(0, len(token), 2):
136            x = token[i:i+2]
137            d = int(x, 16)
138            s = struct.pack('<B', d)
139            data += s
140        return data
141
142    @staticmethod
143    def pattern(token):
144        """
145        Convert an hexadecimal search pattern into a POSIX regular expression.
146
147        For example, the following pattern::
148
149            "B8 0? ?0 ?? ??"
150
151        Would match the following data::
152
153            "B8 0D F0 AD BA"    # mov eax, 0xBAADF00D
154
155        @type  token: str
156        @param token: String to parse.
157
158        @rtype:  str
159        @return: Parsed string value.
160        """
161        token = ''.join([ c for c in token if c == '?' or c.isalnum() ])
162        if len(token) % 2 != 0:
163            raise ValueError("Missing characters in hex data")
164        regexp = ''
165        for i in compat.xrange(0, len(token), 2):
166            x = token[i:i+2]
167            if x == '??':
168                regexp += '.'
169            elif x[0] == '?':
170                f = '\\x%%.1x%s' % x[1]
171                x = ''.join([ f % c for c in compat.xrange(0, 0x10) ])
172                regexp = '%s[%s]' % (regexp, x)
173            elif x[1] == '?':
174                f = '\\x%s%%.1x' % x[0]
175                x = ''.join([ f % c for c in compat.xrange(0, 0x10) ])
176                regexp = '%s[%s]' % (regexp, x)
177            else:
178                regexp = '%s\\x%s' % (regexp, x)
179        return regexp
180
181    @staticmethod
182    def is_pattern(token):
183        """
184        Determine if the given argument is a valid hexadecimal pattern to be
185        used with L{pattern}.
186
187        @type  token: str
188        @param token: String to parse.
189
190        @rtype:  bool
191        @return:
192            C{True} if it's a valid hexadecimal pattern, C{False} otherwise.
193        """
194        return re.match(r"^(?:[\?A-Fa-f0-9][\?A-Fa-f0-9]\s*)+$", token)
195
196    @classmethod
197    def integer_list_file(cls, filename):
198        """
199        Read a list of integers from a file.
200
201        The file format is:
202
203         - # anywhere in the line begins a comment
204         - leading and trailing spaces are ignored
205         - empty lines are ignored
206         - integers can be specified as:
207            - decimal numbers ("100" is 100)
208            - hexadecimal numbers ("0x100" is 256)
209            - binary numbers ("0b100" is 4)
210            - octal numbers ("0100" is 64)
211
212        @type  filename: str
213        @param filename: Name of the file to read.
214
215        @rtype:  list( int )
216        @return: List of integers read from the file.
217        """
218        count  = 0
219        result = list()
220        fd     = open(filename, 'r')
221        for line in fd:
222            count = count + 1
223            if '#' in line:
224                line = line[ : line.find('#') ]
225            line = line.strip()
226            if line:
227                try:
228                    value = cls.integer(line)
229                except ValueError:
230                    e = sys.exc_info()[1]
231                    msg = "Error in line %d of %s: %s"
232                    msg = msg % (count, filename, str(e))
233                    raise ValueError(msg)
234                result.append(value)
235        return result
236
237    @classmethod
238    def string_list_file(cls, filename):
239        """
240        Read a list of string values from a file.
241
242        The file format is:
243
244         - # anywhere in the line begins a comment
245         - leading and trailing spaces are ignored
246         - empty lines are ignored
247         - strings cannot span over a single line
248
249        @type  filename: str
250        @param filename: Name of the file to read.
251
252        @rtype:  list
253        @return: List of integers and strings read from the file.
254        """
255        count  = 0
256        result = list()
257        fd     = open(filename, 'r')
258        for line in fd:
259            count = count + 1
260            if '#' in line:
261                line = line[ : line.find('#') ]
262            line = line.strip()
263            if line:
264                result.append(line)
265        return result
266
267    @classmethod
268    def mixed_list_file(cls, filename):
269        """
270        Read a list of mixed values from a file.
271
272        The file format is:
273
274         - # anywhere in the line begins a comment
275         - leading and trailing spaces are ignored
276         - empty lines are ignored
277         - strings cannot span over a single line
278         - integers can be specified as:
279            - decimal numbers ("100" is 100)
280            - hexadecimal numbers ("0x100" is 256)
281            - binary numbers ("0b100" is 4)
282            - octal numbers ("0100" is 64)
283
284        @type  filename: str
285        @param filename: Name of the file to read.
286
287        @rtype:  list
288        @return: List of integers and strings read from the file.
289        """
290        count  = 0
291        result = list()
292        fd     = open(filename, 'r')
293        for line in fd:
294            count = count + 1
295            if '#' in line:
296                line = line[ : line.find('#') ]
297            line = line.strip()
298            if line:
299                try:
300                    value = cls.integer(line)
301                except ValueError:
302                    value = line
303                result.append(value)
304        return result
305
306#------------------------------------------------------------------------------
307
308class HexOutput (StaticClass):
309    """
310    Static functions for user output parsing.
311    The counterparts for each method are in the L{HexInput} class.
312
313    @type integer_size: int
314    @cvar integer_size: Default size in characters of an outputted integer.
315        This value is platform dependent.
316
317    @type address_size: int
318    @cvar address_size: Default Number of bits of the target architecture.
319        This value is platform dependent.
320    """
321
322    integer_size = (win32.SIZEOF(win32.DWORD)  * 2) + 2
323    address_size = (win32.SIZEOF(win32.SIZE_T) * 2) + 2
324
325    @classmethod
326    def integer(cls, integer, bits = None):
327        """
328        @type  integer: int
329        @param integer: Integer.
330
331        @type  bits: int
332        @param bits:
333            (Optional) Number of bits of the target architecture.
334            The default is platform dependent. See: L{HexOutput.integer_size}
335
336        @rtype:  str
337        @return: Text output.
338        """
339        if bits is None:
340            integer_size = cls.integer_size
341        else:
342            integer_size = (bits / 4) + 2
343        if integer >= 0:
344            return ('0x%%.%dx' % (integer_size - 2)) % integer
345        return ('-0x%%.%dx' % (integer_size - 2)) % -integer
346
347    @classmethod
348    def address(cls, address, bits = None):
349        """
350        @type  address: int
351        @param address: Memory address.
352
353        @type  bits: int
354        @param bits:
355            (Optional) Number of bits of the target architecture.
356            The default is platform dependent. See: L{HexOutput.address_size}
357
358        @rtype:  str
359        @return: Text output.
360        """
361        if bits is None:
362            address_size = cls.address_size
363            bits = win32.bits
364        else:
365            address_size = (bits / 4) + 2
366        if address < 0:
367            address = ((2 ** bits) - 1) ^ ~address
368        return ('0x%%.%dx' % (address_size - 2)) % address
369
370    @staticmethod
371    def hexadecimal(data):
372        """
373        Convert binary data to a string of hexadecimal numbers.
374
375        @type  data: str
376        @param data: Binary data.
377
378        @rtype:  str
379        @return: Hexadecimal representation.
380        """
381        return HexDump.hexadecimal(data, separator = '')
382
383    @classmethod
384    def integer_list_file(cls, filename, values, bits = None):
385        """
386        Write a list of integers to a file.
387        If a file of the same name exists, it's contents are replaced.
388
389        See L{HexInput.integer_list_file} for a description of the file format.
390
391        @type  filename: str
392        @param filename: Name of the file to write.
393
394        @type  values: list( int )
395        @param values: List of integers to write to the file.
396
397        @type  bits: int
398        @param bits:
399            (Optional) Number of bits of the target architecture.
400            The default is platform dependent. See: L{HexOutput.integer_size}
401        """
402        fd = open(filename, 'w')
403        for integer in values:
404            print >> fd, cls.integer(integer, bits)
405        fd.close()
406
407    @classmethod
408    def string_list_file(cls, filename, values):
409        """
410        Write a list of strings to a file.
411        If a file of the same name exists, it's contents are replaced.
412
413        See L{HexInput.string_list_file} for a description of the file format.
414
415        @type  filename: str
416        @param filename: Name of the file to write.
417
418        @type  values: list( int )
419        @param values: List of strings to write to the file.
420        """
421        fd = open(filename, 'w')
422        for string in values:
423            print >> fd, string
424        fd.close()
425
426    @classmethod
427    def mixed_list_file(cls, filename, values, bits):
428        """
429        Write a list of mixed values to a file.
430        If a file of the same name exists, it's contents are replaced.
431
432        See L{HexInput.mixed_list_file} for a description of the file format.
433
434        @type  filename: str
435        @param filename: Name of the file to write.
436
437        @type  values: list( int )
438        @param values: List of mixed values to write to the file.
439
440        @type  bits: int
441        @param bits:
442            (Optional) Number of bits of the target architecture.
443            The default is platform dependent. See: L{HexOutput.integer_size}
444        """
445        fd = open(filename, 'w')
446        for original in values:
447            try:
448                parsed = cls.integer(original, bits)
449            except TypeError:
450                parsed = repr(original)
451            print >> fd, parsed
452        fd.close()
453
454#------------------------------------------------------------------------------
455
456class HexDump (StaticClass):
457    """
458    Static functions for hexadecimal dumps.
459
460    @type integer_size: int
461    @cvar integer_size: Size in characters of an outputted integer.
462        This value is platform dependent.
463
464    @type address_size: int
465    @cvar address_size: Size in characters of an outputted address.
466        This value is platform dependent.
467    """
468
469    integer_size = (win32.SIZEOF(win32.DWORD)  * 2)
470    address_size = (win32.SIZEOF(win32.SIZE_T) * 2)
471
472    @classmethod
473    def integer(cls, integer, bits = None):
474        """
475        @type  integer: int
476        @param integer: Integer.
477
478        @type  bits: int
479        @param bits:
480            (Optional) Number of bits of the target architecture.
481            The default is platform dependent. See: L{HexDump.integer_size}
482
483        @rtype:  str
484        @return: Text output.
485        """
486        if bits is None:
487            integer_size = cls.integer_size
488        else:
489            integer_size = bits / 4
490        return ('%%.%dX' % integer_size) % integer
491
492    @classmethod
493    def address(cls, address, bits = None):
494        """
495        @type  address: int
496        @param address: Memory address.
497
498        @type  bits: int
499        @param bits:
500            (Optional) Number of bits of the target architecture.
501            The default is platform dependent. See: L{HexDump.address_size}
502
503        @rtype:  str
504        @return: Text output.
505        """
506        if bits is None:
507            address_size = cls.address_size
508            bits = win32.bits
509        else:
510            address_size = bits / 4
511        if address < 0:
512            address = ((2 ** bits) - 1) ^ ~address
513        return ('%%.%dX' % address_size) % address
514
515    @staticmethod
516    def printable(data):
517        """
518        Replace unprintable characters with dots.
519
520        @type  data: str
521        @param data: Binary data.
522
523        @rtype:  str
524        @return: Printable text.
525        """
526        result = ''
527        for c in data:
528            if 32 < ord(c) < 128:
529                result += c
530            else:
531                result += '.'
532        return result
533
534    @staticmethod
535    def hexadecimal(data, separator = ''):
536        """
537        Convert binary data to a string of hexadecimal numbers.
538
539        @type  data: str
540        @param data: Binary data.
541
542        @type  separator: str
543        @param separator:
544            Separator between the hexadecimal representation of each character.
545
546        @rtype:  str
547        @return: Hexadecimal representation.
548        """
549        return separator.join( [ '%.2x' % ord(c) for c in data ] )
550
551    @staticmethod
552    def hexa_word(data, separator = ' '):
553        """
554        Convert binary data to a string of hexadecimal WORDs.
555
556        @type  data: str
557        @param data: Binary data.
558
559        @type  separator: str
560        @param separator:
561            Separator between the hexadecimal representation of each WORD.
562
563        @rtype:  str
564        @return: Hexadecimal representation.
565        """
566        if len(data) & 1 != 0:
567            data += '\0'
568        return separator.join( [ '%.4x' % struct.unpack('<H', data[i:i+2])[0] \
569                                           for i in compat.xrange(0, len(data), 2) ] )
570
571    @staticmethod
572    def hexa_dword(data, separator = ' '):
573        """
574        Convert binary data to a string of hexadecimal DWORDs.
575
576        @type  data: str
577        @param data: Binary data.
578
579        @type  separator: str
580        @param separator:
581            Separator between the hexadecimal representation of each DWORD.
582
583        @rtype:  str
584        @return: Hexadecimal representation.
585        """
586        if len(data) & 3 != 0:
587            data += '\0' * (4 - (len(data) & 3))
588        return separator.join( [ '%.8x' % struct.unpack('<L', data[i:i+4])[0] \
589                                           for i in compat.xrange(0, len(data), 4) ] )
590
591    @staticmethod
592    def hexa_qword(data, separator = ' '):
593        """
594        Convert binary data to a string of hexadecimal QWORDs.
595
596        @type  data: str
597        @param data: Binary data.
598
599        @type  separator: str
600        @param separator:
601            Separator between the hexadecimal representation of each QWORD.
602
603        @rtype:  str
604        @return: Hexadecimal representation.
605        """
606        if len(data) & 7 != 0:
607            data += '\0' * (8 - (len(data) & 7))
608        return separator.join( [ '%.16x' % struct.unpack('<Q', data[i:i+8])[0]\
609                                           for i in compat.xrange(0, len(data), 8) ] )
610
611    @classmethod
612    def hexline(cls, data, separator = ' ', width = None):
613        """
614        Dump a line of hexadecimal numbers from binary data.
615
616        @type  data: str
617        @param data: Binary data.
618
619        @type  separator: str
620        @param separator:
621            Separator between the hexadecimal representation of each character.
622
623        @type  width: int
624        @param width:
625            (Optional) Maximum number of characters to convert per text line.
626            This value is also used for padding.
627
628        @rtype:  str
629        @return: Multiline output text.
630        """
631        if width is None:
632            fmt = '%s  %s'
633        else:
634            fmt = '%%-%ds  %%-%ds' % ((len(separator)+2)*width-1, width)
635        return fmt % (cls.hexadecimal(data, separator), cls.printable(data))
636
637    @classmethod
638    def hexblock(cls, data,                                    address = None,
639                                                                  bits = None,
640                                                             separator = ' ',
641                                                                 width = 8):
642        """
643        Dump a block of hexadecimal numbers from binary data.
644        Also show a printable text version of the data.
645
646        @type  data: str
647        @param data: Binary data.
648
649        @type  address: str
650        @param address: Memory address where the data was read from.
651
652        @type  bits: int
653        @param bits:
654            (Optional) Number of bits of the target architecture.
655            The default is platform dependent. See: L{HexDump.address_size}
656
657        @type  separator: str
658        @param separator:
659            Separator between the hexadecimal representation of each character.
660
661        @type  width: int
662        @param width:
663            (Optional) Maximum number of characters to convert per text line.
664
665        @rtype:  str
666        @return: Multiline output text.
667        """
668        return cls.hexblock_cb(cls.hexline, data, address, bits, width,
669                 cb_kwargs = {'width' : width, 'separator' : separator})
670
671    @classmethod
672    def hexblock_cb(cls, callback, data,                        address = None,
673                                                                   bits = None,
674                                                                  width = 16,
675                                                                cb_args = (),
676                                                              cb_kwargs = {}):
677        """
678        Dump a block of binary data using a callback function to convert each
679        line of text.
680
681        @type  callback: function
682        @param callback: Callback function to convert each line of data.
683
684        @type  data: str
685        @param data: Binary data.
686
687        @type  address: str
688        @param address:
689            (Optional) Memory address where the data was read from.
690
691        @type  bits: int
692        @param bits:
693            (Optional) Number of bits of the target architecture.
694            The default is platform dependent. See: L{HexDump.address_size}
695
696        @type  cb_args: str
697        @param cb_args:
698            (Optional) Arguments to pass to the callback function.
699
700        @type  cb_kwargs: str
701        @param cb_kwargs:
702            (Optional) Keyword arguments to pass to the callback function.
703
704        @type  width: int
705        @param width:
706            (Optional) Maximum number of bytes to convert per text line.
707
708        @rtype:  str
709        @return: Multiline output text.
710        """
711        result = ''
712        if address is None:
713            for i in compat.xrange(0, len(data), width):
714                result = '%s%s\n' % ( result, \
715                             callback(data[i:i+width], *cb_args, **cb_kwargs) )
716        else:
717            for i in compat.xrange(0, len(data), width):
718                result = '%s%s: %s\n' % (
719                             result,
720                             cls.address(address, bits),
721                             callback(data[i:i+width], *cb_args, **cb_kwargs)
722                             )
723                address += width
724        return result
725
726    @classmethod
727    def hexblock_byte(cls, data,                                address = None,
728                                                                   bits = None,
729                                                              separator = ' ',
730                                                                  width = 16):
731        """
732        Dump a block of hexadecimal BYTEs from binary data.
733
734        @type  data: str
735        @param data: Binary data.
736
737        @type  address: str
738        @param address: Memory address where the data was read from.
739
740        @type  bits: int
741        @param bits:
742            (Optional) Number of bits of the target architecture.
743            The default is platform dependent. See: L{HexDump.address_size}
744
745        @type  separator: str
746        @param separator:
747            Separator between the hexadecimal representation of each BYTE.
748
749        @type  width: int
750        @param width:
751            (Optional) Maximum number of BYTEs to convert per text line.
752
753        @rtype:  str
754        @return: Multiline output text.
755        """
756        return cls.hexblock_cb(cls.hexadecimal, data,
757                               address, bits, width,
758                               cb_kwargs = {'separator': separator})
759
760    @classmethod
761    def hexblock_word(cls, data,                                address = None,
762                                                                   bits = None,
763                                                              separator = ' ',
764                                                                  width = 8):
765        """
766        Dump a block of hexadecimal WORDs from binary data.
767
768        @type  data: str
769        @param data: Binary data.
770
771        @type  address: str
772        @param address: Memory address where the data was read from.
773
774        @type  bits: int
775        @param bits:
776            (Optional) Number of bits of the target architecture.
777            The default is platform dependent. See: L{HexDump.address_size}
778
779        @type  separator: str
780        @param separator:
781            Separator between the hexadecimal representation of each WORD.
782
783        @type  width: int
784        @param width:
785            (Optional) Maximum number of WORDs to convert per text line.
786
787        @rtype:  str
788        @return: Multiline output text.
789        """
790        return cls.hexblock_cb(cls.hexa_word, data,
791                               address, bits, width * 2,
792                               cb_kwargs = {'separator': separator})
793
794    @classmethod
795    def hexblock_dword(cls, data,                               address = None,
796                                                                   bits = None,
797                                                              separator = ' ',
798                                                                  width = 4):
799        """
800        Dump a block of hexadecimal DWORDs from binary data.
801
802        @type  data: str
803        @param data: Binary data.
804
805        @type  address: str
806        @param address: Memory address where the data was read from.
807
808        @type  bits: int
809        @param bits:
810            (Optional) Number of bits of the target architecture.
811            The default is platform dependent. See: L{HexDump.address_size}
812
813        @type  separator: str
814        @param separator:
815            Separator between the hexadecimal representation of each DWORD.
816
817        @type  width: int
818        @param width:
819            (Optional) Maximum number of DWORDs to convert per text line.
820
821        @rtype:  str
822        @return: Multiline output text.
823        """
824        return cls.hexblock_cb(cls.hexa_dword, data,
825                               address, bits, width * 4,
826                               cb_kwargs = {'separator': separator})
827
828    @classmethod
829    def hexblock_qword(cls, data,                               address = None,
830                                                                   bits = None,
831                                                              separator = ' ',
832                                                                  width = 2):
833        """
834        Dump a block of hexadecimal QWORDs from binary data.
835
836        @type  data: str
837        @param data: Binary data.
838
839        @type  address: str
840        @param address: Memory address where the data was read from.
841
842        @type  bits: int
843        @param bits:
844            (Optional) Number of bits of the target architecture.
845            The default is platform dependent. See: L{HexDump.address_size}
846
847        @type  separator: str
848        @param separator:
849            Separator between the hexadecimal representation of each QWORD.
850
851        @type  width: int
852        @param width:
853            (Optional) Maximum number of QWORDs to convert per text line.
854
855        @rtype:  str
856        @return: Multiline output text.
857        """
858        return cls.hexblock_cb(cls.hexa_qword, data,
859                               address, bits, width * 8,
860                               cb_kwargs = {'separator': separator})
861
862#------------------------------------------------------------------------------
863
864# TODO: implement an ANSI parser to simplify using colors
865
866class Color (object):
867    """
868    Colored console output.
869    """
870
871    @staticmethod
872    def _get_text_attributes():
873        return win32.GetConsoleScreenBufferInfo().wAttributes
874
875    @staticmethod
876    def _set_text_attributes(wAttributes):
877        win32.SetConsoleTextAttribute(wAttributes = wAttributes)
878
879    #--------------------------------------------------------------------------
880
881    @classmethod
882    def can_use_colors(cls):
883        """
884        Determine if we can use colors.
885
886        Colored output only works when the output is a real console, and fails
887        when redirected to a file or pipe. Call this method before issuing a
888        call to any other method of this class to make sure it's actually
889        possible to use colors.
890
891        @rtype:  bool
892        @return: C{True} if it's possible to output text with color,
893            C{False} otherwise.
894        """
895        try:
896            cls._get_text_attributes()
897            return True
898        except Exception:
899            return False
900
901    @classmethod
902    def reset(cls):
903        "Reset the colors to the default values."
904        cls._set_text_attributes(win32.FOREGROUND_GREY)
905
906    #--------------------------------------------------------------------------
907
908    #@classmethod
909    #def underscore(cls, on = True):
910    #    wAttributes = cls._get_text_attributes()
911    #    if on:
912    #        wAttributes |=  win32.COMMON_LVB_UNDERSCORE
913    #    else:
914    #        wAttributes &= ~win32.COMMON_LVB_UNDERSCORE
915    #    cls._set_text_attributes(wAttributes)
916
917    #--------------------------------------------------------------------------
918
919    @classmethod
920    def default(cls):
921        "Make the current foreground color the default."
922        wAttributes = cls._get_text_attributes()
923        wAttributes &= ~win32.FOREGROUND_MASK
924        wAttributes |=  win32.FOREGROUND_GREY
925        wAttributes &= ~win32.FOREGROUND_INTENSITY
926        cls._set_text_attributes(wAttributes)
927
928    @classmethod
929    def light(cls):
930        "Make the current foreground color light."
931        wAttributes = cls._get_text_attributes()
932        wAttributes |= win32.FOREGROUND_INTENSITY
933        cls._set_text_attributes(wAttributes)
934
935    @classmethod
936    def dark(cls):
937        "Make the current foreground color dark."
938        wAttributes = cls._get_text_attributes()
939        wAttributes &= ~win32.FOREGROUND_INTENSITY
940        cls._set_text_attributes(wAttributes)
941
942    @classmethod
943    def black(cls):
944        "Make the text foreground color black."
945        wAttributes = cls._get_text_attributes()
946        wAttributes &= ~win32.FOREGROUND_MASK
947        #wAttributes |=  win32.FOREGROUND_BLACK
948        cls._set_text_attributes(wAttributes)
949
950    @classmethod
951    def white(cls):
952        "Make the text foreground color white."
953        wAttributes = cls._get_text_attributes()
954        wAttributes &= ~win32.FOREGROUND_MASK
955        wAttributes |=  win32.FOREGROUND_GREY
956        cls._set_text_attributes(wAttributes)
957
958    @classmethod
959    def red(cls):
960        "Make the text foreground color red."
961        wAttributes = cls._get_text_attributes()
962        wAttributes &= ~win32.FOREGROUND_MASK
963        wAttributes |=  win32.FOREGROUND_RED
964        cls._set_text_attributes(wAttributes)
965
966    @classmethod
967    def green(cls):
968        "Make the text foreground color green."
969        wAttributes = cls._get_text_attributes()
970        wAttributes &= ~win32.FOREGROUND_MASK
971        wAttributes |=  win32.FOREGROUND_GREEN
972        cls._set_text_attributes(wAttributes)
973
974    @classmethod
975    def blue(cls):
976        "Make the text foreground color blue."
977        wAttributes = cls._get_text_attributes()
978        wAttributes &= ~win32.FOREGROUND_MASK
979        wAttributes |=  win32.FOREGROUND_BLUE
980        cls._set_text_attributes(wAttributes)
981
982    @classmethod
983    def cyan(cls):
984        "Make the text foreground color cyan."
985        wAttributes = cls._get_text_attributes()
986        wAttributes &= ~win32.FOREGROUND_MASK
987        wAttributes |=  win32.FOREGROUND_CYAN
988        cls._set_text_attributes(wAttributes)
989
990    @classmethod
991    def magenta(cls):
992        "Make the text foreground color magenta."
993        wAttributes = cls._get_text_attributes()
994        wAttributes &= ~win32.FOREGROUND_MASK
995        wAttributes |=  win32.FOREGROUND_MAGENTA
996        cls._set_text_attributes(wAttributes)
997
998    @classmethod
999    def yellow(cls):
1000        "Make the text foreground color yellow."
1001        wAttributes = cls._get_text_attributes()
1002        wAttributes &= ~win32.FOREGROUND_MASK
1003        wAttributes |=  win32.FOREGROUND_YELLOW
1004        cls._set_text_attributes(wAttributes)
1005
1006    #--------------------------------------------------------------------------
1007
1008    @classmethod
1009    def bk_default(cls):
1010        "Make the current background color the default."
1011        wAttributes = cls._get_text_attributes()
1012        wAttributes &= ~win32.BACKGROUND_MASK
1013        #wAttributes |= win32.BACKGROUND_BLACK
1014        wAttributes &= ~win32.BACKGROUND_INTENSITY
1015        cls._set_text_attributes(wAttributes)
1016
1017    @classmethod
1018    def bk_light(cls):
1019        "Make the current background color light."
1020        wAttributes = cls._get_text_attributes()
1021        wAttributes |= win32.BACKGROUND_INTENSITY
1022        cls._set_text_attributes(wAttributes)
1023
1024    @classmethod
1025    def bk_dark(cls):
1026        "Make the current background color dark."
1027        wAttributes = cls._get_text_attributes()
1028        wAttributes &= ~win32.BACKGROUND_INTENSITY
1029        cls._set_text_attributes(wAttributes)
1030
1031    @classmethod
1032    def bk_black(cls):
1033        "Make the text background color black."
1034        wAttributes = cls._get_text_attributes()
1035        wAttributes &= ~win32.BACKGROUND_MASK
1036        #wAttributes |= win32.BACKGROUND_BLACK
1037        cls._set_text_attributes(wAttributes)
1038
1039    @classmethod
1040    def bk_white(cls):
1041        "Make the text background color white."
1042        wAttributes = cls._get_text_attributes()
1043        wAttributes &= ~win32.BACKGROUND_MASK
1044        wAttributes |=  win32.BACKGROUND_GREY
1045        cls._set_text_attributes(wAttributes)
1046
1047    @classmethod
1048    def bk_red(cls):
1049        "Make the text background color red."
1050        wAttributes = cls._get_text_attributes()
1051        wAttributes &= ~win32.BACKGROUND_MASK
1052        wAttributes |=  win32.BACKGROUND_RED
1053        cls._set_text_attributes(wAttributes)
1054
1055    @classmethod
1056    def bk_green(cls):
1057        "Make the text background color green."
1058        wAttributes = cls._get_text_attributes()
1059        wAttributes &= ~win32.BACKGROUND_MASK
1060        wAttributes |=  win32.BACKGROUND_GREEN
1061        cls._set_text_attributes(wAttributes)
1062
1063    @classmethod
1064    def bk_blue(cls):
1065        "Make the text background color blue."
1066        wAttributes = cls._get_text_attributes()
1067        wAttributes &= ~win32.BACKGROUND_MASK
1068        wAttributes |=  win32.BACKGROUND_BLUE
1069        cls._set_text_attributes(wAttributes)
1070
1071    @classmethod
1072    def bk_cyan(cls):
1073        "Make the text background color cyan."
1074        wAttributes = cls._get_text_attributes()
1075        wAttributes &= ~win32.BACKGROUND_MASK
1076        wAttributes |=  win32.BACKGROUND_CYAN
1077        cls._set_text_attributes(wAttributes)
1078
1079    @classmethod
1080    def bk_magenta(cls):
1081        "Make the text background color magenta."
1082        wAttributes = cls._get_text_attributes()
1083        wAttributes &= ~win32.BACKGROUND_MASK
1084        wAttributes |=  win32.BACKGROUND_MAGENTA
1085        cls._set_text_attributes(wAttributes)
1086
1087    @classmethod
1088    def bk_yellow(cls):
1089        "Make the text background color yellow."
1090        wAttributes = cls._get_text_attributes()
1091        wAttributes &= ~win32.BACKGROUND_MASK
1092        wAttributes |=  win32.BACKGROUND_YELLOW
1093        cls._set_text_attributes(wAttributes)
1094
1095#------------------------------------------------------------------------------
1096
1097# TODO: another class for ASCII boxes
1098
1099class Table (object):
1100    """
1101    Text based table. The number of columns and the width of each column
1102    is automatically calculated.
1103    """
1104
1105    def __init__(self, sep = ' '):
1106        """
1107        @type  sep: str
1108        @param sep: Separator between cells in each row.
1109        """
1110        self.__cols  = list()
1111        self.__width = list()
1112        self.__sep   = sep
1113
1114    def addRow(self, *row):
1115        """
1116        Add a row to the table. All items are converted to strings.
1117
1118        @type    row: tuple
1119        @keyword row: Each argument is a cell in the table.
1120        """
1121        row     = [ str(item) for item in row ]
1122        len_row = [ len(item) for item in row ]
1123        width   = self.__width
1124        len_old = len(width)
1125        len_new = len(row)
1126        known   = min(len_old, len_new)
1127        missing = len_new - len_old
1128        if missing > 0:
1129            width.extend( len_row[ -missing : ] )
1130        elif missing < 0:
1131            len_row.extend( [0] * (-missing) )
1132        self.__width = [ max( width[i], len_row[i] ) for i in compat.xrange(len(len_row)) ]
1133        self.__cols.append(row)
1134
1135    def justify(self, column, direction):
1136        """
1137        Make the text in a column left or right justified.
1138
1139        @type  column: int
1140        @param column: Index of the column.
1141
1142        @type  direction: int
1143        @param direction:
1144            C{-1} to justify left,
1145            C{1} to justify right.
1146
1147        @raise IndexError: Bad column index.
1148        @raise ValueError: Bad direction value.
1149        """
1150        if direction == -1:
1151            self.__width[column] =   abs(self.__width[column])
1152        elif direction == 1:
1153            self.__width[column] = - abs(self.__width[column])
1154        else:
1155            raise ValueError("Bad direction value.")
1156
1157    def getWidth(self):
1158        """
1159        Get the width of the text output for the table.
1160
1161        @rtype:  int
1162        @return: Width in characters for the text output,
1163            including the newline character.
1164        """
1165        width = 0
1166        if self.__width:
1167            width = sum( abs(x) for x in self.__width )
1168            width = width + len(self.__width) * len(self.__sep) + 1
1169        return width
1170
1171    def getOutput(self):
1172        """
1173        Get the text output for the table.
1174
1175        @rtype:  str
1176        @return: Text output.
1177        """
1178        return '%s\n' % '\n'.join( self.yieldOutput() )
1179
1180    def yieldOutput(self):
1181        """
1182        Generate the text output for the table.
1183
1184        @rtype:  generator of str
1185        @return: Text output.
1186        """
1187        width = self.__width
1188        if width:
1189            num_cols = len(width)
1190            fmt = ['%%%ds' % -w for w in width]
1191            if width[-1] > 0:
1192                fmt[-1] = '%s'
1193            fmt = self.__sep.join(fmt)
1194            for row in self.__cols:
1195                row.extend( [''] * (num_cols - len(row)) )
1196                yield fmt % tuple(row)
1197
1198    def show(self):
1199        """
1200        Print the text output for the table.
1201        """
1202        print(self.getOutput())
1203
1204#------------------------------------------------------------------------------
1205
1206class CrashDump (StaticClass):
1207    """
1208    Static functions for crash dumps.
1209
1210    @type reg_template: str
1211    @cvar reg_template: Template for the L{dump_registers} method.
1212    """
1213
1214    # Templates for the dump_registers method.
1215    reg_template = {
1216        win32.ARCH_I386 : (
1217            'eax=%(Eax).8x ebx=%(Ebx).8x ecx=%(Ecx).8x edx=%(Edx).8x esi=%(Esi).8x edi=%(Edi).8x\n'
1218            'eip=%(Eip).8x esp=%(Esp).8x ebp=%(Ebp).8x %(efl_dump)s\n'
1219            'cs=%(SegCs).4x  ss=%(SegSs).4x  ds=%(SegDs).4x  es=%(SegEs).4x  fs=%(SegFs).4x  gs=%(SegGs).4x             efl=%(EFlags).8x\n'
1220            ),
1221        win32.ARCH_AMD64 : (
1222            'rax=%(Rax).16x rbx=%(Rbx).16x rcx=%(Rcx).16x\n'
1223            'rdx=%(Rdx).16x rsi=%(Rsi).16x rdi=%(Rdi).16x\n'
1224            'rip=%(Rip).16x rsp=%(Rsp).16x rbp=%(Rbp).16x\n'
1225            ' r8=%(R8).16x  r9=%(R9).16x r10=%(R10).16x\n'
1226            'r11=%(R11).16x r12=%(R12).16x r13=%(R13).16x\n'
1227            'r14=%(R14).16x r15=%(R15).16x\n'
1228            '%(efl_dump)s\n'
1229            'cs=%(SegCs).4x  ss=%(SegSs).4x  ds=%(SegDs).4x  es=%(SegEs).4x  fs=%(SegFs).4x  gs=%(SegGs).4x             efl=%(EFlags).8x\n'
1230            ),
1231    }
1232
1233    @staticmethod
1234    def dump_flags(efl):
1235        """
1236        Dump the x86 processor flags.
1237        The output mimics that of the WinDBG debugger.
1238        Used by L{dump_registers}.
1239
1240        @type  efl: int
1241        @param efl: Value of the eFlags register.
1242
1243        @rtype:  str
1244        @return: Text suitable for logging.
1245        """
1246        if efl is None:
1247            return ''
1248        efl_dump = 'iopl=%1d' % ((efl & 0x3000) >> 12)
1249        if efl & 0x100000:
1250            efl_dump += ' vip'
1251        else:
1252            efl_dump += '    '
1253        if efl & 0x80000:
1254            efl_dump += ' vif'
1255        else:
1256            efl_dump += '    '
1257        # 0x20000 ???
1258        if efl & 0x800:
1259            efl_dump += ' ov'       # Overflow
1260        else:
1261            efl_dump += ' no'       # No overflow
1262        if efl & 0x400:
1263            efl_dump += ' dn'       # Downwards
1264        else:
1265            efl_dump += ' up'       # Upwards
1266        if efl & 0x200:
1267            efl_dump += ' ei'       # Enable interrupts
1268        else:
1269            efl_dump += ' di'       # Disable interrupts
1270        # 0x100 trap flag
1271        if efl & 0x80:
1272            efl_dump += ' ng'       # Negative
1273        else:
1274            efl_dump += ' pl'       # Positive
1275        if efl & 0x40:
1276            efl_dump += ' zr'       # Zero
1277        else:
1278            efl_dump += ' nz'       # Nonzero
1279        if efl & 0x10:
1280            efl_dump += ' ac'       # Auxiliary carry
1281        else:
1282            efl_dump += ' na'       # No auxiliary carry
1283        # 0x8 ???
1284        if efl & 0x4:
1285            efl_dump += ' pe'       # Parity odd
1286        else:
1287            efl_dump += ' po'       # Parity even
1288        # 0x2 ???
1289        if efl & 0x1:
1290            efl_dump += ' cy'       # Carry
1291        else:
1292            efl_dump += ' nc'       # No carry
1293        return efl_dump
1294
1295    @classmethod
1296    def dump_registers(cls, registers, arch = None):
1297        """
1298        Dump the x86/x64 processor register values.
1299        The output mimics that of the WinDBG debugger.
1300
1301        @type  registers: dict( str S{->} int )
1302        @param registers: Dictionary mapping register names to their values.
1303
1304        @type  arch: str
1305        @param arch: Architecture of the machine whose registers were dumped.
1306            Defaults to the current architecture.
1307            Currently only the following architectures are supported:
1308             - L{win32.ARCH_I386}
1309             - L{win32.ARCH_AMD64}
1310
1311        @rtype:  str
1312        @return: Text suitable for logging.
1313        """
1314        if registers is None:
1315            return ''
1316        if arch is None:
1317            if 'Eax' in registers:
1318                arch = win32.ARCH_I386
1319            elif 'Rax' in registers:
1320                arch = win32.ARCH_AMD64
1321            else:
1322                arch = 'Unknown'
1323        if arch not in cls.reg_template:
1324            msg = "Don't know how to dump the registers for architecture: %s"
1325            raise NotImplementedError(msg % arch)
1326        registers = registers.copy()
1327        registers['efl_dump'] = cls.dump_flags( registers['EFlags'] )
1328        return cls.reg_template[arch] % registers
1329
1330    @staticmethod
1331    def dump_registers_peek(registers, data, separator = ' ', width = 16):
1332        """
1333        Dump data pointed to by the given registers, if any.
1334
1335        @type  registers: dict( str S{->} int )
1336        @param registers: Dictionary mapping register names to their values.
1337            This value is returned by L{Thread.get_context}.
1338
1339        @type  data: dict( str S{->} str )
1340        @param data: Dictionary mapping register names to the data they point to.
1341            This value is returned by L{Thread.peek_pointers_in_registers}.
1342
1343        @rtype:  str
1344        @return: Text suitable for logging.
1345        """
1346        if None in (registers, data):
1347            return ''
1348        names = compat.keys(data)
1349        names.sort()
1350        result = ''
1351        for reg_name in names:
1352            tag     = reg_name.lower()
1353            dumped  = HexDump.hexline(data[reg_name], separator, width)
1354            result += '%s -> %s\n' % (tag, dumped)
1355        return result
1356
1357    @staticmethod
1358    def dump_data_peek(data,                                      base = 0,
1359                                                             separator = ' ',
1360                                                                 width = 16,
1361                                                                  bits = None):
1362        """
1363        Dump data from pointers guessed within the given binary data.
1364
1365        @type  data: str
1366        @param data: Dictionary mapping offsets to the data they point to.
1367
1368        @type  base: int
1369        @param base: Base offset.
1370
1371        @type  bits: int
1372        @param bits:
1373            (Optional) Number of bits of the target architecture.
1374            The default is platform dependent. See: L{HexDump.address_size}
1375
1376        @rtype:  str
1377        @return: Text suitable for logging.
1378        """
1379        if data is None:
1380            return ''
1381        pointers = compat.keys(data)
1382        pointers.sort()
1383        result = ''
1384        for offset in pointers:
1385            dumped  = HexDump.hexline(data[offset], separator, width)
1386            address = HexDump.address(base + offset, bits)
1387            result += '%s -> %s\n' % (address, dumped)
1388        return result
1389
1390    @staticmethod
1391    def dump_stack_peek(data, separator = ' ', width = 16, arch = None):
1392        """
1393        Dump data from pointers guessed within the given stack dump.
1394
1395        @type  data: str
1396        @param data: Dictionary mapping stack offsets to the data they point to.
1397
1398        @type  separator: str
1399        @param separator:
1400            Separator between the hexadecimal representation of each character.
1401
1402        @type  width: int
1403        @param width:
1404            (Optional) Maximum number of characters to convert per text line.
1405            This value is also used for padding.
1406
1407        @type  arch: str
1408        @param arch: Architecture of the machine whose registers were dumped.
1409            Defaults to the current architecture.
1410
1411        @rtype:  str
1412        @return: Text suitable for logging.
1413        """
1414        if data is None:
1415            return ''
1416        if arch is None:
1417            arch = win32.arch
1418        pointers = compat.keys(data)
1419        pointers.sort()
1420        result = ''
1421        if pointers:
1422            if arch == win32.ARCH_I386:
1423                spreg = 'esp'
1424            elif arch == win32.ARCH_AMD64:
1425                spreg = 'rsp'
1426            else:
1427                spreg = 'STACK' # just a generic tag
1428            tag_fmt = '[%s+0x%%.%dx]' % (spreg, len( '%x' % pointers[-1] ) )
1429            for offset in pointers:
1430                dumped  = HexDump.hexline(data[offset], separator, width)
1431                tag     = tag_fmt % offset
1432                result += '%s -> %s\n' % (tag, dumped)
1433        return result
1434
1435    @staticmethod
1436    def dump_stack_trace(stack_trace, bits = None):
1437        """
1438        Dump a stack trace, as returned by L{Thread.get_stack_trace} with the
1439        C{bUseLabels} parameter set to C{False}.
1440
1441        @type  stack_trace: list( int, int, str )
1442        @param stack_trace: Stack trace as a list of tuples of
1443            ( return address, frame pointer, module filename )
1444
1445        @type  bits: int
1446        @param bits:
1447            (Optional) Number of bits of the target architecture.
1448            The default is platform dependent. See: L{HexDump.address_size}
1449
1450        @rtype:  str
1451        @return: Text suitable for logging.
1452        """
1453        if not stack_trace:
1454            return ''
1455        table = Table()
1456        table.addRow('Frame', 'Origin', 'Module')
1457        for (fp, ra, mod) in stack_trace:
1458            fp_d = HexDump.address(fp, bits)
1459            ra_d = HexDump.address(ra, bits)
1460            table.addRow(fp_d, ra_d, mod)
1461        return table.getOutput()
1462
1463    @staticmethod
1464    def dump_stack_trace_with_labels(stack_trace, bits = None):
1465        """
1466        Dump a stack trace,
1467        as returned by L{Thread.get_stack_trace_with_labels}.
1468
1469        @type  stack_trace: list( int, int, str )
1470        @param stack_trace: Stack trace as a list of tuples of
1471            ( return address, frame pointer, module filename )
1472
1473        @type  bits: int
1474        @param bits:
1475            (Optional) Number of bits of the target architecture.
1476            The default is platform dependent. See: L{HexDump.address_size}
1477
1478        @rtype:  str
1479        @return: Text suitable for logging.
1480        """
1481        if not stack_trace:
1482            return ''
1483        table = Table()
1484        table.addRow('Frame', 'Origin')
1485        for (fp, label) in stack_trace:
1486            table.addRow( HexDump.address(fp, bits), label )
1487        return table.getOutput()
1488
1489    # TODO
1490    # + Instead of a star when EIP points to, it would be better to show
1491    # any register value (or other values like the exception address) that
1492    # points to a location in the dissassembled code.
1493    # + It'd be very useful to show some labels here.
1494    # + It'd be very useful to show register contents for code at EIP
1495    @staticmethod
1496    def dump_code(disassembly,                                      pc = None,
1497                                                            bLowercase = True,
1498                                                                  bits = None):
1499        """
1500        Dump a disassembly. Optionally mark where the program counter is.
1501
1502        @type  disassembly: list of tuple( int, int, str, str )
1503        @param disassembly: Disassembly dump as returned by
1504            L{Process.disassemble} or L{Thread.disassemble_around_pc}.
1505
1506        @type  pc: int
1507        @param pc: (Optional) Program counter.
1508
1509        @type  bLowercase: bool
1510        @param bLowercase: (Optional) If C{True} convert the code to lowercase.
1511
1512        @type  bits: int
1513        @param bits:
1514            (Optional) Number of bits of the target architecture.
1515            The default is platform dependent. See: L{HexDump.address_size}
1516
1517        @rtype:  str
1518        @return: Text suitable for logging.
1519        """
1520        if not disassembly:
1521            return ''
1522        table = Table(sep = ' | ')
1523        for (addr, size, code, dump) in disassembly:
1524            if bLowercase:
1525                code = code.lower()
1526            if addr == pc:
1527                addr = ' * %s' % HexDump.address(addr, bits)
1528            else:
1529                addr = '   %s' % HexDump.address(addr, bits)
1530            table.addRow(addr, dump, code)
1531        table.justify(1, 1)
1532        return table.getOutput()
1533
1534    @staticmethod
1535    def dump_code_line(disassembly_line,                  bShowAddress = True,
1536                                                             bShowDump = True,
1537                                                            bLowercase = True,
1538                                                           dwDumpWidth = None,
1539                                                           dwCodeWidth = None,
1540                                                                  bits = None):
1541        """
1542        Dump a single line of code. To dump a block of code use L{dump_code}.
1543
1544        @type  disassembly_line: tuple( int, int, str, str )
1545        @param disassembly_line: Single item of the list returned by
1546            L{Process.disassemble} or L{Thread.disassemble_around_pc}.
1547
1548        @type  bShowAddress: bool
1549        @param bShowAddress: (Optional) If C{True} show the memory address.
1550
1551        @type  bShowDump: bool
1552        @param bShowDump: (Optional) If C{True} show the hexadecimal dump.
1553
1554        @type  bLowercase: bool
1555        @param bLowercase: (Optional) If C{True} convert the code to lowercase.
1556
1557        @type  dwDumpWidth: int or None
1558        @param dwDumpWidth: (Optional) Width in characters of the hex dump.
1559
1560        @type  dwCodeWidth: int or None
1561        @param dwCodeWidth: (Optional) Width in characters of the code.
1562
1563        @type  bits: int
1564        @param bits:
1565            (Optional) Number of bits of the target architecture.
1566            The default is platform dependent. See: L{HexDump.address_size}
1567
1568        @rtype:  str
1569        @return: Text suitable for logging.
1570        """
1571        if bits is None:
1572            address_size = HexDump.address_size
1573        else:
1574            address_size = bits / 4
1575        (addr, size, code, dump) = disassembly_line
1576        dump = dump.replace(' ', '')
1577        result = list()
1578        fmt = ''
1579        if bShowAddress:
1580            result.append( HexDump.address(addr, bits) )
1581            fmt += '%%%ds:' % address_size
1582        if bShowDump:
1583            result.append(dump)
1584            if dwDumpWidth:
1585                fmt += ' %%-%ds' % dwDumpWidth
1586            else:
1587                fmt += ' %s'
1588        if bLowercase:
1589            code = code.lower()
1590        result.append(code)
1591        if dwCodeWidth:
1592            fmt += ' %%-%ds' % dwCodeWidth
1593        else:
1594            fmt += ' %s'
1595        return fmt % tuple(result)
1596
1597    @staticmethod
1598    def dump_memory_map(memoryMap, mappedFilenames = None, bits = None):
1599        """
1600        Dump the memory map of a process. Optionally show the filenames for
1601        memory mapped files as well.
1602
1603        @type  memoryMap: list( L{win32.MemoryBasicInformation} )
1604        @param memoryMap: Memory map returned by L{Process.get_memory_map}.
1605
1606        @type  mappedFilenames: dict( int S{->} str )
1607        @param mappedFilenames: (Optional) Memory mapped filenames
1608            returned by L{Process.get_mapped_filenames}.
1609
1610        @type  bits: int
1611        @param bits:
1612            (Optional) Number of bits of the target architecture.
1613            The default is platform dependent. See: L{HexDump.address_size}
1614
1615        @rtype:  str
1616        @return: Text suitable for logging.
1617        """
1618        if not memoryMap:
1619            return ''
1620
1621        table = Table()
1622        if mappedFilenames:
1623            table.addRow("Address", "Size", "State", "Access", "Type", "File")
1624        else:
1625            table.addRow("Address", "Size", "State", "Access", "Type")
1626
1627        # For each memory block in the map...
1628        for mbi in memoryMap:
1629
1630            # Address and size of memory block.
1631            BaseAddress = HexDump.address(mbi.BaseAddress, bits)
1632            RegionSize  = HexDump.address(mbi.RegionSize,  bits)
1633
1634            # State (free or allocated).
1635            mbiState = mbi.State
1636            if   mbiState == win32.MEM_RESERVE:
1637                State   = "Reserved"
1638            elif mbiState == win32.MEM_COMMIT:
1639                State   = "Commited"
1640            elif mbiState == win32.MEM_FREE:
1641                State   = "Free"
1642            else:
1643                State   = "Unknown"
1644
1645            # Page protection bits (R/W/X/G).
1646            if mbiState != win32.MEM_COMMIT:
1647                Protect = ""
1648            else:
1649                mbiProtect = mbi.Protect
1650                if   mbiProtect & win32.PAGE_NOACCESS:
1651                    Protect = "--- "
1652                elif mbiProtect & win32.PAGE_READONLY:
1653                    Protect = "R-- "
1654                elif mbiProtect & win32.PAGE_READWRITE:
1655                    Protect = "RW- "
1656                elif mbiProtect & win32.PAGE_WRITECOPY:
1657                    Protect = "RC- "
1658                elif mbiProtect & win32.PAGE_EXECUTE:
1659                    Protect = "--X "
1660                elif mbiProtect & win32.PAGE_EXECUTE_READ:
1661                    Protect = "R-X "
1662                elif mbiProtect & win32.PAGE_EXECUTE_READWRITE:
1663                    Protect = "RWX "
1664                elif mbiProtect & win32.PAGE_EXECUTE_WRITECOPY:
1665                    Protect = "RCX "
1666                else:
1667                    Protect = "??? "
1668                if   mbiProtect & win32.PAGE_GUARD:
1669                    Protect += "G"
1670                else:
1671                    Protect += "-"
1672                if   mbiProtect & win32.PAGE_NOCACHE:
1673                    Protect += "N"
1674                else:
1675                    Protect += "-"
1676                if   mbiProtect & win32.PAGE_WRITECOMBINE:
1677                    Protect += "W"
1678                else:
1679                    Protect += "-"
1680
1681            # Type (file mapping, executable image, or private memory).
1682            mbiType = mbi.Type
1683            if   mbiType == win32.MEM_IMAGE:
1684                Type    = "Image"
1685            elif mbiType == win32.MEM_MAPPED:
1686                Type    = "Mapped"
1687            elif mbiType == win32.MEM_PRIVATE:
1688                Type    = "Private"
1689            elif mbiType == 0:
1690                Type    = ""
1691            else:
1692                Type    = "Unknown"
1693
1694            # Output a row in the table.
1695            if mappedFilenames:
1696                FileName = mappedFilenames.get(mbi.BaseAddress, '')
1697                table.addRow( BaseAddress, RegionSize, State, Protect, Type, FileName )
1698            else:
1699                table.addRow( BaseAddress, RegionSize, State, Protect, Type )
1700
1701        # Return the table output.
1702        return table.getOutput()
1703
1704#------------------------------------------------------------------------------
1705
1706class DebugLog (StaticClass):
1707    'Static functions for debug logging.'
1708
1709    @staticmethod
1710    def log_text(text):
1711        """
1712        Log lines of text, inserting a timestamp.
1713
1714        @type  text: str
1715        @param text: Text to log.
1716
1717        @rtype:  str
1718        @return: Log line.
1719        """
1720        if text.endswith('\n'):
1721            text = text[:-len('\n')]
1722        #text  = text.replace('\n', '\n\t\t')           # text CSV
1723        ltime = time.strftime("%X")
1724        msecs = (time.time() % 1) * 1000
1725        return '[%s.%04d] %s' % (ltime, msecs, text)
1726        #return '[%s.%04d]\t%s' % (ltime, msecs, text)  # text CSV
1727
1728    @classmethod
1729    def log_event(cls, event, text = None):
1730        """
1731        Log lines of text associated with a debug event.
1732
1733        @type  event: L{Event}
1734        @param event: Event object.
1735
1736        @type  text: str
1737        @param text: (Optional) Text to log. If no text is provided the default
1738            is to show a description of the event itself.
1739
1740        @rtype:  str
1741        @return: Log line.
1742        """
1743        if not text:
1744            if event.get_event_code() == win32.EXCEPTION_DEBUG_EVENT:
1745                what = event.get_exception_description()
1746                if event.is_first_chance():
1747                    what = '%s (first chance)' % what
1748                else:
1749                    what = '%s (second chance)' % what
1750                try:
1751                    address = event.get_fault_address()
1752                except NotImplementedError:
1753                    address = event.get_exception_address()
1754            else:
1755                what    = event.get_event_name()
1756                address = event.get_thread().get_pc()
1757            process = event.get_process()
1758            label = process.get_label_at_address(address)
1759            address = HexDump.address(address, process.get_bits())
1760            if label:
1761                where = '%s (%s)' % (address, label)
1762            else:
1763                where = address
1764            text = '%s at %s' % (what, where)
1765        text = 'pid %d tid %d: %s' % (event.get_pid(), event.get_tid(), text)
1766        #text = 'pid %d tid %d:\t%s' % (event.get_pid(), event.get_tid(), text)     # text CSV
1767        return cls.log_text(text)
1768
1769#------------------------------------------------------------------------------
1770
1771class Logger(object):
1772    """
1773    Logs text to standard output and/or a text file.
1774
1775    @type logfile: str or None
1776    @ivar logfile: Append messages to this text file.
1777
1778    @type verbose: bool
1779    @ivar verbose: C{True} to print messages to standard output.
1780
1781    @type fd: file
1782    @ivar fd: File object where log messages are printed to.
1783        C{None} if no log file is used.
1784    """
1785
1786    def __init__(self, logfile = None, verbose = True):
1787        """
1788        @type  logfile: str or None
1789        @param logfile: Append messages to this text file.
1790
1791        @type  verbose: bool
1792        @param verbose: C{True} to print messages to standard output.
1793        """
1794        self.verbose = verbose
1795        self.logfile = logfile
1796        if self.logfile:
1797            self.fd = open(self.logfile, 'a+')
1798
1799    def __logfile_error(self, e):
1800        """
1801        Shows an error message to standard error
1802        if the log file can't be written to.
1803
1804        Used internally.
1805
1806        @type  e: Exception
1807        @param e: Exception raised when trying to write to the log file.
1808        """
1809        from sys import stderr
1810        msg = "Warning, error writing log file %s: %s\n"
1811        msg = msg % (self.logfile, str(e))
1812        stderr.write(DebugLog.log_text(msg))
1813        self.logfile = None
1814        self.fd      = None
1815
1816    def __do_log(self, text):
1817        """
1818        Writes the given text verbatim into the log file (if any)
1819        and/or standard input (if the verbose flag is turned on).
1820
1821        Used internally.
1822
1823        @type  text: str
1824        @param text: Text to print.
1825        """
1826        if isinstance(text, compat.unicode):
1827            text = text.encode('cp1252')
1828        if self.verbose:
1829            print(text)
1830        if self.logfile:
1831            try:
1832                self.fd.writelines('%s\n' % text)
1833            except IOError:
1834                e = sys.exc_info()[1]
1835                self.__logfile_error(e)
1836
1837    def log_text(self, text):
1838        """
1839        Log lines of text, inserting a timestamp.
1840
1841        @type  text: str
1842        @param text: Text to log.
1843        """
1844        self.__do_log( DebugLog.log_text(text) )
1845
1846    def log_event(self, event, text = None):
1847        """
1848        Log lines of text associated with a debug event.
1849
1850        @type  event: L{Event}
1851        @param event: Event object.
1852
1853        @type  text: str
1854        @param text: (Optional) Text to log. If no text is provided the default
1855            is to show a description of the event itself.
1856        """
1857        self.__do_log( DebugLog.log_event(event, text) )
1858
1859    def log_exc(self):
1860        """
1861        Log lines of text associated with the last Python exception.
1862        """
1863        self.__do_log( 'Exception raised: %s' % traceback.format_exc() )
1864
1865    def is_enabled(self):
1866        """
1867        Determines if the logger will actually print anything when the log_*
1868        methods are called.
1869
1870        This may save some processing if the log text requires a lengthy
1871        calculation to prepare. If no log file is set and stdout logging
1872        is disabled, there's no point in preparing a log text that won't
1873        be shown to anyone.
1874
1875        @rtype:  bool
1876        @return: C{True} if a log file was set and/or standard output logging
1877            is enabled, or C{False} otherwise.
1878        """
1879        return self.verbose or self.logfile
1880