Commit aee53f45d53eb5027d1b89253c19ad4c1305ce74

Authored by Christian Herdtweck
1 parent 485c9edb

added option --json to olevba; make output modes mutually exclusive

Showing 1 changed file with 27 additions and 11 deletions
oletools/olevba.py
@@ -2520,10 +2520,19 @@ def main(): @@ -2520,10 +2520,19 @@ def main():
2520 help='if the file is a zip archive, open all files from it, using the provided password (requires Python 2.6+)') 2520 help='if the file is a zip archive, open all files from it, using the provided password (requires Python 2.6+)')
2521 parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', 2521 parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*',
2522 help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)') 2522 help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)')
2523 - parser.add_option("-t", '--triage', action="store_true", dest="triage_mode",  
2524 - help='triage mode, display results as a summary table (default for multiple files)')  
2525 - parser.add_option("-d", '--detailed', action="store_true", dest="detailed_mode",  
2526 - help='detailed mode, display full results (default for single file)') 2523 + # output mode; could make this even simpler with add_option(type='choice') but that would make
  2524 + # cmd line interface incompatible...
  2525 + modes = optparse.OptionGroup(parser, title='Output mode (mutually exclusive)')
  2526 + modes.add_option("-t", '--triage', action="store_const", dest="output_mode",
  2527 + const='triage', default='unspecified',
  2528 + help='triage mode, display results as a summary table (default for multiple files)')
  2529 + modes.add_option("-d", '--detailed', action="store_const", dest="output_mode",
  2530 + const='detailed', default='unspecified',
  2531 + help='detailed mode, display full results (default for single file)')
  2532 + modes.add_option("-j", '--json', action="store_const", dest="output_mode",
  2533 + const='json', default='unspecified',
  2534 + help='json mode, detailed in json format (never default)')
  2535 + parser.add_option_group(modes)
2527 parser.add_option("-a", '--analysis', action="store_false", dest="display_code", default=True, 2536 parser.add_option("-a", '--analysis', action="store_false", dest="display_code", default=True,
2528 help='display only analysis results, not the macro source code') 2537 help='display only analysis results, not the macro source code')
2529 parser.add_option("-c", '--code', action="store_true", dest="vba_code_only", default=False, 2538 parser.add_option("-c", '--code', action="store_true", dest="vba_code_only", default=False,
@@ -2546,6 +2555,8 @@ def main(): @@ -2546,6 +2555,8 @@ def main():
2546 # TODO: --novba to disable VBA expressions parsing 2555 # TODO: --novba to disable VBA expressions parsing
2547 2556
2548 (options, args) = parser.parse_args() 2557 (options, args) = parser.parse_args()
  2558 + print options.output_mode
  2559 + sys.exit()
2549 2560
2550 # Print help if no arguments are passed 2561 # Print help if no arguments are passed
2551 if len(args) == 0: 2562 if len(args) == 0:
@@ -2573,8 +2584,9 @@ def main(): @@ -2573,8 +2584,9 @@ def main():
2573 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('Type', 'Macros', 'AutoEx', 'Susp.', 'IOCs', 'HexStr') 2584 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('Type', 'Macros', 'AutoEx', 'Susp.', 'IOCs', 'HexStr')
2574 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('-'*8, '-'*7, '-'*7, '-'*7, '-'*7, '-'*7) 2585 # print '%-8s %-7s %-7s %-7s %-7s %-7s' % ('-'*8, '-'*7, '-'*7, '-'*7, '-'*7, '-'*7)
2575 2586
2576 - # Column headers (except if detailed mode)  
2577 - if not options.detailed_mode or options.triage_mode: 2587 + # Column headers (do not know how many files there will be yet, so if no output_mode
  2588 + # was specified, we will print triage for first file --> need these headers)
  2589 + if options.output_mode in ('triage', 'unspecified'):
2578 print '%-12s %-65s' % ('Flags', 'Filename') 2590 print '%-12s %-65s' % ('Flags', 'Filename')
2579 print '%-12s %-65s' % ('-' * 11, '-' * 65) 2591 print '%-12s %-65s' % ('-' * 11, '-' * 65)
2580 2592
@@ -2589,13 +2601,13 @@ def main(): @@ -2589,13 +2601,13 @@ def main():
2589 continue 2601 continue
2590 # Open the file 2602 # Open the file
2591 vba_parser = VBA_Parser_CLI(filename, data=data, container=container) 2603 vba_parser = VBA_Parser_CLI(filename, data=data, container=container)
2592 - if options.detailed_mode and not options.triage_mode: 2604 + if options.output_mode == 'detailed':
2593 # fully detailed output 2605 # fully detailed output
2594 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, 2606 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings,
2595 display_code=options.display_code, global_analysis=True, #options.global_analysis, 2607 display_code=options.display_code, global_analysis=True, #options.global_analysis,
2596 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, 2608 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only,
2597 show_deobfuscated_code=options.show_deobfuscated_code) 2609 show_deobfuscated_code=options.show_deobfuscated_code)
2598 - else: 2610 + elif options.output_mode in ('triage', 'unspecified'):
2599 # print container name when it changes: 2611 # print container name when it changes:
2600 if container != previous_container: 2612 if container != previous_container:
2601 if container is not None: 2613 if container is not None:
@@ -2603,14 +2615,18 @@ def main(): @@ -2603,14 +2615,18 @@ def main():
2603 previous_container = container 2615 previous_container = container
2604 # summarized output for triage: 2616 # summarized output for triage:
2605 vba_parser.process_file_triage(show_decoded_strings=options.show_decoded_strings) 2617 vba_parser.process_file_triage(show_decoded_strings=options.show_decoded_strings)
  2618 + elif options.output_mode == 'json':
  2619 + raise NotImplementedError('about to add json output!')
  2620 + else: # (should be impossible)
  2621 + raise ValueError('unexpected output mode: "{0}"!'.format(options.output_mode))
2606 count += 1 2622 count += 1
2607 - if not options.detailed_mode or options.triage_mode: 2623 + if options.output_mode == 'triage':
2608 print '\n(Flags: OpX=OpenXML, XML=Word2003XML, MHT=MHTML, TXT=Text, M=Macros, ' \ 2624 print '\n(Flags: OpX=OpenXML, XML=Word2003XML, MHT=MHTML, TXT=Text, M=Macros, ' \
2609 'A=Auto-executable, S=Suspicious keywords, I=IOCs, H=Hex strings, ' \ 2625 'A=Auto-executable, S=Suspicious keywords, I=IOCs, H=Hex strings, ' \
2610 'B=Base64 strings, D=Dridex strings, V=VBA strings, ?=Unknown)\n' 2626 'B=Base64 strings, D=Dridex strings, V=VBA strings, ?=Unknown)\n'
2611 2627
2612 - if count == 1 and not options.triage_mode and not options.detailed_mode:  
2613 - # if options -t and -d were not specified and it's a single file, print details: 2628 + if count == 1 and options.output_mode == 'unspecified':
  2629 + # if options -t, -d and -j were not specified and it's a single file, print details:
2614 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, 2630 vba_parser.process_file(show_decoded_strings=options.show_decoded_strings,
2615 display_code=options.display_code, global_analysis=True, #options.global_analysis, 2631 display_code=options.display_code, global_analysis=True, #options.global_analysis,
2616 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, 2632 hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only,