Commit c61a0d9368c62f77201b420ccb4cc26f0c18e6bd

Authored by Philippe Lagadec
1 parent e87c0b10

olevba: detect auto-executable macros, ignore empty macros

Showing 1 changed file with 59 additions and 2 deletions
oletools/olevba.py
... ... @@ -82,14 +82,14 @@ Usage: olevba.py <file>
82 82 # 2014-12-05 v0.05 PL: - refactored most functions into a class, new API
83 83 # - added detect_vba_macros
84 84 # 2014-12-10 v0.06 PL: - hide first lines with VB attributes
  85 +# - detect auto-executable macros
  86 +# - ignore empty macros
85 87  
86 88 __version__ = '0.06'
87 89  
88 90 #------------------------------------------------------------------------------
89 91 # TODO:
90 92 # + do not use logging, but a provided logger (null logger by default)
91   -# + by default, do not display empty macros containing only lines with Attribute=... (option)
92   -# (...unless it can be used to hide code: to be tested)
93 93 # + optparse
94 94 # + nicer output
95 95 # + setup logging (common with other oletools)
... ... @@ -130,6 +130,27 @@ MODULE_EXTENSION = "bas"
130 130 CLASS_EXTENSION = "cls"
131 131 FORM_EXTENSION = "frm"
132 132  
  133 +# Keywords to detect auto-executable macros
  134 +AUTOEXEC_KEYWORDS = {
  135 + # MS Word:
  136 + 'Macro running automatically when the Word document is opened':
  137 + ('AutoExec', 'AutoOpen', 'Document_Open', 'DocumentOpen'),
  138 + 'Macro running automatically when the Word document is closed':
  139 + ('AutoExit', 'AutoClose', 'Document_Close', 'DocumentBeforeClose'),
  140 + 'Macro running automatically when the Word document is modified':
  141 + ('DocumentChange',),
  142 + 'Macro running automatically when a new Word document is created':
  143 + ('AutoNew', 'Document_New', 'NewDocument'),
  144 +
  145 + # MS Excel:
  146 + 'Macro running automatically when the Excel Workbook is opened':
  147 + ('Auto_Open', 'Workbook_Open'),
  148 + 'Macro running automatically when the Excel Workbook is closed':
  149 + ('Auto_Close', 'Workbook_Close'),
  150 +
  151 + #TODO: full list in MS specs??
  152 +}
  153 +
133 154  
134 155 #--- FUNCTIONS ----------------------------------------------------------------
135 156  
... ... @@ -664,6 +685,22 @@ def filter_vba(vba_code):
664 685 return vba
665 686  
666 687  
  688 +def detect_autoexec(vba_code):
  689 + """
  690 + Detect if the VBA code contains keywords corresponding to macros running
  691 + automatically when triggered by specific actions (e.g. when a document is
  692 + opened or closed).
  693 +
  694 + :param vba_code: str, VBA source code
  695 + :return: list of str tuples (keyword, description)
  696 + """
  697 + results = []
  698 + for description, keywords in AUTOEXEC_KEYWORDS.items():
  699 + for keyword in keywords:
  700 + if keyword in vba_code:
  701 + results.append((keyword, description))
  702 + return results
  703 +
667 704  
668 705 #=== CLASSES =================================================================
669 706  
... ... @@ -878,6 +915,8 @@ class VBA_Parser(object):
878 915  
879 916 if __name__ == '__main__':
880 917  
  918 + from thirdparty.prettytable import prettytable
  919 +
881 920 if len(sys.argv)<2:
882 921 print __doc__
883 922 sys.exit(1)
... ... @@ -894,12 +933,30 @@ if __name__ == &#39;__main__&#39;:
894 933 if vba.detect_vba_macros():
895 934 print 'Contains VBA Macros:'
896 935 for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros():
  936 + # hide attribute lines:
  937 + vba_code = filter_vba(vba_code)
  938 + # ignore empty macros:
  939 + if vba_code.strip() == '':
  940 + continue
897 941 print '-'*79
898 942 print 'Filename :', filename
899 943 print 'OLE stream :', stream_path
900 944 print 'VBA filename:', vba_filename
901 945 print '- '*39
902 946 print filter_vba(vba_code)
  947 + print '- '*39
  948 + autoexec_keywords = detect_autoexec(vba_code)
  949 + if autoexec_keywords:
  950 + print 'Auto-executable macro keywords found:'
  951 + t = prettytable.PrettyTable(('Keyword', 'Description'))
  952 + t.max_width['Keyword'] = 20
  953 + t.max_width['Description'] = 59
  954 + for keyword, description in autoexec_keywords:
  955 + t.add_row((keyword, description))
  956 + print t
  957 + else:
  958 + print 'Auto-executable macro keywords: None found'
  959 +
903 960 else:
904 961 print 'No VBA macros found.'
905 962 except TypeError:
... ...