Commit 3cae86e62d99a29858721ca6f99c8c2cf15be8e4

Authored by decalage2
1 parent e28b2001

olevba: if XLMMacroDeobfuscator is available, use it to extract and deobfuscate XLM macros

Showing 1 changed file with 196 additions and 80 deletions
oletools/olevba.py
... ... @@ -235,7 +235,7 @@ from __future__ import print_function
235 235 # for issue #619)
236 236 # 2021-04-14 PL: - added detection of Workbook_BeforeClose (issue #518)
237 237  
238   -__version__ = '0.56.2'
  238 +__version__ = '0.60.dev2'
239 239  
240 240 #------------------------------------------------------------------------------
241 241 # TODO:
... ... @@ -310,6 +310,18 @@ import colorclass
310 310 if os.name == 'nt':
311 311 colorclass.Windows.enable(auto_colors=True)
312 312  
  313 +from pyparsing import \
  314 + CaselessKeyword, CaselessLiteral, Combine, Forward, Literal, \
  315 + Optional, QuotedString,Regex, Suppress, Word, WordStart, \
  316 + alphanums, alphas, hexnums,nums, opAssoc, srange, \
  317 + infixNotation, ParserElement
  318 +
  319 +# attempt to import XLMMacroDeobfuscator (optional)
  320 +try:
  321 + from XLMMacroDeobfuscator import deobfuscator as xlmdeobfuscator
  322 + XLMDEOBFUSCATOR = True
  323 +except ImportError:
  324 + XLMDEOBFUSCATOR = False
313 325  
314 326 # IMPORTANT: it should be possible to run oletools directly as scripts
315 327 # in any directory without installing them with pip or setup.py.
... ... @@ -326,17 +338,14 @@ if _parent_dir not in sys.path:
326 338 import olefile
327 339 from oletools.thirdparty.tablestream import tablestream
328 340 from oletools.thirdparty.xglob import xglob, PathNotFoundException
329   -from pyparsing import \
330   - CaselessKeyword, CaselessLiteral, Combine, Forward, Literal, \
331   - Optional, QuotedString,Regex, Suppress, Word, WordStart, \
332   - alphanums, alphas, hexnums,nums, opAssoc, srange, \
333   - infixNotation, ParserElement
  341 +from oletools.thirdparty.oledump.plugin_biff import cBIFF
334 342 from oletools import ppt_parser
335 343 from oletools import oleform
336 344 from oletools import rtfobj
337 345 from oletools import crypto
338 346 from oletools.common.io_encoding import ensure_stdout_handles_unicode
339 347 from oletools.common import codepages
  348 +from oletools import ftguess
340 349  
341 350 # === PYTHON 2+3 SUPPORT ======================================================
342 351  
... ... @@ -2710,7 +2719,8 @@ class VBA_Parser(object):
2710 2719 self.type = None
2711 2720 self.vba_projects = None
2712 2721 self.vba_forms = None
2713   - self.contains_macros = None # will be set to True or False by detect_macros
  2722 + self.contains_vba_macros = None # will be set to True or False by detect_vba_macros
  2723 + self.contains_xlm_macros = None # will be set to True or False by detect_xlm_macros
2714 2724 self.vba_code_all_modules = None # to store the source code of all modules
2715 2725 # list of tuples for each module: (subfilename, stream_path, vba_filename, vba_code)
2716 2726 self.modules = None
... ... @@ -2736,9 +2746,13 @@ class VBA_Parser(object):
2736 2746 self.vba_stomping_detected = None
2737 2747 # will be set to True or False by detect_is_encrypted method
2738 2748 self.is_encrypted = False
  2749 + # TODO: those are disabled for now:
2739 2750 self.xlm_macrosheet_found = False
2740 2751 self.template_injection_found = False
2741 2752  
  2753 + # call ftguess to identify file type:
  2754 + self.ftg = ftguess.FileTypeGuesser(self.filename, data=data)
  2755 + log.debug('ftguess: file type=%s - container=%s' % (self.ftg.ftype.name, self.ftg.container))
2742 2756 # if filename is None:
2743 2757 # if isinstance(_file, basestring):
2744 2758 # if len(_file) < olefile.MINIMAL_OLEFILE_SIZE:
... ... @@ -2752,7 +2766,7 @@ class VBA_Parser(object):
2752 2766 self.open_ole(_file)
2753 2767  
2754 2768 # if this worked, try whether it is a ppt file (special ole file)
2755   - # TODO: instead of this we should have a function to test if it is a PPT
  2769 + # TODO: instead of this we should have a function to test if it is a PPT (e.g. using ftguess)
2756 2770 self.open_ppt()
2757 2771 if self.type is None and zipfile.is_zipfile(_file):
2758 2772 # Zip file, which may be an OpenXML document
... ... @@ -2840,55 +2854,55 @@ class VBA_Parser(object):
2840 2854 #TODO: if the zip file is encrypted, suggest to use the -z option, or try '-z infected' automatically
2841 2855 # check each file within the zip if it is an OLE file, by reading its magic:
2842 2856 for subfile in z.namelist():
2843   - log.debug("subfile {}".format(subfile))
  2857 + log.debug("OpenXML subfile {}".format(subfile))
2844 2858 with z.open(subfile) as file_handle:
2845   - found_ole = False
2846   - template_injection_detected = False
2847   - xml_macrosheet_found = False
  2859 + # found_ole = False
  2860 + # template_injection_detected = False
  2861 + # xml_macrosheet_found = False
2848 2862 magic = file_handle.read(len(olefile.MAGIC))
2849 2863 if magic == olefile.MAGIC:
2850   - found_ole = True
2851   - # in case we did not find an OLE file,
2852   - # there could be a XLM macrosheet or a template injection attempt
2853   - if not found_ole:
2854   - read_all_file = file_handle.read()
2855   - # try to detect template injection attempt
2856   - # https://ired.team/offensive-security/initial-access/phishing-with-ms-office/inject-macros-from-a-remote-dotm-template-docx-with-macros
2857   - subfile_that_can_contain_templates = "word/_rels/settings.xml.rels"
2858   - if subfile == subfile_that_can_contain_templates:
2859   - regex_template = b"Type=\"http://schemas\.openxmlformats\.org/officeDocument/\d{4}/relationships/attachedTemplate\"\s+Target=\"(.+?)\""
2860   - template_injection_found = re.search(regex_template, read_all_file)
2861   - if template_injection_found:
2862   - injected_template_url = template_injection_found.group(1).decode()
2863   - message = "Found injected template in subfile {}. Template URL: {}"\
2864   - "".format(subfile_that_can_contain_templates, injected_template_url)
2865   - log.info(message)
2866   - template_injection_detected = True
2867   - self.template_injection_found = True
2868   - # try to find a XML macrosheet
2869   - macro_sheet_footer = b"</xm:macrosheet>"
2870   - len_macro_sheet_footer = len(macro_sheet_footer)
2871   - last_bytes_to_check = read_all_file[-len_macro_sheet_footer:]
2872   - if last_bytes_to_check == macro_sheet_footer:
2873   - message = "Found XLM Macro in subfile: {}".format(subfile)
2874   - log.info(message)
2875   - xml_macrosheet_found = True
2876   - self.xlm_macrosheet_found = True
2877   -
2878   - if found_ole or xml_macrosheet_found or template_injection_detected:
2879   - log.debug('Opening OLE file %s within zip' % subfile)
2880   - with z.open(subfile) as file_handle:
2881   - ole_data = file_handle.read()
2882   - try:
2883   - self.append_subfile(filename=subfile, data=ole_data)
2884   - except OlevbaBaseException as exc:
2885   - if self.relaxed:
2886   - log.info('%s is not a valid OLE file (%s)' % (subfile, exc))
2887   - log.debug('Trace:', exc_info=True)
2888   - continue
2889   - else:
2890   - raise SubstreamOpenError(self.filename, subfile,
2891   - exc)
  2864 + # found_ole = True
  2865 + # # in case we did not find an OLE file,
  2866 + # # there could be a XLM macrosheet or a template injection attempt
  2867 + # if not found_ole:
  2868 + # read_all_file = file_handle.read()
  2869 + # # try to detect template injection attempt
  2870 + # # https://ired.team/offensive-security/initial-access/phishing-with-ms-office/inject-macros-from-a-remote-dotm-template-docx-with-macros
  2871 + # subfile_that_can_contain_templates = "word/_rels/settings.xml.rels"
  2872 + # if subfile == subfile_that_can_contain_templates:
  2873 + # regex_template = b"Type=\"http://schemas\.openxmlformats\.org/officeDocument/\d{4}/relationships/attachedTemplate\"\s+Target=\"(.+?)\""
  2874 + # template_injection_found = re.search(regex_template, read_all_file)
  2875 + # if template_injection_found:
  2876 + # injected_template_url = template_injection_found.group(1).decode()
  2877 + # message = "Found injected template in subfile {}. Template URL: {}"\
  2878 + # "".format(subfile_that_can_contain_templates, injected_template_url)
  2879 + # log.info(message)
  2880 + # template_injection_detected = True
  2881 + # self.template_injection_found = True
  2882 + # # try to find a XML macrosheet
  2883 + # macro_sheet_footer = b"</xm:macrosheet>"
  2884 + # len_macro_sheet_footer = len(macro_sheet_footer)
  2885 + # last_bytes_to_check = read_all_file[-len_macro_sheet_footer:]
  2886 + # if last_bytes_to_check == macro_sheet_footer:
  2887 + # message = "Found XLM Macro in subfile: {}".format(subfile)
  2888 + # log.info(message)
  2889 + # xml_macrosheet_found = True
  2890 + # self.xlm_macrosheet_found = True
  2891 + #
  2892 + # if found_ole or xml_macrosheet_found or template_injection_detected:
  2893 + log.debug('Opening OLE file %s within zip' % subfile)
  2894 + with z.open(subfile) as file_handle:
  2895 + ole_data = file_handle.read()
  2896 + try:
  2897 + self.append_subfile(filename=subfile, data=ole_data)
  2898 + except OlevbaBaseException as exc:
  2899 + if self.relaxed:
  2900 + log.info('%s is not a valid OLE file (%s)' % (subfile, exc))
  2901 + log.debug('Trace:', exc_info=True)
  2902 + continue
  2903 + else:
  2904 + raise SubstreamOpenError(self.filename, subfile,
  2905 + exc)
2892 2906 z.close()
2893 2907 # set type only if parsing succeeds
2894 2908 self.type = TYPE_OpenXML
... ... @@ -3134,7 +3148,7 @@ class VBA_Parser(object):
3134 3148 if s.startswith(b'E'):
3135 3149 xlm_macros.append('Formula or Macro: %s' % bytes2str(s[1:]))
3136 3150 if xlm_macro_found:
3137   - self.contains_macros = True
  3151 + self.contains_xlm_macros = True
3138 3152 self.xlm_macros = xlm_macros
3139 3153 self.type = TYPE_SLK
3140 3154  
... ... @@ -3150,7 +3164,7 @@ class VBA_Parser(object):
3150 3164 # On Python 2, store it as a raw bytes string
3151 3165 # On Python 3, convert it to unicode assuming it was encoded with UTF-8
3152 3166 self.vba_code_all_modules = bytes2str(data)
3153   - self.contains_macros = True
  3167 + self.contains_vba_macros = True
3154 3168 # set type only if parsing succeeds
3155 3169 self.type = TYPE_TEXT
3156 3170  
... ... @@ -3257,6 +3271,20 @@ class VBA_Parser(object):
3257 3271 self.vba_projects.append((vba_root, project_path, dir_path))
3258 3272 return self.vba_projects
3259 3273  
  3274 + def detect_macros(self):
  3275 + """
  3276 + Detect the potential presence of VBA or Excel4/XLM macros in the file,
  3277 + by calling detect_vba_macros and detect_xlm_macros.
  3278 + (if the no_xlm option is set, XLM macros are not checked)
  3279 +
  3280 + :return: bool, True if at least one VBA project has been found, False otherwise
  3281 + """
  3282 + vba = self.detect_vba_macros()
  3283 + xlm = False
  3284 + if not self.no_xlm:
  3285 + xlm = self.detect_xlm_macros()
  3286 + return (vba or xlm)
  3287 +
3260 3288 def detect_vba_macros(self):
3261 3289 """
3262 3290 Detect the potential presence of VBA macros in the file, by checking
... ... @@ -3273,28 +3301,28 @@ class VBA_Parser(object):
3273 3301 :return: bool, True if at least one VBA project has been found, False otherwise
3274 3302 """
3275 3303 log.debug("detect vba macros")
3276   - #TODO: return None or raise exception if format not supported
3277   - #TODO: return the number of VBA projects found instead of True/False?
  3304 + # TODO: return None or raise exception if format not supported
  3305 + # TODO: return the number of VBA projects found instead of True/False?
3278 3306 # if this method was already called, return the previous result:
3279   - if self.contains_macros is not None:
3280   - return self.contains_macros
  3307 + if self.contains_vba_macros is not None:
  3308 + return self.contains_vba_macros
3281 3309 # if OpenXML/PPT, check all the OLE subfiles:
3282 3310 if self.ole_file is None:
3283 3311 for ole_subfile in self.ole_subfiles:
3284 3312 log.debug("ole subfile {}".format(ole_subfile))
3285 3313 ole_subfile.no_xlm = self.no_xlm
3286 3314 if ole_subfile.detect_vba_macros():
3287   - self.contains_macros = True
  3315 + self.contains_vba_macros = True
3288 3316 return True
3289 3317 # otherwise, no macro found:
3290   - self.contains_macros = False
  3318 + self.contains_vba_macros = False
3291 3319 return False
3292 3320 # otherwise it's an OLE file, find VBA projects:
3293 3321 vba_projects = self.find_vba_projects()
3294 3322 if len(vba_projects) == 0:
3295   - self.contains_macros = False
  3323 + self.contains_vba_macros = False
3296 3324 else:
3297   - self.contains_macros = True
  3325 + self.contains_vba_macros = True
3298 3326 # Also look for VBA code in any stream including orphans
3299 3327 # (happens in some malformed files)
3300 3328 ole = self.ole_file
... ... @@ -3318,26 +3346,100 @@ class VBA_Parser(object):
3318 3346 log.debug(repr(data))
3319 3347 if b'Attribut\x00' in data:
3320 3348 log.debug('Found VBA compressed code')
3321   - self.contains_macros = True
  3349 + self.contains_vba_macros = True
3322 3350 except IOError as exc:
3323 3351 if self.relaxed:
3324 3352 log.info('Error when reading OLE Stream %r' % d.name)
3325 3353 log.debug('Trace:', exc_trace=True)
3326 3354 else:
3327 3355 raise SubstreamOpenError(self.filename, d.name, exc)
3328   - if (not self.no_xlm) and self.detect_xlm_macros():
3329   - self.contains_macros = True
3330   - return self.contains_macros
  3356 + return self.contains_vba_macros
3331 3357  
3332 3358 def detect_xlm_macros(self):
  3359 + """
  3360 + Detect the potential presence of Excel 4/XLM macros in the file, by checking
  3361 + if it contains a macro worksheet. Both OLE and OpenXML files are supported.
  3362 + Only Excel files may contain XLM macros, and also SLK and CSV files.
  3363 +
  3364 + If XLMMacroDeobfuscator is available, it will be used. Otherwise plugin_biff
  3365 + is used as fallback (plugin_biff only supports OLE files, not XLSX or XLSB)
  3366 +
  3367 + :return: bool, True if at least one macro worksheet has been found, False otherwise
  3368 + """
3333 3369 log.debug("detect xlm macros")
  3370 + # if this method was already called, return the previous result:
  3371 + if self.contains_xlm_macros is not None:
  3372 + return self.contains_xlm_macros
3334 3373 # if this is a SLK file, the analysis was done in open_slk:
3335 3374 if self.type == TYPE_SLK:
3336   - return self.contains_macros
3337   - from oletools.thirdparty.oledump.plugin_biff import cBIFF
  3375 + return self.contains_xlm_macros
  3376 + # TODO: check also CSV files for formulas?
3338 3377 self.xlm_macros = []
  3378 + # check if the file is Excel, otherwise return False
  3379 + if not self.ftg.is_excel():
  3380 + self.contains_xlm_macros = False
  3381 + return False
  3382 + if XLMDEOBFUSCATOR:
  3383 + # XLMMacroDeobfuscator is available, use it:
  3384 + # But it only works with files on disk for now
  3385 + if not self.file_on_disk:
  3386 + log.warning('XLMMacroDeobfuscator only works with files on disk, not in memory. Analysis might be less complete.')
  3387 + else:
  3388 + try:
  3389 + return self._extract_xlm_xlmdeobf()
  3390 + except Exception:
  3391 + log.error('Error when running XLMMacroDeobfuscator')
  3392 + # fall back to plugin_biff:
3339 3393 if self.ole_file is None:
  3394 + # TODO: handle OpenXML here
3340 3395 return False
  3396 + return self._extract_xlm_plugin_biff()
  3397 +
  3398 + def _extract_xlm_xlmdeobf(self):
  3399 + """
  3400 + Run XLMMacroDeobfuscator to detect and extract XLM macros
  3401 + :return: bool, True if at least one macro worksheet has been found, False otherwise
  3402 + """
  3403 + log.debug('Calling XLMMacroDeobfuscator to detect and extract XLM macros')
  3404 + xlmdeobfuscator.SILENT = True
  3405 + # we build the output as a list of strings:
  3406 + xlm = ["RAW EXCEL4/XLM MACRO FORMULAS:"]
  3407 + # First, extract only formulas without emulation
  3408 + result = xlmdeobfuscator.process_file(file=self.filename,
  3409 + noninteractive=True,
  3410 + noindent=True,
  3411 + # output_formula_format='CELL:[[CELL_ADDR]], [[INT-FORMULA]]',
  3412 + return_deobfuscated=True,
  3413 + timeout=30,
  3414 + extract_only=True,
  3415 + )
  3416 + if len(result) == 0:
  3417 + # no XLM macro was found
  3418 + self.contains_xlm_macros = False
  3419 + return False
  3420 + xlm += result
  3421 + xlm.append('- ' * 38)
  3422 + xlm.append('EMULATION - DEOBFUSCATED EXCEL4/XLM MACRO FORMULAS:')
  3423 + result = xlmdeobfuscator.process_file(file=sys.argv[1],
  3424 + noninteractive=True,
  3425 + noindent=True,
  3426 + # output_formula_format='CELL:[[CELL_ADDR]], [[INT-FORMULA]]',
  3427 + return_deobfuscated=True,
  3428 + timeout=30,
  3429 + )
  3430 + xlm += result
  3431 + log.debug(xlm)
  3432 + self.xlm_macros = xlm
  3433 + self.contains_xlm_macros = True
  3434 + return True
  3435 +
  3436 +
  3437 + def _extract_xlm_plugin_biff(self):
  3438 + """
  3439 + Run plugin_biff to detect and extract XLM macros
  3440 + :return: bool, True if at least one macro worksheet has been found, False otherwise
  3441 + """
  3442 + log.debug('_extract_xlm_plugin_biff')
3341 3443 for excel_stream in ('Workbook', 'Book'):
3342 3444 if self.ole_file.exists(excel_stream):
3343 3445 log.debug('Found Excel stream %r' % excel_stream)
... ... @@ -3360,9 +3462,11 @@ class VBA_Parser(object):
3360 3462 # ref: https://inquest.net/blog/2020/03/18/Getting-Sneakier-Hidden-Sheets-Data-Connections-and-XLM-Macros
3361 3463 biff_plugin = cBIFF(name=[excel_stream], stream=data, options='-o DCONN -s')
3362 3464 self.xlm_macros += biff_plugin.Analyze()
  3465 + self.contains_xlm_macros = True
3363 3466 return True
3364 3467 except:
3365 3468 log.exception('Error when running oledump.plugin_biff, please report to %s' % URL_OLEVBA_ISSUES)
  3469 + self.contains_xlm_macros = False
3366 3470 return False
3367 3471  
3368 3472 def detect_is_encrypted(self):
... ... @@ -3420,6 +3524,12 @@ class VBA_Parser(object):
3420 3524 for ole_subfile in self.ole_subfiles:
3421 3525 for results in ole_subfile.extract_macros():
3422 3526 yield results
  3527 + # we also need to yield XLM macros
  3528 + if self.xlm_macros:
  3529 + vba_code = ''
  3530 + for line in self.xlm_macros:
  3531 + vba_code += "' " + line + '\n'
  3532 + yield ('xlm_macro', 'xlm_macro', 'xlm_macro.txt', vba_code)
3423 3533 else:
3424 3534 # This is an OLE file:
3425 3535 self.find_vba_projects()
... ... @@ -3528,12 +3638,16 @@ class VBA_Parser(object):
3528 3638  
3529 3639 def analyze_macros(self, show_decoded_strings=False, deobfuscate=False):
3530 3640 """
3531   - runs extract_macros and analyze the source code of all VBA macros
  3641 + runs extract_macros and analyze the source code of all VBA+XLM macros
3532 3642 found in the file.
3533 3643 All results are stored in self.analysis_results.
3534 3644 If called more than once, simply returns the previous results.
  3645 +
  3646 + :return: list of tuples (type, keyword, description)
  3647 + (type = 'AutoExec', 'Suspicious', 'IOC', 'Hex String', 'Base64 String' or 'Dridex String')
3535 3648 """
3536   - if self.detect_vba_macros():
  3649 + # Check if there are VBA or XLM macros:
  3650 + if self.detect_macros():
3537 3651 # if the analysis was already done, avoid doing it twice:
3538 3652 if self.analysis_results is not None:
3539 3653 return self.analysis_results
... ... @@ -3552,12 +3666,13 @@ class VBA_Parser(object):
3552 3666 'this may have been used to hide malicious code'
3553 3667 scanner.suspicious_keywords.append((keyword, description))
3554 3668 scanner.results.append(('Suspicious', keyword, description))
3555   - if self.xlm_macrosheet_found:
  3669 + if self.contains_xlm_macros:
3556 3670 log.debug('adding XLM macrosheet found to suspicious keywords')
3557   - keyword = 'XLM macrosheet'
3558   - description = 'XLM macrosheet found. It could contain malicious code'
  3671 + keyword = 'XLM macro'
  3672 + description = 'XLM macro found. It may contain malicious code'
3559 3673 scanner.suspicious_keywords.append((keyword, description))
3560 3674 scanner.results.append(('Suspicious', keyword, description))
  3675 + # TODO: this has been temporarily disabled
3561 3676 if self.template_injection_found:
3562 3677 log.debug('adding Template Injection to suspicious keywords')
3563 3678 keyword = 'Template Injection'
... ... @@ -4029,7 +4144,7 @@ class VBA_Parser_CLI(VBA_Parser):
4029 4144 try:
4030 4145 #TODO: handle olefile errors, when an OLE file is malformed
4031 4146 print('Type: %s'% self.type)
4032   - if self.detect_vba_macros():
  4147 + if self.detect_macros():
4033 4148 # run analysis before displaying VBA code, in order to colorize found keywords
4034 4149 self.run_analysis(show_decoded_strings=show_decoded_strings, deobfuscate=deobfuscate)
4035 4150 #print 'Contains VBA Macros:'
... ... @@ -4109,7 +4224,7 @@ class VBA_Parser_CLI(VBA_Parser):
4109 4224 print('MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):\n\n')
4110 4225 print(self.reveal())
4111 4226 else:
4112   - print('No VBA macros found.')
  4227 + print('No VBA or XLM macros found.')
4113 4228 except OlevbaBaseException:
4114 4229 raise
4115 4230 except Exception as exc:
... ... @@ -4164,7 +4279,7 @@ class VBA_Parser_CLI(VBA_Parser):
4164 4279 #TODO: handle olefile errors, when an OLE file is malformed
4165 4280 result['type'] = self.type
4166 4281 macros = []
4167   - if self.detect_vba_macros():
  4282 + if self.detect_macros():
4168 4283 for (subfilename, stream_path, vba_filename, vba_code) in self.extract_all_macros():
4169 4284 curr_macro = {}
4170 4285 if hide_attributes:
... ... @@ -4207,7 +4322,7 @@ class VBA_Parser_CLI(VBA_Parser):
4207 4322 #TODO: replace print by writing to a provided output file (sys.stdout by default)
4208 4323 try:
4209 4324 #TODO: handle olefile errors, when an OLE file is malformed
4210   - if self.detect_vba_macros():
  4325 + if self.detect_macros():
4211 4326 # print a waiting message only if the output is not redirected to a file:
4212 4327 if sys.stdout.isatty():
4213 4328 print('Analysis...\r', end='')
... ... @@ -4216,7 +4331,7 @@ class VBA_Parser_CLI(VBA_Parser):
4216 4331 deobfuscate=deobfuscate)
4217 4332 flags = TYPE2TAG[self.type]
4218 4333 macros = autoexec = suspicious = iocs = hexstrings = base64obf = dridex = vba_obf = '-'
4219   - if self.contains_macros: macros = 'M'
  4334 + if self.contains_vba_macros: macros = 'M'
4220 4335 if self.nb_autoexec: autoexec = 'A'
4221 4336 if self.nb_suspicious: suspicious = 'S'
4222 4337 if self.nb_iocs: iocs = 'I'
... ... @@ -4351,6 +4466,7 @@ def parse_args(cmd_line_args=None):
4351 4466 def process_file(filename, data, container, options, crypto_nesting=0):
4352 4467 """
4353 4468 Part of main function that processes a single file.
  4469 + This is meant to be used only for the command-line interface of olevba
4354 4470  
4355 4471 This handles exceptions and encryption.
4356 4472  
... ...