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