Package winappdbg :: Module textio
[hide private]
[frames] | no frames]

Source Code for Module winappdbg.textio

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