Commit ce3c92cf276c6048cd06f3ad04ac44f87d77d002

Authored by decalage2
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:
... ...