Commit c61a0d9368c62f77201b420ccb4cc26f0c18e6bd
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,14 +82,14 @@ Usage: olevba.py <file> | ||
| 82 | # 2014-12-05 v0.05 PL: - refactored most functions into a class, new API | 82 | # 2014-12-05 v0.05 PL: - refactored most functions into a class, new API |
| 83 | # - added detect_vba_macros | 83 | # - added detect_vba_macros |
| 84 | # 2014-12-10 v0.06 PL: - hide first lines with VB attributes | 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 | __version__ = '0.06' | 88 | __version__ = '0.06' |
| 87 | 89 | ||
| 88 | #------------------------------------------------------------------------------ | 90 | #------------------------------------------------------------------------------ |
| 89 | # TODO: | 91 | # TODO: |
| 90 | # + do not use logging, but a provided logger (null logger by default) | 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 | # + optparse | 93 | # + optparse |
| 94 | # + nicer output | 94 | # + nicer output |
| 95 | # + setup logging (common with other oletools) | 95 | # + setup logging (common with other oletools) |
| @@ -130,6 +130,27 @@ MODULE_EXTENSION = "bas" | @@ -130,6 +130,27 @@ MODULE_EXTENSION = "bas" | ||
| 130 | CLASS_EXTENSION = "cls" | 130 | CLASS_EXTENSION = "cls" |
| 131 | FORM_EXTENSION = "frm" | 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 | #--- FUNCTIONS ---------------------------------------------------------------- | 155 | #--- FUNCTIONS ---------------------------------------------------------------- |
| 135 | 156 | ||
| @@ -664,6 +685,22 @@ def filter_vba(vba_code): | @@ -664,6 +685,22 @@ def filter_vba(vba_code): | ||
| 664 | return vba | 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 | #=== CLASSES ================================================================= | 705 | #=== CLASSES ================================================================= |
| 669 | 706 | ||
| @@ -878,6 +915,8 @@ class VBA_Parser(object): | @@ -878,6 +915,8 @@ class VBA_Parser(object): | ||
| 878 | 915 | ||
| 879 | if __name__ == '__main__': | 916 | if __name__ == '__main__': |
| 880 | 917 | ||
| 918 | + from thirdparty.prettytable import prettytable | ||
| 919 | + | ||
| 881 | if len(sys.argv)<2: | 920 | if len(sys.argv)<2: |
| 882 | print __doc__ | 921 | print __doc__ |
| 883 | sys.exit(1) | 922 | sys.exit(1) |
| @@ -894,12 +933,30 @@ if __name__ == '__main__': | @@ -894,12 +933,30 @@ if __name__ == '__main__': | ||
| 894 | if vba.detect_vba_macros(): | 933 | if vba.detect_vba_macros(): |
| 895 | print 'Contains VBA Macros:' | 934 | print 'Contains VBA Macros:' |
| 896 | for (filename, stream_path, vba_filename, vba_code) in vba.extract_macros(): | 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 | print '-'*79 | 941 | print '-'*79 |
| 898 | print 'Filename :', filename | 942 | print 'Filename :', filename |
| 899 | print 'OLE stream :', stream_path | 943 | print 'OLE stream :', stream_path |
| 900 | print 'VBA filename:', vba_filename | 944 | print 'VBA filename:', vba_filename |
| 901 | print '- '*39 | 945 | print '- '*39 |
| 902 | print filter_vba(vba_code) | 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 | else: | 960 | else: |
| 904 | print 'No VBA macros found.' | 961 | print 'No VBA macros found.' |
| 905 | except TypeError: | 962 | except TypeError: |