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 | 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__ == '__main__': |
| 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: | ... | ... |