예제 #1
0
    def __init__(self,
                 apk_path,
                 target_method,
                 output_dir,
                 max_search_layer=3):
        self.quark = Quark(apk_path)
        self.apkinfo = self.quark.apkinfo

        # Parse smali code into classname methodname and descriptor.
        classname = target_method.split("->")[0]
        methodname = target_method.split("->")[1].split("(")[0]
        descriptor = "(" + target_method.split("->")[1].split("(")[1]

        self.method = self.apkinfo.find_method(
            class_name=classname,
            method_name=methodname,
            descriptor=descriptor,
        )

        if self.method is None:
            raise ValueError("Target method not found!")

        self.output_dir = output_dir
        self.api_set = set()
        self.max_search_layer = max_search_layer
        return
예제 #2
0
    def analysis(self, apk, rule, core_library="androguard"):
        """
        The main function of Quark-Engine analysis, the analysis is based on the provided APK file.

        :param core_library: the library to analysis binary
        :param apk: the APK file
        :param rule: the rule to be checked, it could be a directory or a single json rule
        :return: None
        """

        self.quark = Quark(apk, core_library)

        if os.path.isdir(rule):

            rules_list = os.listdir(rule)

            for single_rule in rules_list:
                if single_rule.endswith("json"):
                    rule_path = os.path.join(rule, single_rule)
                    rule_checker = RuleObject(rule_path)

                    # Run the checker
                    self.quark.run(rule_checker)

                    # Generate json report
                    self.quark.generate_json_report(rule_checker)

        elif os.path.isfile(rule):
            if rule.endswith("json"):
                rule = RuleObject(rule)
                # Run checker
                self.quark.run(rule)
                # Generate json report
                self.quark.generate_json_report(rule)
예제 #3
0
class Report:
    """
    This module is for users who want to use quark as a Python module.
    """

    def __init__(self):
        self.quark = None

    def analysis(self, apk, rule, core_library="androguard"):
        """
        The main function of Quark-Engine analysis, the analysis is based on the provided APK file.

        :param core_library: the library to analysis binary
        :param apk: the APK file
        :param rule: the rule to be checked, it could be a directory or a single json rule
        :return: None
        """

        self.quark = Quark(apk, core_library)

        if os.path.isdir(rule):

            rules_list = os.listdir(rule)

            for single_rule in rules_list:
                if single_rule.endswith("json"):
                    rule_path = os.path.join(rule, single_rule)
                    rule_checker = RuleObject(rule_path)

                    # Run the checker
                    self.quark.run(rule_checker)

                    # Generate json report
                    self.quark.generate_json_report(rule_checker)

        elif os.path.isfile(rule):
            if rule.endswith("json"):
                rule = RuleObject(rule)
                # Run checker
                self.quark.run(rule)
                # Generate json report
                self.quark.generate_json_report(rule)

    def get_report(self, report_type):
        """
        Output the Quark-Engine report according to the report_type argument.

        :param report_type: string of the report format
        :return: string of the quark report with the format you specify
        """

        if report_type == "json":
            return self.quark.get_json_report()

        raise ValueError(
            "The format are not supported, please refer to the Quark manual."
        )
예제 #4
0
def main(apk, rule, thershold, output, summary):
    logging.basicConfig(level=logging.WARN)
    print(f'Apk File: {apk}')
    print(f'Rule Directory: {rule}', )

    if output:
        print(f'Generate Json File: {output.name}')

    if thershold:
        print(f'Thershold:{thershold}%')

    counter = 0
    displayed = 0

    quark = Quark(apk)

    # Analyzing
    for rule_file in tqdm.tqdm(get_rules_from_directory(rule)):
        rule = QuarkRule(rule_file)
        quark.analysis_rule(rule)
        counter = counter + 1

    # Summarizing
    if summary:
        for rule, behavior_list in quark.report.passed_behaviors.items():
            max_stage = max(
                [behavior.reached_stage for behavior in behavior_list])

            if thershold and max_stage*20 < thershold:
                continue

            print(f'Crime @ {rule.crime:<80} {max_stage*20:>3}%')
            displayed = displayed + 1

        print(f'Total: {displayed}/{counter}')
    else:
        print(f'Total: {counter}')

    # Generate json file
    if output:
        json.dump(quark.get_json_report(), output, indent=4)
예제 #5
0
 def _worker_initializer(apk, core_library):
     global _quark
     _quark = Quark(apk, core_library)
예제 #6
0
def entry_point(
    summary,
    detail,
    rule_generation,
    apk,
    rule,
    output,
    webreport,
    graph,
    classification,
    threshold,
    list,
    permission,
    label,
    comparison,
    core_library,
    num_of_process,
):
    """Quark is an Obfuscation-Neglect Android Malware Scoring System"""
    # Load rules
    rule_buffer_list = []
    rule_filter = summary or detail

    # Determine the location of rules
    if rule_filter and rule_filter.endswith("json"):
        if not os.path.isfile(rule_filter):
            print_warning(
                f"Specified rule not found.\n"
                f"If you want to specify one of the rules of Quark-Rule, "
                f"use {yellow(f'{config.DIR_PATH}/{rule_filter}')} "
                f"as an argument.")
            return

        rule_path_list = [rule_filter]
    else:
        rule_path_list = [
            os.path.join(dir_path, file)
            for dir_path, _, file_list in os.walk(rule) for file in file_list
            if file.endswith("json")
        ]

    # Load rules into memory
    update_rule_buffer(rule_buffer_list, rule_path_list)

    # Determine if the user provide a rule label
    if (rule_filter and rule_filter != "all_rules"
            and not rule_filter.endswith("json")):
        rule_checker_list = [
            rule_checker for rule_checker in rule_buffer_list
            if rule_filter in rule_checker.label
        ]
    else:
        rule_checker_list = rule_buffer_list

    if comparison:

        # selection of labels on which it will be done the comparison on radar chart
        # first look for all label found in the rule list
        all_labels = set()  # array type, e.g. ['network', 'collection']

        for rule_checker in rule_checker_list:
            all_labels.update(rule_checker.label)

        # let user choose a list of label on which it will be performed the analysis
        selected_label = np.array(
            select_label_menu(all_labels, min_labels=5, max_labels=15))

        # perform label based analysis on the apk_
        malware_confidences = {}
        for apk_ in apk:
            data = (ParallelQuark(apk_, core_library, num_of_process)
                    if num_of_process > 1 else Quark(apk_, core_library))
            all_labels = {}
            # dictionary containing
            # key: label
            # value: list of confidence values
            # $ print(all_rules["accessibility service"])
            # > [60, 40, 60, 40, 60, 40]

            # analyse malware only on rules where appears label selected
            rule_checker_list = [
                rule_checker for rule_checker in rule_buffer_list
                if len(np.intersect1d(rule_checker.label, selected_label)) != 0
            ]

            if num_of_process > 1:
                data.apply_rules(rule_checker_list)

            for rule_checker in tqdm(rule_checker_list):
                # Run the checker
                data.run(rule_checker)
                confidence = rule_checker.check_item.count(True) * 20
                labels = (rule_checker.label
                          )  # array type, e.g. ['network', 'collection']
                for single_label in labels:
                    if single_label in all_labels:
                        all_labels[single_label].append(confidence)
                    else:
                        all_labels[single_label] = [confidence]

            # extrapolate data used to plot radar chart
            radar_data = {}
            for _label in selected_label:
                confidences = np.array(all_labels[_label])
                # on radar data use the maximum confidence for a certain label
                radar_data[_label] = np.max(confidences)

            radar_confidence = [
                value_ for _label, value_ in radar_data.items()
            ]
            malware_confidences[apk_.split("/")[-1]] = radar_confidence

        show_comparison_graph(
            title=f"Malicious Actions Comparison Between {len(apk)} Malwares",
            lables=selected_label,
            malware_confidences=malware_confidences,
            font_size=22,
        )

        return

    # Load APK
    data = (ParallelQuark(apk[0], core_library, num_of_process)
            if num_of_process > 1 else Quark(apk[0], core_library))

    if label:
        all_labels = {}
        # dictionary containing
        # key: label
        # value: list of confidence values
        # $ print(all_rules["accessibility service"])
        # > [60, 40, 60, 40, 60, 40]

        if num_of_process > 1:
            data.apply_rules(rule_buffer_list)

        for rule_checker in tqdm(rule_buffer_list):
            # Run the checker
            data.run(rule_checker)
            confidence = rule_checker.check_item.count(True) * 20
            labels = (rule_checker.label
                      )  # array type, e.g. ['network', 'collection']
            for single_label in labels:
                if single_label in all_labels:
                    all_labels[single_label].append(confidence)
                else:
                    all_labels[single_label] = [confidence]

        # get how many label with max confidence >= 80%
        counter_high_confidence = sum(
            max(value) >= 80 for single_label, value in all_labels.items())

        print_info(f"Total Label found: {yellow(len(all_labels))}")
        print_info(
            f"Rules with label which max confidence >= 80%: {yellow(counter_high_confidence)}"
        )

        data.show_label_report(rule, all_labels, label)
        print(data.quark_analysis.label_report_table)

    # Show summary report
    if summary:

        if isinstance(data, ParallelQuark):
            data.apply_rules(rule_checker_list)

        for rule_checker in tqdm(rule_checker_list):
            # Run the checker
            data.run(rule_checker)

            data.show_summary_report(rule_checker, threshold)

        w = Weight(data.quark_analysis.score_sum,
                   data.quark_analysis.weight_sum)
        print_warning(w.calculate())
        print_info(f"Total Score: {data.quark_analysis.score_sum}")
        print(data.quark_analysis.summary_report_table)

        if classification:
            data.show_rule_classification()
        if graph:
            data.show_call_graph(graph)

    # Show detail report
    if detail:
        threshold_number = int(threshold) if threshold else 0

        if isinstance(data, ParallelQuark):
            data.apply_rules(rule_checker_list)

        for rule_checker in tqdm(rule_checker_list):
            # Run the checker
            data.run(rule_checker)

            confidence = rule_checker.check_item.count(True) * 20

            if confidence >= threshold_number:
                print(f"Rulepath: "
                      f"{os.path.join(rule, rule_checker.rule_filename)}")
                print(f"Rule crime: {rule_checker.crime}")
                data.show_detail_report(rule_checker)
                print_success("OK")

        if classification:
            data.show_rule_classification()
        if graph:
            data.show_call_graph()

    if rule_generation:
        generator = RuleGeneration(apk[0], rule_generation)

        if webreport:
            if ".html" not in webreport:
                webreport = f"{webreport}.html"
            webreport_file = os.path.join(rule_generation, webreport)
            generator.generate_rule(web_editor=webreport_file)
        else:
            generator.generate_rule()

    # Show JSON report
    if output:
        if isinstance(data, ParallelQuark):
            data.apply_rules(rule_buffer_list)

        for rule_checker in tqdm(rule_buffer_list):
            # Run the checker
            data.run(rule_checker)

            data.generate_json_report(rule_checker)

        json_report = data.get_json_report()

        with open(output, "w") as file:
            json.dump(json_report, file, indent=4)
            file.close()

    # Generate web report
    if webreport:
        if summary or detail:
            for rule_checker in tqdm(rule_buffer_list):
                data.generate_json_report(rule_checker)

            json_report = data.get_json_report()
            report_html = ReportGenerator(
                json_report).get_analysis_report_html()

            if ".html" not in webreport:
                webreport = f"{webreport}.html"

            with open(webreport, "w") as file:
                file.write(report_html)
                file.close()

    if list:

        if list == "all":
            for all_method in data.apkinfo.all_methods:
                print(all_method.full_name)
        if list == "native":
            for api in data.apkinfo.android_apis:
                print(api.full_name)
        if list == "custom":
            for custom_method in data.apkinfo.custom_methods:
                print(custom_method.full_name)

    if permission:

        for p in data.apkinfo.permissions:
            print(p)

    if isinstance(data, ParallelQuark):
        data.close()
예제 #7
0
def simple_quark_obj():
    r = requests.get(APK_SOURCE, allow_redirects=True)
    open(APK_FILENAME, "wb").write(r.content)

    apk_file = APK_FILENAME
    return Quark(apk_file)
예제 #8
0
class RadioContrast:
    """
    This module is for generating rules with the APIs in a specific method.
    """
    def __init__(self,
                 apk_path,
                 target_method,
                 output_dir,
                 max_search_layer=3):
        self.quark = Quark(apk_path)
        self.apkinfo = self.quark.apkinfo

        # Parse smali code into classname methodname and descriptor.
        classname = target_method.split("->")[0]
        methodname = target_method.split("->")[1].split("(")[0]
        descriptor = "(" + target_method.split("->")[1].split("(")[1]

        self.method = self.apkinfo.find_method(
            class_name=classname,
            method_name=methodname,
            descriptor=descriptor,
        )

        if self.method is None:
            raise ValueError("Target method not found!")

        self.output_dir = output_dir
        self.api_set = set()
        self.max_search_layer = max_search_layer
        return

    def method_recursive_search(self, method_set, depth=1):
        """
        Find all APIs in the target method.

        :param method_set: the list that contains each MethodAnalysis.
        :param depth: maximum number of recursive search functions.
        :return: a set of first_method_list ∩ second_method_list or None.
        """

        # Not found same method usage, try to find the next layer.
        depth += 1
        if depth > self.max_search_layer:
            return

        # Append first layer into next layer.
        next_level_set = method_set.copy()

        # Extend the xref from function into next layer.
        for md in next_level_set:
            if md[0].is_android_api():
                self.api_set.add(md[0])
                continue

            self.method_recursive_search(self.apkinfo.lowerfunc(md[0]), depth)

    def generate_rule(self, percentile_rank=0.2, web_editor=None):
        """
        Generate rules and export them to the output directory.

        :param percentile_rank: The percentile rank
                                for filter APIs by used count.
        :param web_editor: The path of the web editor html file.
        :return: None
        """
        # Rescursive search for apis in target method.
        lower_funcs = set(self.apkinfo.lowerfunc(self.method))
        self.method_recursive_search(lower_funcs)
        self.api_set, _ = filter_api_by_usage_count(
            self.apkinfo, self.api_set, percentile_rank=percentile_rank)

        first_apis_pool = list(self.api_set)
        second_apis_pool = list(self.api_set)

        # Setup progress bar.
        second_api_pool_num = len(second_apis_pool)
        outter_loop = tqdm(first_apis_pool)

        self.generated_result = list()

        # The number of rule file.
        rule_number = 1

        for api1 in first_apis_pool:
            outter_loop.update(1)

            for num, api2 in enumerate(second_apis_pool, start=1):
                inner_desc = f"{num}/{second_api_pool_num}"
                outter_loop.set_postfix(inner_loop=inner_desc, refresh=True)

                # Skip the case of same method.
                if api2.name == api1.name:
                    continue

                generated_rule = {
                    "crime":
                    "",
                    "permission": [],
                    "api": [
                        {
                            "class": api1.class_name,
                            "method": api1.name,
                            "descriptor": api1.descriptor,
                        },
                        {
                            "class": api2.class_name,
                            "method": api2.name,
                            "descriptor": api2.descriptor,
                        },
                    ],
                    "score":
                    1,
                    "label": [],
                }
                comb = RuleObject("test", json_data=generated_rule)

                try:
                    self.quark.run(comb)
                except KeyboardInterrupt:
                    raise
                except Exception as e:
                    tqdm.write(
                        "{} and {} combination has some error when analyzing,\
                        ERROR MESSAGE: {}".format(
                            api1,
                            api2,
                            e,
                        ), )
                    continue

                if comb.check_item[4]:
                    continue

                if web_editor:
                    generated_rule["number"] = rule_number
                    self.generated_result.append(generated_rule)
                    rule_number += 1

                else:
                    rule_name = f"{rule_number}.json"
                    rule_path = os.path.join(self.output_dir, rule_name)
                    with open(rule_path, "w") as rule_file:
                        json.dump(generated_rule, rule_file, indent=4)

                    rule_number += 1

        if web_editor:
            web_editor_data = {
                "apk_filename": self.quark.apkinfo.filename,
                "md5": self.quark.apkinfo.md5,
                "size_bytes": self.quark.apkinfo.filesize,
                "result": self.generated_result
            }

            editor_html = ReportGenerator(
                web_editor_data).get_rule_generate_editor_html()

            if ".html" not in web_editor:
                web_editor = f"{web_editor}.html"

            with open(web_editor, "w") as file:
                file.write(editor_html)
                file.close()

        # Clear progress bar
        outter_loop.clear()
        outter_loop.close()
        return