Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)