Commit 6641ac1ab37ac2842ceed687143f50f6a91002d1
1 parent
8b21d586
created exception ProcessingError, raise from process_... functions and handle in main
Showing
1 changed file
with
101 additions
and
88 deletions
oletools/olevba.py
| ... | ... | @@ -214,7 +214,6 @@ import optparse |
| 214 | 214 | import os.path |
| 215 | 215 | import binascii |
| 216 | 216 | import base64 |
| 217 | -import traceback | |
| 218 | 217 | import zlib |
| 219 | 218 | import email # for MHTML parsing |
| 220 | 219 | import string # for printable |
| ... | ... | @@ -304,6 +303,16 @@ class FileOpenError(Exception): |
| 304 | 303 | self.filename = filename |
| 305 | 304 | |
| 306 | 305 | |
| 306 | +class ProcessingError(Exception): | |
| 307 | + """ raised by VBA_Parser.process_file* functions """ | |
| 308 | + | |
| 309 | + def __init__(self, filename, orig_exception): | |
| 310 | + super(ProcessingError, self).__init__( | |
| 311 | + 'Error processing file %s (%s)' % (filename, orig_exception)) | |
| 312 | + self.filename = filename | |
| 313 | + self.orig_exception = orig_exception | |
| 314 | + | |
| 315 | + | |
| 307 | 316 | class MsoExtractionError(RuntimeError): |
| 308 | 317 | """ raised by mso_file_extract if parsing MSO/ActiveMIME data failed """ |
| 309 | 318 | |
| ... | ... | @@ -2643,12 +2652,11 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2643 | 2652 | print self.reveal() |
| 2644 | 2653 | else: |
| 2645 | 2654 | print 'No VBA macros found.' |
| 2646 | - except Exception: #TypeError: | |
| 2647 | - #raise | |
| 2648 | - #TODO: print more info if debug mode | |
| 2649 | - #print sys.exc_value | |
| 2650 | - # display the exception with full stack trace for debugging, but do not stop: | |
| 2651 | - traceback.print_exc() | |
| 2655 | + except Exception as exc: | |
| 2656 | + # display the exception with full stack trace for debugging | |
| 2657 | + log.exception('Error processing file %s (%s)' % (self.filename, exc)) | |
| 2658 | + log.debug('Traceback:', exc_info=True) | |
| 2659 | + raise ProcessingError(self.filename, exc) | |
| 2652 | 2660 | print '' |
| 2653 | 2661 | |
| 2654 | 2662 | |
| ... | ... | @@ -2710,12 +2718,11 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2710 | 2718 | result['code_deobfuscated'] = self.reveal() |
| 2711 | 2719 | result['macros'] = macros |
| 2712 | 2720 | result['json_conversion_successful'] = True |
| 2713 | - except Exception: #TypeError: | |
| 2714 | - #raise | |
| 2715 | - #TODO: print more info if debug mode | |
| 2716 | - #print sys.exc_value | |
| 2717 | - # display the exception with full stack trace for debugging, but do not stop: | |
| 2718 | - traceback.print_exc() | |
| 2721 | + except Exception as exc: | |
| 2722 | + # display the exception with full stack trace for debugging | |
| 2723 | + log.exception('Error processing file %s (%s)' % (self.filename, exc)) | |
| 2724 | + log.debug('Traceback:', exc_info=True) | |
| 2725 | + raise ProcessingError(self.filename, exc) | |
| 2719 | 2726 | |
| 2720 | 2727 | return result |
| 2721 | 2728 | |
| ... | ... | @@ -2725,54 +2732,46 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2725 | 2732 | Process a file in triage mode, showing only summary results on one line. |
| 2726 | 2733 | """ |
| 2727 | 2734 | #TODO: replace print by writing to a provided output file (sys.stdout by default) |
| 2728 | - message = '' | |
| 2729 | 2735 | try: |
| 2730 | - if self.type is not None: | |
| 2731 | - #TODO: handle olefile errors, when an OLE file is malformed | |
| 2732 | - if self.detect_vba_macros(): | |
| 2733 | - # print a waiting message only if the output is not redirected to a file: | |
| 2734 | - if sys.stdout.isatty(): | |
| 2735 | - print 'Analysis...\r', | |
| 2736 | - sys.stdout.flush() | |
| 2737 | - self.analyze_macros(show_decoded_strings=show_decoded_strings, | |
| 2738 | - deobfuscate=deobfuscate) | |
| 2739 | - flags = TYPE2TAG[self.type] | |
| 2740 | - macros = autoexec = suspicious = iocs = hexstrings = base64obf = dridex = vba_obf = '-' | |
| 2741 | - if self.contains_macros: macros = 'M' | |
| 2742 | - if self.nb_autoexec: autoexec = 'A' | |
| 2743 | - if self.nb_suspicious: suspicious = 'S' | |
| 2744 | - if self.nb_iocs: iocs = 'I' | |
| 2745 | - if self.nb_hexstrings: hexstrings = 'H' | |
| 2746 | - if self.nb_base64strings: base64obf = 'B' | |
| 2747 | - if self.nb_dridexstrings: dridex = 'D' | |
| 2748 | - if self.nb_vbastrings: vba_obf = 'V' | |
| 2749 | - flags += '%s%s%s%s%s%s%s%s' % (macros, autoexec, suspicious, iocs, hexstrings, | |
| 2750 | - base64obf, dridex, vba_obf) | |
| 2751 | - # old table display: | |
| 2752 | - # macros = autoexec = suspicious = iocs = hexstrings = 'no' | |
| 2753 | - # if nb_macros: macros = 'YES:%d' % nb_macros | |
| 2754 | - # if nb_autoexec: autoexec = 'YES:%d' % nb_autoexec | |
| 2755 | - # if nb_suspicious: suspicious = 'YES:%d' % nb_suspicious | |
| 2756 | - # if nb_iocs: iocs = 'YES:%d' % nb_iocs | |
| 2757 | - # if nb_hexstrings: hexstrings = 'YES:%d' % nb_hexstrings | |
| 2758 | - # # 2nd line = info | |
| 2759 | - # print '%-8s %-7s %-7s %-7s %-7s %-7s' % (self.type, macros, autoexec, suspicious, iocs, hexstrings) | |
| 2760 | - else: | |
| 2761 | - # self.type==None | |
| 2762 | - # file type not OLE nor OpenXML | |
| 2763 | - flags = '?' | |
| 2764 | - message = 'File format not supported' | |
| 2765 | - except Exception: | |
| 2766 | - # another error occurred | |
| 2767 | - #raise | |
| 2768 | - #TODO: print more info if debug mode | |
| 2769 | - #TODO: distinguish real errors from incorrect file types | |
| 2770 | - flags = '!ERROR' | |
| 2771 | - message = sys.exc_value | |
| 2772 | - line = '%-12s %s' % (flags, self.filename) | |
| 2773 | - if message: | |
| 2774 | - line += ' - %s' % message | |
| 2775 | - print line | |
| 2736 | + #TODO: handle olefile errors, when an OLE file is malformed | |
| 2737 | + if self.detect_vba_macros(): | |
| 2738 | + # print a waiting message only if the output is not redirected to a file: | |
| 2739 | + if sys.stdout.isatty(): | |
| 2740 | + print 'Analysis...\r', | |
| 2741 | + sys.stdout.flush() | |
| 2742 | + self.analyze_macros(show_decoded_strings=show_decoded_strings, | |
| 2743 | + deobfuscate=deobfuscate) | |
| 2744 | + flags = TYPE2TAG[self.type] | |
| 2745 | + macros = autoexec = suspicious = iocs = hexstrings = base64obf = dridex = vba_obf = '-' | |
| 2746 | + if self.contains_macros: macros = 'M' | |
| 2747 | + if self.nb_autoexec: autoexec = 'A' | |
| 2748 | + if self.nb_suspicious: suspicious = 'S' | |
| 2749 | + if self.nb_iocs: iocs = 'I' | |
| 2750 | + if self.nb_hexstrings: hexstrings = 'H' | |
| 2751 | + if self.nb_base64strings: base64obf = 'B' | |
| 2752 | + if self.nb_dridexstrings: dridex = 'D' | |
| 2753 | + if self.nb_vbastrings: vba_obf = 'V' | |
| 2754 | + flags += '%s%s%s%s%s%s%s%s' % (macros, autoexec, suspicious, iocs, hexstrings, | |
| 2755 | + base64obf, dridex, vba_obf) | |
| 2756 | + | |
| 2757 | + line = '%-12s %s' % (flags, self.filename) | |
| 2758 | + print line | |
| 2759 | + | |
| 2760 | + # old table display: | |
| 2761 | + # macros = autoexec = suspicious = iocs = hexstrings = 'no' | |
| 2762 | + # if nb_macros: macros = 'YES:%d' % nb_macros | |
| 2763 | + # if nb_autoexec: autoexec = 'YES:%d' % nb_autoexec | |
| 2764 | + # if nb_suspicious: suspicious = 'YES:%d' % nb_suspicious | |
| 2765 | + # if nb_iocs: iocs = 'YES:%d' % nb_iocs | |
| 2766 | + # if nb_hexstrings: hexstrings = 'YES:%d' % nb_hexstrings | |
| 2767 | + # # 2nd line = info | |
| 2768 | + # print '%-8s %-7s %-7s %-7s %-7s %-7s' % (self.type, macros, autoexec, suspicious, iocs, hexstrings) | |
| 2769 | + except Exception as exc: | |
| 2770 | + # display the exception with full stack trace for debugging only | |
| 2771 | + log.debug('Error processing file %s (%s)' % (self.filename, exc), | |
| 2772 | + exc_info=True) | |
| 2773 | + raise ProcessingError(self.filename, exc) | |
| 2774 | + | |
| 2776 | 2775 | |
| 2777 | 2776 | # t = prettytable.PrettyTable(('filename', 'type', 'macros', 'autoexec', 'suspicious', 'ioc', 'hexstrings'), |
| 2778 | 2777 | # header=False, border=False) |
| ... | ... | @@ -2888,39 +2887,53 @@ def main(): |
| 2888 | 2887 | # ignore directory names stored in zip files: |
| 2889 | 2888 | if container and filename.endswith('/'): |
| 2890 | 2889 | continue |
| 2891 | - # Open the file | |
| 2890 | + | |
| 2892 | 2891 | try: |
| 2892 | + # Open the file | |
| 2893 | 2893 | vba_parser = VBA_Parser_CLI(filename, data=data, container=container) |
| 2894 | + | |
| 2895 | + if options.output_mode == 'detailed': | |
| 2896 | + # fully detailed output | |
| 2897 | + vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, | |
| 2898 | + display_code=options.display_code, | |
| 2899 | + hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, | |
| 2900 | + show_deobfuscated_code=options.show_deobfuscated_code, | |
| 2901 | + deobfuscate=options.deobfuscate) | |
| 2902 | + elif options.output_mode in ('triage', 'unspecified'): | |
| 2903 | + # print container name when it changes: | |
| 2904 | + if container != previous_container: | |
| 2905 | + if container is not None: | |
| 2906 | + print '\nFiles in %s:' % container | |
| 2907 | + previous_container = container | |
| 2908 | + # summarized output for triage: | |
| 2909 | + vba_parser.process_file_triage(show_decoded_strings=options.show_decoded_strings, | |
| 2910 | + deobfuscate=options.deobfuscate) | |
| 2911 | + elif options.output_mode == 'json': | |
| 2912 | + json_results.append( | |
| 2913 | + vba_parser.process_file_json(show_decoded_strings=options.show_decoded_strings, | |
| 2914 | + display_code=options.display_code, | |
| 2915 | + hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, | |
| 2916 | + show_deobfuscated_code=options.show_deobfuscated_code)) | |
| 2917 | + else: # (should be impossible) | |
| 2918 | + raise ValueError('unexpected output mode: "{0}"!'.format(options.output_mode)) | |
| 2919 | + count += 1 | |
| 2920 | + | |
| 2894 | 2921 | except FileOpenError as exc: |
| 2895 | - log.exception('Failed to open %s (%s)!' % (filename, exc)) | |
| 2922 | + if options.output_mode in ('triage', 'unspecified'): | |
| 2923 | + print '%-12s %s - File format not supported' % ('?', filename) | |
| 2924 | + else: | |
| 2925 | + log.exception('Failed to open %s -- probably not supported!' % filename) | |
| 2896 | 2926 | return_code = max(return_code, RETURN_OPEN_ERROR) |
| 2897 | - continue | |
| 2927 | + except ProcessingError as exc: | |
| 2928 | + if options.output_mode in ('triage', 'unspecified'): | |
| 2929 | + print '%-12s %s - %s' % ('!ERROR', filename, exc.orig_exception) | |
| 2930 | + else: | |
| 2931 | + log.exception('Error processing file %s (%s)!' | |
| 2932 | + % (filename, exc.orig_exception)) | |
| 2933 | + return_code = max(return_code, RETURN_PARSE_ERROR) | |
| 2934 | + finally: | |
| 2935 | + vba_parser.close() | |
| 2898 | 2936 | |
| 2899 | - if options.output_mode == 'detailed': | |
| 2900 | - # fully detailed output | |
| 2901 | - vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, | |
| 2902 | - display_code=options.display_code, | |
| 2903 | - hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, | |
| 2904 | - show_deobfuscated_code=options.show_deobfuscated_code, | |
| 2905 | - deobfuscate=options.deobfuscate) | |
| 2906 | - elif options.output_mode in ('triage', 'unspecified'): | |
| 2907 | - # print container name when it changes: | |
| 2908 | - if container != previous_container: | |
| 2909 | - if container is not None: | |
| 2910 | - print '\nFiles in %s:' % container | |
| 2911 | - previous_container = container | |
| 2912 | - # summarized output for triage: | |
| 2913 | - vba_parser.process_file_triage(show_decoded_strings=options.show_decoded_strings, | |
| 2914 | - deobfuscate=options.deobfuscate) | |
| 2915 | - elif options.output_mode == 'json': | |
| 2916 | - json_results.append( | |
| 2917 | - vba_parser.process_file_json(show_decoded_strings=options.show_decoded_strings, | |
| 2918 | - display_code=options.display_code, | |
| 2919 | - hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, | |
| 2920 | - show_deobfuscated_code=options.show_deobfuscated_code)) | |
| 2921 | - else: # (should be impossible) | |
| 2922 | - raise ValueError('unexpected output mode: "{0}"!'.format(options.output_mode)) | |
| 2923 | - count += 1 | |
| 2924 | 2937 | if options.output_mode == 'triage': |
| 2925 | 2938 | print '\n(Flags: OpX=OpenXML, XML=Word2003XML, MHT=MHTML, TXT=Text, M=Macros, ' \ |
| 2926 | 2939 | 'A=Auto-executable, S=Suspicious keywords, I=IOCs, H=Hex strings, ' \ | ... | ... |