Commit ce3c92cf276c6048cd06f3ad04ac44f87d77d002
1 parent
6f3de117
rtfobj: table output with tablestream
Showing
1 changed file
with
57 additions
and
37 deletions
oletools/rtfobj.py
| @@ -59,6 +59,7 @@ http://www.decalage.info/python/oletools | @@ -59,6 +59,7 @@ http://www.decalage.info/python/oletools | ||
| 59 | # 2016-07-19 PL: - fixed Python 2.6-2.7 support | 59 | # 2016-07-19 PL: - fixed Python 2.6-2.7 support |
| 60 | # 2016-07-30 PL: - new API with class RtfObject | 60 | # 2016-07-30 PL: - new API with class RtfObject |
| 61 | # - backward-compatible API rtf_iter_objects (fixed issue #70) | 61 | # - backward-compatible API rtf_iter_objects (fixed issue #70) |
| 62 | +# 2016-07-31 PL: - table output with tablestream | ||
| 62 | 63 | ||
| 63 | __version__ = '0.50' | 64 | __version__ = '0.50' |
| 64 | 65 | ||
| @@ -76,6 +77,8 @@ from thirdparty.xglob import xglob | @@ -76,6 +77,8 @@ from thirdparty.xglob import xglob | ||
| 76 | from oleobj import OleObject, OleNativeStream | 77 | from oleobj import OleObject, OleNativeStream |
| 77 | import oleobj | 78 | import oleobj |
| 78 | 79 | ||
| 80 | +from thirdparty.tablestream import tablestream | ||
| 81 | + | ||
| 79 | 82 | ||
| 80 | # === LOGGING ================================================================= | 83 | # === LOGGING ================================================================= |
| 81 | 84 | ||
| @@ -601,6 +604,12 @@ def sanitize_filename(filename, replacement='_', max_length=200): | @@ -601,6 +604,12 @@ def sanitize_filename(filename, replacement='_', max_length=200): | ||
| 601 | 604 | ||
| 602 | 605 | ||
| 603 | def process_file(container, filename, data, output_dir=None): | 606 | def process_file(container, filename, data, output_dir=None): |
| 607 | + tstream = tablestream.TableStream( | ||
| 608 | + column_width=(3, 10, 31, 31), | ||
| 609 | + header_row=('id', 'index', 'OLE Object', 'OLE Package'), | ||
| 610 | + style=tablestream.TableStyleSlim | ||
| 611 | + ) | ||
| 612 | + | ||
| 604 | if output_dir: | 613 | if output_dir: |
| 605 | if not os.path.isdir(output_dir): | 614 | if not os.path.isdir(output_dir): |
| 606 | log.info('creating output directory %s' % output_dir) | 615 | log.info('creating output directory %s' % output_dir) |
| @@ -616,50 +625,62 @@ def process_file(container, filename, data, output_dir=None): | @@ -616,50 +625,62 @@ def process_file(container, filename, data, output_dir=None): | ||
| 616 | # TODO: option to extract objects to files (false by default) | 625 | # TODO: option to extract objects to files (false by default) |
| 617 | if data is None: | 626 | if data is None: |
| 618 | data = open(filename, 'rb').read() | 627 | data = open(filename, 'rb').read() |
| 619 | - print('='*79) | ||
| 620 | - print('File: %r - %d bytes' % (filename, len(data))) | 628 | + # print('='*79) |
| 629 | + # print('File: %r - %d bytes' % (filename, len(data))) | ||
| 621 | rtfp = RtfObjParser(data) | 630 | rtfp = RtfObjParser(data) |
| 622 | rtfp.parse() | 631 | rtfp.parse() |
| 623 | for rtfobj in rtfp.objects: | 632 | for rtfobj in rtfp.objects: |
| 624 | - print('-'*79) | ||
| 625 | - print('found object size %d at index %08X - end %08X' | ||
| 626 | - % (len(rtfobj.rawdata), rtfobj.start, rtfobj.end)) | ||
| 627 | - fname = '%s_object_%08X.raw' % (fname_prefix, rtfobj.start) | ||
| 628 | - print('saving object to file %s' % fname) | ||
| 629 | - open(fname, 'wb').write(rtfobj.rawdata) | 633 | + # print('-'*79) |
| 634 | + # print('found object size %d at index %08X - end %08X' | ||
| 635 | + # % (len(rtfobj.rawdata), rtfobj.start, rtfobj.end)) | ||
| 636 | + # fname = '%s_object_%08X.raw' % (fname_prefix, rtfobj.start) | ||
| 637 | + # print('saving object to file %s' % fname) | ||
| 638 | + # open(fname, 'wb').write(rtfobj.rawdata) | ||
| 630 | if rtfobj.is_ole: | 639 | if rtfobj.is_ole: |
| 631 | - print('extract file embedded in OLE object:') | ||
| 632 | - print('format_id = %d' % rtfobj.format_id) | ||
| 633 | - print('class name = %r' % rtfobj.class_name) | ||
| 634 | - print('data size = %d' % rtfobj.oledata_size) | 640 | + ole_column = 'format_id: %d\n' % rtfobj.format_id |
| 641 | + ole_column += 'class name: %r\n' % rtfobj.class_name | ||
| 642 | + ole_column += 'data size: %d' % rtfobj.oledata_size | ||
| 643 | + # print('extract file embedded in OLE object:') | ||
| 644 | + # print('format_id = %d' % rtfobj.format_id) | ||
| 645 | + # print('class name = %r' % rtfobj.class_name) | ||
| 646 | + # print('data size = %d' % rtfobj.oledata_size) | ||
| 635 | # set a file extension according to the class name: | 647 | # set a file extension according to the class name: |
| 636 | - class_name = rtfobj.class_name.lower() | ||
| 637 | - if class_name.startswith(b'word'): | ||
| 638 | - ext = 'doc' | ||
| 639 | - elif class_name.startswith(b'package'): | ||
| 640 | - ext = 'package' | ||
| 641 | - else: | ||
| 642 | - ext = 'bin' | ||
| 643 | - fname = '%s_object_%08X.%s' % (fname_prefix, rtfobj.start, ext) | ||
| 644 | - print('saving to file %s' % fname) | ||
| 645 | - open(fname, 'wb').write(rtfobj.oledata) | 648 | + # class_name = rtfobj.class_name.lower() |
| 649 | + # if class_name.startswith(b'word'): | ||
| 650 | + # ext = 'doc' | ||
| 651 | + # elif class_name.startswith(b'package'): | ||
| 652 | + # ext = 'package' | ||
| 653 | + # else: | ||
| 654 | + # ext = 'bin' | ||
| 655 | + # fname = '%s_object_%08X.%s' % (fname_prefix, rtfobj.start, ext) | ||
| 656 | + # print('saving to file %s' % fname) | ||
| 657 | + # open(fname, 'wb').write(rtfobj.oledata) | ||
| 646 | if rtfobj.is_package: | 658 | if rtfobj.is_package: |
| 647 | - print('Parsing OLE Package') | ||
| 648 | - print('Filename = %r' % rtfobj.filename) | ||
| 649 | - print('Source path = %r' % rtfobj.src_path) | ||
| 650 | - print('Temp path = %r' % rtfobj.temp_path) | ||
| 651 | - if rtfobj.filename: | ||
| 652 | - fname = '%s_%s' % (fname_prefix, | ||
| 653 | - sanitize_filename(rtfobj.filename)) | ||
| 654 | - else: | ||
| 655 | - fname = '%s_object_%08X.noname' % (fname_prefix, rtfobj.start) | ||
| 656 | - print('saving to file %s' % fname) | ||
| 657 | - open(fname, 'wb').write(rtfobj.olepkgdata) | 659 | + pkg_column = 'Filename: %r\n' % rtfobj.filename |
| 660 | + pkg_column += 'Source path: %r\n' % rtfobj.src_path | ||
| 661 | + pkg_column += 'Temp path = %r' % rtfobj.temp_path | ||
| 662 | + # print('Parsing OLE Package') | ||
| 663 | + # print('Filename = %r' % rtfobj.filename) | ||
| 664 | + # print('Source path = %r' % rtfobj.src_path) | ||
| 665 | + # print('Temp path = %r' % rtfobj.temp_path) | ||
| 666 | + # if rtfobj.filename: | ||
| 667 | + # fname = '%s_%s' % (fname_prefix, | ||
| 668 | + # sanitize_filename(rtfobj.filename)) | ||
| 669 | + # else: | ||
| 670 | + # fname = '%s_object_%08X.noname' % (fname_prefix, rtfobj.start) | ||
| 671 | + # print('saving to file %s' % fname) | ||
| 672 | + # open(fname, 'wb').write(rtfobj.olepkgdata) | ||
| 658 | else: | 673 | else: |
| 659 | - print('Not an OLE Package') | 674 | + pkg_column = 'Not an OLE Package' |
| 660 | else: | 675 | else: |
| 661 | - print('Not a well-formed OLE object') | ||
| 662 | - | 676 | + ole_column = 'Not a well-formed OLE object' |
| 677 | + tstream.write_row(( | ||
| 678 | + rtfp.objects.index(rtfobj), | ||
| 679 | + # filename, | ||
| 680 | + '%08Xh' % rtfobj.start, | ||
| 681 | + ole_column, | ||
| 682 | + pkg_column | ||
| 683 | + )) | ||
| 663 | 684 | ||
| 664 | 685 | ||
| 665 | #=== MAIN ================================================================= | 686 | #=== MAIN ================================================================= |
| @@ -714,7 +735,6 @@ def main(): | @@ -714,7 +735,6 @@ def main(): | ||
| 714 | log.setLevel(logging.NOTSET) | 735 | log.setLevel(logging.NOTSET) |
| 715 | oleobj.log.setLevel(logging.NOTSET) | 736 | oleobj.log.setLevel(logging.NOTSET) |
| 716 | 737 | ||
| 717 | - | ||
| 718 | for container, filename, data in xglob.iter_files(args, recursive=options.recursive, | 738 | for container, filename, data in xglob.iter_files(args, recursive=options.recursive, |
| 719 | zip_password=options.zip_password, zip_fname=options.zip_fname): | 739 | zip_password=options.zip_password, zip_fname=options.zip_fname): |
| 720 | # ignore directory names stored in zip files: | 740 | # ignore directory names stored in zip files: |