Commit f133f113184ef10bbaa8f97f567fa46931341fd7
1 parent
b933bc98
olevba: added OptionParser, main and process_file
Showing
1 changed file
with
55 additions
and
27 deletions
oletools/olevba.py
| @@ -18,8 +18,6 @@ http://www.decalage.info/python/oletools | @@ -18,8 +18,6 @@ http://www.decalage.info/python/oletools | ||
| 18 | 18 | ||
| 19 | olevba is based on source code from officeparser by John William Davison | 19 | olevba is based on source code from officeparser by John William Davison |
| 20 | https://github.com/unixfreak0037/officeparser | 20 | https://github.com/unixfreak0037/officeparser |
| 21 | - | ||
| 22 | -Usage: olevba.py <file> | ||
| 23 | """ | 21 | """ |
| 24 | 22 | ||
| 25 | #=== LICENSE ================================================================== | 23 | #=== LICENSE ================================================================== |
| @@ -88,27 +86,26 @@ Usage: olevba.py <file> | @@ -88,27 +86,26 @@ Usage: olevba.py <file> | ||
| 88 | # 2014-12-15 v0.08 PL: - improved display for empty macros | 86 | # 2014-12-15 v0.08 PL: - improved display for empty macros |
| 89 | # - added pattern extraction | 87 | # - added pattern extraction |
| 90 | # 2014-12-25 v0.09 PL: - added suspicious keywords detection | 88 | # 2014-12-25 v0.09 PL: - added suspicious keywords detection |
| 89 | +# 2014-12-27 v0.10 PL: - added OptionParser, main and process_file | ||
| 91 | 90 | ||
| 92 | -__version__ = '0.09' | 91 | +__version__ = '0.10' |
| 93 | 92 | ||
| 94 | #------------------------------------------------------------------------------ | 93 | #------------------------------------------------------------------------------ |
| 95 | # TODO: | 94 | # TODO: |
| 95 | +# + process several files in dirs or zips with password | ||
| 96 | # + do not use logging, but a provided logger (null logger by default) | 96 | # + do not use logging, but a provided logger (null logger by default) |
| 97 | -# + optparse | ||
| 98 | # + nicer output | 97 | # + nicer output |
| 99 | # + setup logging (common with other oletools) | 98 | # + setup logging (common with other oletools) |
| 100 | # + update readme, wiki and decalage.info, pypi (link to sample files) | 99 | # + update readme, wiki and decalage.info, pypi (link to sample files) |
| 100 | + | ||
| 101 | +# TODO later: | ||
| 101 | # + performance improvement: instead of searching each keyword separately, | 102 | # + performance improvement: instead of searching each keyword separately, |
| 102 | # first split vba code into a list of words (per line), then check each | 103 | # first split vba code into a list of words (per line), then check each |
| 103 | # word against a dict. (or put vba words into a set/dict?) | 104 | # word against a dict. (or put vba words into a set/dict?) |
| 104 | # + for regex, maybe combine them into a single re with named groups? | 105 | # + for regex, maybe combine them into a single re with named groups? |
| 105 | # + add Yara support, include sample rules? plugins like balbuzard? | 106 | # + add Yara support, include sample rules? plugins like balbuzard? |
| 106 | # + add balbuzard support | 107 | # + add balbuzard support |
| 107 | -# + move main into functions | ||
| 108 | - | ||
| 109 | -# TODO later: | ||
| 110 | -# + output to file | ||
| 111 | -# + process several files in dirs or zips with password | 108 | +# + output to file (replace print by file.write, sys.stdout by default) |
| 112 | # + look for VBA in embedded documents (e.g. Excel in Word) | 109 | # + look for VBA in embedded documents (e.g. Excel in Word) |
| 113 | # + support SRP streams (see Lenny's article + links and sample) | 110 | # + support SRP streams (see Lenny's article + links and sample) |
| 114 | # - python 3.x support | 111 | # - python 3.x support |
| @@ -133,8 +130,10 @@ import cStringIO | @@ -133,8 +130,10 @@ import cStringIO | ||
| 133 | import math | 130 | import math |
| 134 | import zipfile | 131 | import zipfile |
| 135 | import re | 132 | import re |
| 133 | +import optparse | ||
| 136 | 134 | ||
| 137 | import thirdparty.olefile as olefile | 135 | import thirdparty.olefile as olefile |
| 136 | +from thirdparty.prettytable import prettytable | ||
| 138 | 137 | ||
| 139 | #--- CONSTANTS ---------------------------------------------------------------- | 138 | #--- CONSTANTS ---------------------------------------------------------------- |
| 140 | 139 | ||
| @@ -815,6 +814,7 @@ class VBA_Parser(object): | @@ -815,6 +814,7 @@ class VBA_Parser(object): | ||
| 815 | :param filename: actual filename if _file is a file-like object or file content | 814 | :param filename: actual filename if _file is a file-like object or file content |
| 816 | in a bytes string | 815 | in a bytes string |
| 817 | """ | 816 | """ |
| 817 | + #TODO: filename should be mandatory, optional data is a string or file-like object | ||
| 818 | #TODO: also support olefile and zipfile as input | 818 | #TODO: also support olefile and zipfile as input |
| 819 | self.file = _file | 819 | self.file = _file |
| 820 | self.ole_file = None | 820 | self.ole_file = None |
| @@ -1005,33 +1005,24 @@ class VBA_Parser(object): | @@ -1005,33 +1005,24 @@ class VBA_Parser(object): | ||
| 1005 | self.ole_file.close() | 1005 | self.ole_file.close() |
| 1006 | 1006 | ||
| 1007 | 1007 | ||
| 1008 | -#=== MAIN ===================================================================== | ||
| 1009 | - | ||
| 1010 | -if __name__ == '__main__': | ||
| 1011 | - | ||
| 1012 | - from thirdparty.prettytable import prettytable | ||
| 1013 | - | ||
| 1014 | - if len(sys.argv)<2: | ||
| 1015 | - print __doc__ | ||
| 1016 | - sys.exit(1) | ||
| 1017 | - | ||
| 1018 | - logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING) #INFO) | ||
| 1019 | - | ||
| 1020 | - #TODO: option parser | ||
| 1021 | - fname = sys.argv[1] | 1008 | +def process_file (filename): |
| 1009 | + """ | ||
| 1010 | + Process a single file | ||
| 1011 | + """ | ||
| 1012 | + #TODO: replace print by writing to a provided output file (sys.stdout by default) | ||
| 1022 | print '='*79 | 1013 | print '='*79 |
| 1023 | - print 'File:', fname | 1014 | + print 'File:', filename |
| 1024 | try: | 1015 | try: |
| 1025 | #TODO: handle olefile errors, when an OLE file is malformed | 1016 | #TODO: handle olefile errors, when an OLE file is malformed |
| 1026 | - vba = VBA_Parser(fname) | 1017 | + vba = VBA_Parser(filename, filename=filename) |
| 1027 | print 'Type:', vba.type | 1018 | print 'Type:', vba.type |
| 1028 | if vba.detect_vba_macros(): | 1019 | if vba.detect_vba_macros(): |
| 1029 | print 'Contains VBA Macros:' | 1020 | print 'Contains VBA Macros:' |
| 1030 | - for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): | 1021 | + for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): |
| 1031 | # hide attribute lines: | 1022 | # hide attribute lines: |
| 1032 | vba_code = filter_vba(vba_code) | 1023 | vba_code = filter_vba(vba_code) |
| 1033 | print '-'*79 | 1024 | print '-'*79 |
| 1034 | - print 'Filename :', filename | 1025 | + print 'Filename :', subfilename |
| 1035 | print 'OLE stream :', stream_path | 1026 | print 'OLE stream :', stream_path |
| 1036 | print 'VBA filename:', vba_filename | 1027 | print 'VBA filename:', vba_filename |
| 1037 | print '- '*39 | 1028 | print '- '*39 |
| @@ -1088,4 +1079,41 @@ if __name__ == '__main__': | @@ -1088,4 +1079,41 @@ if __name__ == '__main__': | ||
| 1088 | raise | 1079 | raise |
| 1089 | print sys.exc_value | 1080 | print sys.exc_value |
| 1090 | 1081 | ||
| 1082 | + | ||
| 1083 | +#=== MAIN ===================================================================== | ||
| 1084 | + | ||
| 1085 | +def main(): | ||
| 1086 | + """ | ||
| 1087 | + Main function, called when olevba is run from the command line | ||
| 1088 | + """ | ||
| 1089 | + usage = 'usage: %prog [options] <filename> [filename2 ...]' | ||
| 1090 | + parser = optparse.OptionParser(usage=usage) | ||
| 1091 | + # parser.add_option('-o', '--outfile', dest='outfile', | ||
| 1092 | + # help='output file') | ||
| 1093 | + # parser.add_option('-c', '--csv', dest='csv', | ||
| 1094 | + # help='export results to a CSV file') | ||
| 1095 | + # parser.add_option("-r", action="store_true", dest="recursive", | ||
| 1096 | + # help='find files recursively in subdirectories.') | ||
| 1097 | + # parser.add_option("-z", "--zip", dest='zip_password', type='str', default=None, | ||
| 1098 | + # help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)') | ||
| 1099 | + # parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', | ||
| 1100 | + # help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)') | ||
| 1101 | + | ||
| 1102 | + (options, args) = parser.parse_args() | ||
| 1103 | + | ||
| 1104 | + # Print help if no argurments are passed | ||
| 1105 | + if len(args) == 0: | ||
| 1106 | + print __doc__ | ||
| 1107 | + parser.print_help() | ||
| 1108 | + sys.exit() | ||
| 1109 | + | ||
| 1110 | + logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING) #INFO) | ||
| 1111 | + | ||
| 1112 | + for filespec in args: | ||
| 1113 | + #data = open(filespec, 'rb').read() | ||
| 1114 | + process_file(filespec) | ||
| 1115 | + | ||
| 1116 | +if __name__ == '__main__': | ||
| 1117 | + main() | ||
| 1118 | + | ||
| 1091 | # This was coded while listening to "Dust" from I Love You But I've Chosen Darkness | 1119 | # This was coded while listening to "Dust" from I Love You But I've Chosen Darkness |
| 1092 | \ No newline at end of file | 1120 | \ No newline at end of file |