def trace(self, entrypoint='*auto'): # Clear out any intermediate IOCs from a previous run. vba_context.intermediate_iocs = set() # TODO: use the provided entrypoint # Create the global context for the engine context = Context(_globals=self.globals, engine=self, doc_vars=self.doc_vars, loaded_excel=self.loaded_excel, filename=self.filename, metadata=self.metadata) context.is_vbscript = self.is_vbscript # Add any URLs we can pull directly from the file being analyzed. fname = self.filename is_data = False if ((fname is None) or (len(fname.strip()) == 0)): fname = self.data is_data = True direct_urls = read_ole_fields.pull_urls_office97(fname, is_data) for url in direct_urls: context.save_intermediate_iocs(url) # Save the true names of imported external functions. for func_name in self.externals.keys(): func = self.externals[func_name] context.dll_func_true_names[func.name] = func.alias_name # Save the document text in the proper variable in the context. context.globals["ActiveDocument.Content.Text".lower()] = "\n".join(self.doc_text) context.globals["ActiveDocument.Range.Text".lower()] = "\n".join(self.doc_text) context.globals["ActiveDocument.Range".lower()] = "\n".join(self.doc_text) context.globals["ActiveDocument.Content.Start".lower()] = 0 context.globals["ActiveDocument.Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["ActiveDocument.Paragraphs".lower()] = self.doc_text context.globals["ThisDocument.Content.Text".lower()] = "\n".join(self.doc_text) context.globals["ThisDocument.Range.Text".lower()] = "\n".join(self.doc_text) context.globals["ThisDocument.Range".lower()] = "\n".join(self.doc_text) context.globals["ThisDocument.Content.Start".lower()] = 0 context.globals["ThisDocument.Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["ThisDocument.Paragraphs".lower()] = self.doc_text context.globals["['ActiveDocument'].Content.Text".lower()] = "\n".join(self.doc_text) context.globals["['ActiveDocument'].Range.Text".lower()] = "\n".join(self.doc_text) context.globals["['ActiveDocument'].Range".lower()] = "\n".join(self.doc_text) context.globals["['ActiveDocument'].Content.Start".lower()] = 0 context.globals["['ActiveDocument'].Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["['ActiveDocument'].Paragraphs".lower()] = self.doc_text context.globals["['ThisDocument'].Content.Text".lower()] = "\n".join(self.doc_text) context.globals["['ThisDocument'].Range.Text".lower()] = "\n".join(self.doc_text) context.globals["['ThisDocument'].Range".lower()] = "\n".join(self.doc_text) context.globals["['ThisDocument'].Content.Start".lower()] = 0 context.globals["['ThisDocument'].Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["['ThisDocument'].Paragraphs".lower()] = self.doc_text # Fake up some comments. # TODO: Figure out how to actually read the comments. context.globals["ActiveDocument.Comments".lower()] = ["Comment 1", "Comment 2"] context.globals["ThisDocument.Comments".lower()] = ["Comment 1", "Comment 2"] # reset the actions list, in case it is called several times self.actions = [] # Track the external functions called. self.external_funcs = self._get_external_funcs() context.external_funcs = self.external_funcs # First emulate any Visual Basic that appears outside of subs/funcs. log.info("Emulating loose statements...") done_emulation = False for m in self.modules: if (m.eval(context=context)): context.dump_all_files(autoclose=True) done_emulation = True # Look for hardcoded entry functions. for entry_point in self.entry_points: entry_point = entry_point.lower() log.debug("Trying entry point " + entry_point) if entry_point in self.globals: context.report_action('Found Entry Point', str(entry_point), '') self.globals[entry_point].eval(context=context) context.dump_all_files(autoclose=True) done_emulation = True # Look for callback functions that can act as entry points. for name in self.globals.keys(): # Look for functions whose name ends with a callback suffix. for suffix in self.callback_suffixes: # Is this a callback? if (str(name).lower().endswith(suffix.lower())): # Is this a function? item = self.globals[name] if (isinstance(item, Function) or isinstance(item, Sub)): # Emulate it. context.report_action('Found Entry Point', str(name), '') item.eval(context=context) context.dump_all_files(autoclose=True) done_emulation = True # Did we find an entry point? if (not done_emulation): # Count the # of subroutines in the document. only_sub = None sub_name = None sub_count = 0 for name in self.globals.keys(): item = self.globals[name] if (isinstance(item, Sub)): only_sub = item sub_name = name sub_count += 1 # If there is only 1 subroutine, emulate that. if (sub_count == 1): context.report_action('Found Entry Point', str(sub_name), '') only_sub.eval(context=context) context.dump_all_files(autoclose=True)
def trace(self, entrypoint='*auto'): # Clear out any intermediate IOCs from a previous run. vba_context.intermediate_iocs = set() # TODO: use the provided entrypoint # Create the global context for the engine context = Context(_globals=self.globals, engine=self, doc_vars=self.doc_vars, loaded_excel=self.loaded_excel, filename=self.filename, metadata=self.metadata) context.is_vbscript = self.is_vbscript # Add any URLs we can pull directly from the file being analyzed. fname = self.filename is_data = False if ((fname is None) or (len(fname.strip()) == 0)): fname = self.data is_data = True direct_urls = read_ole_fields.pull_urls_office97(fname, is_data, self.vba) for url in direct_urls: context.save_intermediate_iocs(url) # Save the true names of imported external functions. for func_name in self.externals.keys(): func = self.externals[func_name] context.dll_func_true_names[func.name] = func.alias_name # Save the document tables in the context. context.globals["__DOC_TABLE_CONTENTS__"] = self.doc_tables # Save the document text in the proper variable in the context. context.globals["Me.Content.Text".lower()] = "\n".join(self.doc_text) context.globals["Me.Range.Text".lower()] = "\n".join(self.doc_text) context.globals["Me.Range".lower()] = "\n".join(self.doc_text) context.globals["Me.Content.Start".lower()] = 0 context.globals["Me.Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["Me.Paragraphs".lower()] = self.doc_text context.globals["ActiveDocument.Content.Text".lower()] = "\n".join(self.doc_text) context.globals["ActiveDocument.Range.Text".lower()] = "\n".join(self.doc_text) context.globals["ActiveDocument.Range".lower()] = "\n".join(self.doc_text) context.globals["ActiveDocument.Content.Start".lower()] = 0 context.globals["ActiveDocument.Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["ActiveDocument.Paragraphs".lower()] = self.doc_text context.globals["ThisDocument.Content.Text".lower()] = "\n".join(self.doc_text) context.globals["ThisDocument.Range.Text".lower()] = "\n".join(self.doc_text) context.globals["ThisDocument.Range".lower()] = "\n".join(self.doc_text) context.globals["ThisDocument.Content.Start".lower()] = 0 context.globals["ThisDocument.Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["ThisDocument.Paragraphs".lower()] = self.doc_text context.globals["['Me'].Content.Text".lower()] = "\n".join(self.doc_text) context.globals["['Me'].Range.Text".lower()] = "\n".join(self.doc_text) context.globals["['Me'].Range".lower()] = "\n".join(self.doc_text) context.globals["['Me'].Content.Start".lower()] = 0 context.globals["['Me'].Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["['Me'].Paragraphs".lower()] = self.doc_text context.globals["['ActiveDocument'].Content.Text".lower()] = "\n".join(self.doc_text) context.globals["['ActiveDocument'].Range.Text".lower()] = "\n".join(self.doc_text) context.globals["['ActiveDocument'].Range".lower()] = "\n".join(self.doc_text) context.globals["['ActiveDocument'].Content.Start".lower()] = 0 context.globals["['ActiveDocument'].Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["['ActiveDocument'].Paragraphs".lower()] = self.doc_text context.globals["['ThisDocument'].Content.Text".lower()] = "\n".join(self.doc_text) context.globals["['ThisDocument'].Range.Text".lower()] = "\n".join(self.doc_text) context.globals["['ThisDocument'].Range".lower()] = "\n".join(self.doc_text) context.globals["['ThisDocument'].Content.Start".lower()] = 0 context.globals["['ThisDocument'].Content.End".lower()] = len("\n".join(self.doc_text)) context.globals["['ThisDocument'].Paragraphs".lower()] = self.doc_text context.globals["['ActiveDocument'].Characters".lower()] = list("\n".join(self.doc_text)) context.globals["ActiveDocument.Characters".lower()] = list("\n".join(self.doc_text)) context.globals["ActiveDocument.Characters.Count".lower()] = long(len(self.doc_text)) context.globals["Count".lower()] = 1 context.globals[".Pages.Count".lower()] = 1 context.globals["me.Pages.Count".lower()] = 1 context.globals["['ThisDocument'].Characters".lower()] = list("\n".join(self.doc_text)) context.globals["ThisDocument.Characters".lower()] = list("\n".join(self.doc_text)) # Break out document words. doc_words = [] for word in re.split(r"[ \n]", "\n".join(self.doc_text)): word = word.strip() if (word.startswith("-")): word = word[1:] doc_words.append("-") doc_words.append(word.strip()) context.globals["ActiveDocument.Words".lower()] = doc_words context.globals["ThisDocument.Words".lower()] = doc_words # Fake up some comments if needed. if (self.comments is None): context.globals["ActiveDocument.Comments".lower()] = ["Comment 1", "Comment 2"] context.globals["ThisDocument.Comments".lower()] = ["Comment 1", "Comment 2"] else: context.globals["ActiveDocument.Comments".lower()] = self.comments context.globals["ThisDocument.Comments".lower()] = self.comments # reset the actions list, in case it is called several times self.actions = [] # Track the external functions called. self.external_funcs = self._get_external_funcs() context.external_funcs = self.external_funcs # First emulate any Visual Basic that appears outside of subs/funcs. log.info("Emulating loose statements...") done_emulation = False for m in self.modules: if (m.eval(context=context)): context.dump_all_files(autoclose=True) done_emulation = context.got_actions # Look for hardcoded entry functions. for entry_point in self.entry_points: entry_point = entry_point.lower() log.debug("Trying entry point " + entry_point) if entry_point in self.globals: context.report_action('Found Entry Point', str(entry_point), '') self.globals[entry_point].eval(context=context) context.dump_all_files(autoclose=True) done_emulation = True # Look for callback functions that can act as entry points. for name in self.globals.keys(): # Look for functions whose name ends with a callback suffix. for suffix in self.callback_suffixes: # Is this a callback? if (str(name).lower().endswith(suffix.lower())): # Is this a function? item = self.globals[name] if (isinstance(item, Function) or isinstance(item, Sub)): # Emulate it. context.report_action('Found Entry Point', str(name), '') item.eval(context=context) context.dump_all_files(autoclose=True) done_emulation = True # Did we find an entry point? if (not done_emulation): # Try heuristics to find possible entry points. log.warn("No entry points found. Using heuristics to find entry points...") # Find any 0 argument subroutines. We will try emulating these as potential entry points. zero_arg_subs = [] for name in self.globals.keys(): item = self.globals[name] if ((isinstance(item, Sub)) and (len(item.params) == 0)): zero_arg_subs.append(item) # Emulate all 0 argument subroutines as potential entry points. for only_sub in zero_arg_subs: sub_name = only_sub.name context.report_action('Found Heuristic Entry Point', str(sub_name), '') only_sub.eval(context=context) context.dump_all_files(autoclose=True)
def trace(self, entrypoint='*auto', regular_emulation=True): """Perform the VBA/VBScript emulation. """ # Clear out any intermediate IOCs from a previous run. vba_context.intermediate_iocs = set() vba_context.num_b64_iocs = 0 vba_context.shellcode = {} # Create the global context for the engine context = vba_context.Context(_globals=self.globals, engine=self, doc_vars=self.doc_vars, loaded_excel=self.loaded_excel, filename=self.filename, metadata=self.metadata) context.is_vbscript = self.is_vbscript context.do_jit = self.do_jit # Add any URLs we can pull directly from the file being analyzed. fname = self.filename is_data = False if ((fname is None) or (len(fname.strip()) == 0)): fname = self.data is_data = True direct_urls = read_ole_fields.pull_urls_office97( fname, is_data, self.vba) for url in direct_urls: context.save_intermediate_iocs(url) direct_urls = pull_urls_excel_sheets(self.loaded_excel) for url in direct_urls: context.save_intermediate_iocs(url) # Pull base64 saved in Excel cells. cell_b64_blobs = pull_b64_excel_sheets(self.loaded_excel) for cell_b64_blob in cell_b64_blobs: context.save_intermediate_iocs(cell_b64_blob) # Save the true names of imported external functions. for func_name in self.externals: func = self.externals[func_name] context.dll_func_true_names[func.name] = func.alias_name # Save the document tables in the context. context.globals["__DOC_TABLE_CONTENTS__"] = self.doc_tables # Save the document text in the proper variable in the context. context.globals["Range.Text".lower()] = "\n".join(self.doc_text) context.globals["Me.Content".lower()] = "\n".join(self.doc_text) context.globals["Me.Content.Text".lower()] = "\n".join(self.doc_text) context.globals["Me.Range.Text".lower()] = "\n".join(self.doc_text) context.globals["Me.Range".lower()] = "\n".join(self.doc_text) context.globals["Me.Content.Start".lower()] = 0 context.globals["Me.Content.End".lower()] = len("\n".join( self.doc_text)) context.globals["Me.Paragraphs".lower()] = self.doc_text context.globals["ActiveDocument.Content".lower()] = "\n".join( self.doc_text) context.globals["ActiveDocument.Content.Text".lower()] = "\n".join( self.doc_text) context.globals["ActiveDocument.Range.Text".lower()] = "\n".join( self.doc_text) context.globals["ActiveDocument.Range".lower()] = "\n".join( self.doc_text) context.globals["ActiveDocument.Content.Start".lower()] = 0 context.globals["ActiveDocument.Content.End".lower()] = len("\n".join( self.doc_text)) context.globals["ActiveDocument.Paragraphs".lower()] = self.doc_text context.globals["ThisDocument.Content".lower()] = "\n".join( self.doc_text) context.globals["ThisDocument.Content.Text".lower()] = "\n".join( self.doc_text) context.globals["ThisDocument.Range.Text".lower()] = "\n".join( self.doc_text) context.globals["ThisDocument.Range".lower()] = "\n".join( self.doc_text) context.globals["ThisDocument.Content.Start".lower()] = 0 context.globals["ThisDocument.Content.End".lower()] = len("\n".join( self.doc_text)) context.globals["ThisDocument.Paragraphs".lower()] = self.doc_text context.globals["['Me'].Content.Text".lower()] = "\n".join( self.doc_text) context.globals["['Me'].Range.Text".lower()] = "\n".join(self.doc_text) context.globals["['Me'].Range".lower()] = "\n".join(self.doc_text) context.globals["['Me'].Content.Start".lower()] = 0 context.globals["['Me'].Content.End".lower()] = len("\n".join( self.doc_text)) context.globals["['Me'].Paragraphs".lower()] = self.doc_text context.globals["['ActiveDocument'].Content.Text".lower()] = "\n".join( self.doc_text) context.globals["['ActiveDocument'].Range.Text".lower()] = "\n".join( self.doc_text) context.globals["['ActiveDocument'].Range".lower()] = "\n".join( self.doc_text) context.globals["['ActiveDocument'].Content.Start".lower()] = 0 context.globals["['ActiveDocument'].Content.End".lower()] = len( "\n".join(self.doc_text)) context.globals[ "['ActiveDocument'].Paragraphs".lower()] = self.doc_text context.globals["['ThisDocument'].Content.Text".lower()] = "\n".join( self.doc_text) context.globals["['ThisDocument'].Range.Text".lower()] = "\n".join( self.doc_text) context.globals["['ThisDocument'].Range".lower()] = "\n".join( self.doc_text) context.globals["['ThisDocument'].Content.Start".lower()] = 0 context.globals["['ThisDocument'].Content.End".lower()] = len( "\n".join(self.doc_text)) context.globals["['ThisDocument'].Paragraphs".lower()] = self.doc_text context.globals["['ActiveDocument'].Characters".lower()] = list( "\n".join(self.doc_text)) context.globals["ActiveDocument.Characters".lower()] = list("\n".join( self.doc_text)) context.globals["ActiveDocument.Characters.Count".lower()] = long( len(self.doc_text)) context.globals["Count".lower()] = 1 context.globals[".Pages.Count".lower()] = 1 context.globals["me.Pages.Count".lower()] = 1 context.globals["['ThisDocument'].Characters".lower()] = list( "\n".join(self.doc_text)) context.globals["ThisDocument.Characters".lower()] = list("\n".join( self.doc_text)) context.globals["ThisDocument.Sections".lower()] = list("\n".join( self.doc_text)) context.globals["ActiveDocument.Sections".lower()] = list("\n".join( self.doc_text)) # Break out document words. doc_words = [] for word in re.split(r"[ \n]", "\n".join(self.doc_text)): word = word.strip() if (word.startswith("-")): word = word[1:] doc_words.append("-") doc_words.append(word.strip()) context.globals["ActiveDocument.Words".lower()] = doc_words context.globals["ThisDocument.Words".lower()] = doc_words # Fake up some comments if needed. if (self.comments is None): context.globals["ActiveDocument.Comments".lower()] = [ "Comment 1", "Comment 2" ] context.globals["ThisDocument.Comments".lower()] = [ "Comment 1", "Comment 2" ] else: context.globals["ActiveDocument.Comments".lower()] = self.comments context.globals["ThisDocument.Comments".lower()] = self.comments if (self.metadata is not None): all_comments = "" # pylint: disable=not-an-iterable for comment in self.comments: all_comments += comment + "/n" self.metadata.comments = all_comments # Reset the actions list, in case it is called several times if regular_emulation: self.actions = [] # Track whether wild card values have been checked in boolean expressions. tested_wildcard = False # Set whether wildcard matches will always match or always fail to match. context.wildcard_match_value = regular_emulation # Track the external functions called. self.external_funcs = self._get_external_funcs() context.external_funcs = self.external_funcs # Track decoded strings. self.decoded_strs = set() # First emulate any Visual Basic that appears outside of subs/funcs. if (regular_emulation): context.report_action('Start Regular Emulation', '', 'All wildcard matches will match') else: context.report_action('Start Speculative Emulation', '', 'All wildcard matches will fail') log.info("Emulating loose statements...") done_emulation = False for m in self.modules: if (m.eval(context=context)): context.dump_all_files(autoclose=True) done_emulation = context.got_actions tested_wildcard = tested_wildcard or context.tested_wildcard self.decoded_strs.update(context.get_decoded_strs()) # Only start from user specified entry points if we have any. tmp_entry_points = self.entry_points only_user_entry_points = (len(self.user_entry_points) > 0) if only_user_entry_points: tmp_entry_points = self.user_entry_points # Perform analysis starting at given entry functions. for entry_point in tmp_entry_points: entry_point = entry_point.lower() if (log.getEffectiveLevel() == logging.DEBUG): log.debug("Trying entry point " + entry_point) if ((entry_point in self.globals) and (hasattr(self.globals[entry_point], "eval"))): context.report_action('Found Entry Point', safe_str_convert(entry_point), '') # We will be trying multiple entry points, so make a copy # of the context so we don't accumulate stage changes across # entry points. tmp_context = vba_context.Context(context=context, _locals=context.locals, copy_globals=True) self.globals[entry_point].eval(context=tmp_context) tmp_context.dump_all_files(autoclose=True) self.decoded_strs.update(tmp_context.get_decoded_strs()) # Save whether we got actions from this entry point. context.got_actions = tmp_context.got_actions done_emulation = True tested_wildcard = tested_wildcard or tmp_context.tested_wildcard # Stop analysis at the user specified analysis points if we have some. if only_user_entry_points: if (tested_wildcard and regular_emulation): self.trace(regular_emulation=False) return # Look for callback functions that can act as entry points. for name in self.globals: # Look for functions whose name ends with a callback suffix. for suffix in self.callback_suffixes: # Is this a callback? if (safe_str_convert(name).lower().endswith(suffix.lower())): # Is this a function? item = self.globals[name] if isinstance(item, (Function, Sub)): # Emulate it. context.report_action('Found Entry Point', safe_str_convert(name), '') # We will be trying multiple entry points, so make a copy # of the context so we don't accumulate stage changes across # entry points. tmp_context = vba_context.Context( context=context, _locals=context.locals, copy_globals=True) item.eval(context=tmp_context) tmp_context.dump_all_files(autoclose=True) # Save whether we got actions from this entry point. context.got_actions = tmp_context.got_actions tested_wildcard = tested_wildcard or tmp_context.tested_wildcard self.decoded_strs.update( tmp_context.get_decoded_strs()) # Did we find a proper entry point? if (not done_emulation): # Try heuristics to find possible entry points. log.warn( "No entry points found. Using heuristics to find entry points..." ) # Find any 0 argument subroutines. We will try emulating these as potential entry points. zero_arg_subs = [] for name in self.globals: item = self.globals[name] if ((isinstance(item, Sub)) and (len(item.params) == 0)): zero_arg_subs.append(item) # Emulate all 0 argument subroutines as potential entry points. for only_sub in zero_arg_subs: sub_name = only_sub.name context.report_action('Found Heuristic Entry Point', safe_str_convert(sub_name), '') # We will be trying multiple entry points, so make a copy # of the context so we don't accumulate stage changes across # entry points. tmp_context = vba_context.Context(context=context, _locals=context.locals, copy_globals=True) only_sub.eval(context=tmp_context) tmp_context.dump_all_files(autoclose=True) tested_wildcard = tested_wildcard or tmp_context.tested_wildcard self.decoded_strs.update(tmp_context.get_decoded_strs()) # If we used some wildcard boolean values in boolean expressions we now have # an opportunity to do some really simple speculative emulation. We just # emulated the behavior where all comparisons to a wild card value match, now # see what the behavior looks like if NONE of the comparisons match. if (tested_wildcard and regular_emulation): self.trace(regular_emulation=False)