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 | 59 | # 2016-07-19 PL: - fixed Python 2.6-2.7 support |
| 60 | 60 | # 2016-07-30 PL: - new API with class RtfObject |
| 61 | 61 | # - backward-compatible API rtf_iter_objects (fixed issue #70) |
| 62 | +# 2016-07-31 PL: - table output with tablestream | |
| 62 | 63 | |
| 63 | 64 | __version__ = '0.50' |
| 64 | 65 | |
| ... | ... | @@ -76,6 +77,8 @@ from thirdparty.xglob import xglob |
| 76 | 77 | from oleobj import OleObject, OleNativeStream |
| 77 | 78 | import oleobj |
| 78 | 79 | |
| 80 | +from thirdparty.tablestream import tablestream | |
| 81 | + | |
| 79 | 82 | |
| 80 | 83 | # === LOGGING ================================================================= |
| 81 | 84 | |
| ... | ... | @@ -601,6 +604,12 @@ def sanitize_filename(filename, replacement='_', max_length=200): |
| 601 | 604 | |
| 602 | 605 | |
| 603 | 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 | 613 | if output_dir: |
| 605 | 614 | if not os.path.isdir(output_dir): |
| 606 | 615 | log.info('creating output directory %s' % output_dir) |
| ... | ... | @@ -616,50 +625,62 @@ def process_file(container, filename, data, output_dir=None): |
| 616 | 625 | # TODO: option to extract objects to files (false by default) |
| 617 | 626 | if data is None: |
| 618 | 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 | 630 | rtfp = RtfObjParser(data) |
| 622 | 631 | rtfp.parse() |
| 623 | 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 | 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 | 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 | 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 | 673 | else: |
| 659 | - print('Not an OLE Package') | |
| 674 | + pkg_column = 'Not an OLE Package' | |
| 660 | 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 | 686 | #=== MAIN ================================================================= |
| ... | ... | @@ -714,7 +735,6 @@ def main(): |
| 714 | 735 | log.setLevel(logging.NOTSET) |
| 715 | 736 | oleobj.log.setLevel(logging.NOTSET) |
| 716 | 737 | |
| 717 | - | |
| 718 | 738 | for container, filename, data in xglob.iter_files(args, recursive=options.recursive, |
| 719 | 739 | zip_password=options.zip_password, zip_fname=options.zip_fname): |
| 720 | 740 | # ignore directory names stored in zip files: | ... | ... |