Exemplo n.º 1
0
    def extract_all_user_names(filename=None):
        """
        Get all user-named labels inside IDA. Also prints into output window.
        :return: dictionary of all user named labels: label_name -> ea
        """
        results = {}
        output = ''

        for ea, name in idautils.Names():
            if ida_kernwin.user_cancelled():
                return results

            if '_' in name:
                if name.split('_')[0] in ('def', 'sub', 'loc', 'jpt', 'j',
                                          'nullsub'):
                    continue
            flags = ida_bytes.get_full_flags(ea)
            if ida_bytes.has_user_name(flags):
                results[name] = ea
                output += '{} = 0x{:08x};\n'.format(name, ea)

        if filename is not None:
            with open(filename, 'w') as f:
                f.write(output)

        return results
Exemplo n.º 2
0
Arquivo: form.py Projeto: gunjin1/capa
    def update(self, text):
        """emit progress update

        check if user cancelled action, raise exception for parent function to catch
        """
        if ida_kernwin.user_cancelled():
            raise UserCancelledError("user cancelled")
        self.progress.emit("extracting features from %s" % text)
Exemplo n.º 3
0
 def _process(self, i):
     n = self._add_node(i)
     self.n_processed += 1
     if n < 0:
         return n
     if len(self.parents) > 1:
         lp = self.parents.back().obj_id
         for k_obj_id, v in self.reverse.items():
             if k_obj_id == lp:
                 p = v
                 break
         self.cg.add_edge(p, n)
     if self.n_items:
         if self.n_processed >= self.n_items:
             ida_kernwin.hide_wait_box()
         if ida_kernwin.user_cancelled():
             return 1
     return 0
Exemplo n.º 4
0
Arquivo: form.py Projeto: gunjin1/capa
    def load_capa_function_results(self):
        """ """
        if not self.rules_cache or not self.ruleset_cache:
            # only reload rules if caches are empty
            if not self.load_capa_rules():
                return False
        else:
            logger.info(
                'Using cached ruleset, click "Reset" to reload rules from disk.'
            )

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False
        update_wait_box("loading IDA extractor")

        try:
            # must use extractor to get function, as capa analysis requires casted object
            extractor = CapaExplorerFeatureExtractor()
        except Exception as e:
            logger.error("Failed to load IDA feature extractor (error: %s)" %
                         e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False
        update_wait_box("extracting function features")

        try:
            f = idaapi.get_func(idaapi.get_screen_ea())
            if f:
                f = extractor.get_function(f.start_ea)
                self.rulegen_current_function = f

                func_features, bb_features = find_func_features(f, extractor)
                self.rulegen_func_features_cache = collections.defaultdict(
                    set, copy.copy(func_features))
                self.rulegen_bb_features_cache = collections.defaultdict(
                    dict, copy.copy(bb_features))

                if ida_kernwin.user_cancelled():
                    logger.info("User cancelled analysis.")
                    return False
                update_wait_box("matching function/basic block rule scope")

                try:
                    # add function and bb rule matches to function features, for display purposes
                    func_matches, bb_matches = find_func_matches(
                        f, self.ruleset_cache, func_features, bb_features)
                    for (name, res) in itertools.chain(func_matches.items(),
                                                       bb_matches.items()):
                        rule = self.ruleset_cache[name]
                        if rule.meta.get("capa/subscope-rule"):
                            continue
                        for (ea, _) in res:
                            func_features[capa.features.common.MatchedRule(
                                name)].add(ea)
                except Exception as e:
                    logger.error(
                        "Failed to match function/basic block rule scope (error: %s)"
                        % e)
                    return False
            else:
                func_features = {}
        except UserCancelledError:
            logger.info("User cancelled analysis.")
            return False
        except Exception as e:
            logger.error("Failed to extract function features (error: %s)" % e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False
        update_wait_box("extracting file features")

        try:
            file_features = find_file_features(extractor)
            self.rulegen_file_features_cache = collections.defaultdict(
                dict, copy.copy(file_features))

            if ida_kernwin.user_cancelled():
                logger.info("User cancelled analysis.")
                return False
            update_wait_box("matching file rule scope")

            try:
                # add file matches to file features, for display purposes
                for (name, res) in find_file_matches(self.ruleset_cache,
                                                     file_features).items():
                    rule = self.ruleset_cache[name]
                    if rule.meta.get("capa/subscope-rule"):
                        continue
                    for (ea, _) in res:
                        file_features[capa.features.common.MatchedRule(
                            name)].add(ea)
            except Exception as e:
                logger.error("Failed to match file scope rules (error: %s)" %
                             e)
                return False
        except Exception as e:
            logger.error("Failed to extract file features (error: %s)" % e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False
        update_wait_box("rendering views")

        try:
            # load preview and feature tree
            self.view_rulegen_preview.load_preview_meta(
                f.start_ea if f else None,
                settings.user.get(CAPA_SETTINGS_RULEGEN_AUTHOR,
                                  "<insert_author>"),
                settings.user.get(CAPA_SETTINGS_RULEGEN_SCOPE, "function"),
            )
            self.view_rulegen_features.load_features(file_features,
                                                     func_features)

            # self.view_rulegen_header_label.setText("Function Features (%s)" % trim_function_name(f))
            self.set_view_status_label("capa rules directory: %s (%d rules)" %
                                       (settings.user[CAPA_SETTINGS_RULE_PATH],
                                        len(self.rules_cache)))
        except Exception as e:
            logger.error("Failed to render views (error: %s)" % e)
            return False

        return True
Exemplo n.º 5
0
Arquivo: form.py Projeto: gunjin1/capa
    def load_capa_results(self, use_cache=False):
        """run capa analysis and render results in UI

        note: this function must always return, exception or not, in order for plugin to safely close the IDA
        wait box
        """
        if not use_cache:
            # new analysis, new doc
            self.doc = None
            self.process_total = 0
            self.process_count = 1

            def slot_progress_feature_extraction(text):
                """slot function to handle feature extraction progress updates"""
                update_wait_box("%s (%d of %d)" %
                                (text, self.process_count, self.process_total))
                self.process_count += 1

            extractor = CapaExplorerFeatureExtractor()
            extractor.indicator.progress.connect(
                slot_progress_feature_extraction)

            update_wait_box("calculating analysis")

            try:
                self.process_total += len(tuple(extractor.get_functions()))
            except Exception as e:
                logger.error("Failed to calculate analysis (error: %s).", e)
                return False

            if ida_kernwin.user_cancelled():
                logger.info("User cancelled analysis.")
                return False

            update_wait_box("loading rules")

            if not self.load_capa_rules():
                return False

            if ida_kernwin.user_cancelled():
                logger.info("User cancelled analysis.")
                return False

            update_wait_box("extracting features")

            try:
                meta = capa.ida.helpers.collect_metadata()
                capabilities, counts = capa.main.find_capabilities(
                    self.ruleset_cache, extractor, disable_progress=True)
                meta["analysis"].update(counts)
            except UserCancelledError:
                logger.info("User cancelled analysis.")
                return False
            except Exception as e:
                logger.error(
                    "Failed to extract capabilities from database (error: %s)",
                    e)
                return False

            update_wait_box("checking for file limitations")

            try:
                # support binary files specifically for x86/AMD64 shellcode
                # warn user binary file is loaded but still allow capa to process it
                # TODO: check specific architecture of binary files based on how user configured IDA processors
                if idaapi.get_file_type_name() == "Binary file":
                    logger.warning("-" * 80)
                    logger.warning(" Input file appears to be a binary file.")
                    logger.warning(" ")
                    logger.warning(
                        " capa currently only supports analyzing binary files containing x86/AMD64 shellcode with IDA."
                    )
                    logger.warning(
                        " This means the results may be misleading or incomplete if the binary file loaded in IDA is not x86/AMD64."
                    )
                    logger.warning(
                        " If you don't know the input file type, you can try using the `file` utility to guess it."
                    )
                    logger.warning("-" * 80)

                    capa.ida.helpers.inform_user_ida_ui(
                        "capa encountered file type warnings during analysis")

                if capa.main.has_file_limitation(self.ruleset_cache,
                                                 capabilities,
                                                 is_standalone=False):
                    capa.ida.helpers.inform_user_ida_ui(
                        "capa encountered file limitation warnings during analysis"
                    )
            except Exception as e:
                logger.error(
                    "Failed to check for file limitations (error: %s)", e)
                return False

            if ida_kernwin.user_cancelled():
                logger.info("User cancelled analysis.")
                return False

            update_wait_box("rendering results")

            try:
                self.doc = capa.render.result_document.convert_capabilities_to_result_document(
                    meta, self.ruleset_cache, capabilities)
            except Exception as e:
                logger.error("Failed to render results (error: %s)", e)
                return False

        try:
            self.model_data.render_capa_doc(
                self.doc, self.view_show_results_by_function.isChecked())
            self.set_view_status_label("capa rules directory: %s (%d rules)" %
                                       (settings.user[CAPA_SETTINGS_RULE_PATH],
                                        len(self.rules_cache)))
        except Exception as e:
            logger.error("Failed to render results (error: %s)", e)
            return False

        return True
Exemplo n.º 6
0
Arquivo: form.py Projeto: gunjin1/capa
    def load_capa_rules(self):
        """ """
        self.ruleset_cache = None
        self.rules_cache = None

        try:
            # resolve rules directory - check self and settings first, then ask user
            if not os.path.exists(
                    settings.user.get(CAPA_SETTINGS_RULE_PATH, "")):
                idaapi.info(
                    "Please select a file directory containing capa rules.")
                path = self.ask_user_directory()
                if not path:
                    logger.warning(
                        "You must select a file directory containing capa rules before analysis can be run. The standard collection of capa rules can be downloaded from https://github.com/fireeye/capa-rules."
                    )
                    return False
                settings.user[CAPA_SETTINGS_RULE_PATH] = path
        except Exception as e:
            logger.error("Failed to load capa rules (error: %s).", e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False

        rule_path = settings.user[CAPA_SETTINGS_RULE_PATH]
        try:
            # TODO refactor: this first part is identical to capa.main.get_rules
            if not os.path.exists(rule_path):
                raise IOError(
                    "rule path %s does not exist or cannot be accessed" %
                    rule_path)

            rule_paths = []
            if os.path.isfile(rule_path):
                rule_paths.append(rule_path)
            elif os.path.isdir(rule_path):
                for root, dirs, files in os.walk(rule_path):
                    if ".github" in root:
                        # the .github directory contains CI config in capa-rules
                        # this includes some .yml files
                        # these are not rules
                        continue
                    for file in files:
                        if not file.endswith(".yml"):
                            if not (file.startswith(".git") or file.endswith(
                                (".git", ".md", ".txt"))):
                                # expect to see .git* files, readme.md, format.md, and maybe a .git directory
                                # other things maybe are rules, but are mis-named.
                                logger.warning("skipping non-.yml file: %s",
                                               file)
                            continue
                        rule_path = os.path.join(root, file)
                        rule_paths.append(rule_path)

            rules = []
            total_paths = len(rule_paths)
            for (i, rule_path) in enumerate(rule_paths):
                update_wait_box("loading capa rules from %s (%d of %d)" %
                                (settings.user[CAPA_SETTINGS_RULE_PATH], i + 1,
                                 total_paths))
                if ida_kernwin.user_cancelled():
                    raise UserCancelledError("user cancelled")
                try:
                    rule = capa.rules.Rule.from_yaml_file(rule_path)
                except capa.rules.InvalidRule:
                    raise
                else:
                    rule.meta["capa/path"] = rule_path
                    if capa.main.is_nursery_rule_path(rule_path):
                        rule.meta["capa/nursery"] = True
                    rules.append(rule)
            _rules = copy.copy(rules)
            ruleset = capa.rules.RuleSet(_rules)
        except UserCancelledError:
            logger.info("User cancelled analysis.")
            return False
        except Exception as e:
            capa.ida.helpers.inform_user_ida_ui(
                "Failed to load capa rules from %s" %
                settings.user[CAPA_SETTINGS_RULE_PATH])
            logger.error("Failed to load rules from %s (error: %s).",
                         settings.user[CAPA_SETTINGS_RULE_PATH], e)
            logger.error(
                "Make sure your file directory contains properly formatted capa rules. You can download the standard collection of capa rules from https://github.com/fireeye/capa-rules."
            )
            settings.user[CAPA_SETTINGS_RULE_PATH] = ""
            return False

        self.ruleset_cache = ruleset
        self.rules_cache = rules

        return True
Exemplo n.º 7
0
# Note: this try/except block below is just there to
# let us (at Hex-Rays) test this script in various
# situations.
try:
    perform_decompilation = under_test__perform_decompilation
except:
    pass


step_sleep = 0.5
ida_kernwin.show_wait_box("Processing")
try:
    all_eas = list(idautils.Functions())
    neas = len(all_eas)
    for i, ea in enumerate(all_eas):
        if ida_kernwin.user_cancelled():
            break
        ida_kernwin.replace_wait_box("Processing; step %d/%d" % (i+1, neas))

        if perform_decompilation:
            try:
                ida_hexrays.decompile(ida_funcs.get_func(ea))
            except ida_hexrays.DecompilationFailure as df:
                print("Decompilation failure: %s" % df)

        time.sleep(step_sleep * random.random())
finally:
    ida_kernwin.hide_wait_box()


Exemplo n.º 8
0
    def load_capa_results(self):
        """run capa analysis and render results in UI

        note: this function must always return, exception or not, in order for plugin to safely close the IDA
        wait box
        """
        # new analysis, new doc
        self.doc = None
        self.process_total = 0
        self.process_count = 1

        def update_wait_box(text):
            """update the IDA wait box"""
            ida_kernwin.replace_wait_box("capa explorer...%s" % text)

        def slot_progress_feature_extraction(text):
            """slot function to handle feature extraction progress updates"""
            update_wait_box("%s (%d of %d)" %
                            (text, self.process_count, self.process_total))
            self.process_count += 1

        extractor = CapaExplorerFeatureExtractor()
        extractor.indicator.progress.connect(slot_progress_feature_extraction)

        update_wait_box("calculating analysis")

        try:
            self.process_total += len(tuple(extractor.get_functions()))
        except Exception as e:
            logger.error("Failed to calculate analysis (error: %s).", e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False

        update_wait_box("loading rules")

        try:
            # resolve rules directory - check self and settings first, then ask user
            if not self.rule_path:
                if "rule_path" in settings and os.path.exists(
                        settings["rule_path"]):
                    self.rule_path = settings["rule_path"]
                else:
                    idaapi.info(
                        "Please select a file directory containing capa rules."
                    )
                    rule_path = self.ask_user_directory()
                    if not rule_path:
                        logger.warning(
                            "You must select a file directory containing capa rules before analysis can be run. The standard collection of capa rules can be downloaded from https://github.com/fireeye/capa-rules."
                        )
                        return False
                    self.rule_path = rule_path
                    settings.user["rule_path"] = rule_path
        except Exception as e:
            logger.error("Failed to load capa rules (error: %s).", e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False

        rule_path = self.rule_path

        try:
            if not os.path.exists(rule_path):
                raise IOError(
                    "rule path %s does not exist or cannot be accessed" %
                    rule_path)

            rule_paths = []
            if os.path.isfile(rule_path):
                rule_paths.append(rule_path)
            elif os.path.isdir(rule_path):
                for root, dirs, files in os.walk(rule_path):
                    if ".github" in root:
                        # the .github directory contains CI config in capa-rules
                        # this includes some .yml files
                        # these are not rules
                        continue
                    for file in files:
                        if not file.endswith(".yml"):
                            if not (file.endswith(".md")
                                    or file.endswith(".git")
                                    or file.endswith(".txt")):
                                # expect to see readme.md, format.md, and maybe a .git directory
                                # other things maybe are rules, but are mis-named.
                                logger.warning("skipping non-.yml file: %s",
                                               file)
                            continue
                        rule_path = os.path.join(root, file)
                        rule_paths.append(rule_path)

            rules = []
            total_paths = len(rule_paths)
            for (i, rule_path) in enumerate(rule_paths):
                update_wait_box("loading capa rules from %s (%d of %d)" %
                                (self.rule_path, i + 1, total_paths))
                if ida_kernwin.user_cancelled():
                    raise UserCancelledError("user cancelled")
                try:
                    rule = capa.rules.Rule.from_yaml_file(rule_path)
                except capa.rules.InvalidRule:
                    raise
                else:
                    rule.meta["capa/path"] = rule_path
                    if capa.main.is_nursery_rule_path(rule_path):
                        rule.meta["capa/nursery"] = True
                    rules.append(rule)

            rule_count = len(rules)
            rules = capa.rules.RuleSet(rules)
        except UserCancelledError:
            logger.info("User cancelled analysis.")
            return False
        except Exception as e:
            capa.ida.helpers.inform_user_ida_ui(
                "Failed to load capa rules from %s" % self.rule_path)
            logger.error("Failed to load rules from %s (error: %s).",
                         self.rule_path, e)
            logger.error(
                "Make sure your file directory contains properly formatted capa rules. You can download the standard collection of capa rules from https://github.com/fireeye/capa-rules."
            )
            self.rule_path = ""
            settings.user.del_value("rule_path")
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False

        update_wait_box("extracting features")

        try:
            meta = capa.ida.helpers.collect_metadata()
            capabilities, counts = capa.main.find_capabilities(
                rules, extractor, disable_progress=True)
            meta["analysis"].update(counts)
        except UserCancelledError:
            logger.info("User cancelled analysis.")
            return False
        except Exception as e:
            logger.error(
                "Failed to extract capabilities from database (error: %s)", e)
            return False

        update_wait_box("checking for file limitations")

        try:
            # support binary files specifically for x86/AMD64 shellcode
            # warn user binary file is loaded but still allow capa to process it
            # TODO: check specific architecture of binary files based on how user configured IDA processors
            if idaapi.get_file_type_name() == "Binary file":
                logger.warning("-" * 80)
                logger.warning(" Input file appears to be a binary file.")
                logger.warning(" ")
                logger.warning(
                    " capa currently only supports analyzing binary files containing x86/AMD64 shellcode with IDA."
                )
                logger.warning(
                    " This means the results may be misleading or incomplete if the binary file loaded in IDA is not x86/AMD64."
                )
                logger.warning(
                    " If you don't know the input file type, you can try using the `file` utility to guess it."
                )
                logger.warning("-" * 80)

                capa.ida.helpers.inform_user_ida_ui(
                    "capa encountered file type warnings during analysis")

            if capa.main.has_file_limitation(rules,
                                             capabilities,
                                             is_standalone=False):
                capa.ida.helpers.inform_user_ida_ui(
                    "capa encountered file limitation warnings during analysis"
                )
        except Exception as e:
            logger.error("Failed to check for file limitations (error: %s)", e)
            return False

        if ida_kernwin.user_cancelled():
            logger.info("User cancelled analysis.")
            return False

        update_wait_box("rendering results")

        try:
            self.doc = capa.render.convert_capabilities_to_result_document(
                meta, rules, capabilities)
            self.model_data.render_capa_doc(self.doc)
            self.render_capa_doc_mitre_summary()
            self.enable_controls()
            self.set_view_status_label("capa rules directory: %s (%d rules)" %
                                       (self.rule_path, rule_count))
        except Exception as e:
            logger.error("Failed to render results (error: %s)", e)
            return False

        return True