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 update_rule_buffer(rule_buffer_list, rule_path_list): for rule_path in rule_path_list: with open(rule_path, "r") as json_file: json_data = json.loads(json_file.read()) if isinstance(json_data, list): for rule_data in json_data: rule_buffer_list.append(RuleObject(rule_path, rule_data)) else: rule_buffer_list.append(RuleObject(rule_path, json_data))
def test_check_parameter_values_with_one_keyword_rule( self, simple_quark_obj, rule_with_one_keyword): rule_object = RuleObject(rule_with_one_keyword) with patch("quark.core.quark.Quark.check_parameter_values") as mock: simple_quark_obj.run(rule_object) mock.assert_called()
def test_init_with_complete_rule(self, complete_rule): rule = RuleObject(complete_rule) assert all(rule.check_item) is False assert rule.crime == "Send Location via SMS" assert rule.permission == [ "android.permission.SEND_SMS", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION", ] assert rule.api == [ { "class": "Landroid/telephony/TelephonyManager", "method": "getCellLocation", "descriptor": "()Landroid/telephony/CellLocation;", }, { "class": "Landroid/telephony/SmsManager", "method": "sendTextMessage", "descriptor": ("(Ljava/lang/String; Ljava/lang/String;" " Ljava/lang/String; Landroid/app/PendingIntent;" " Landroid/app/PendingIntent;)V"), }, ] assert rule.score == 4 assert rule.rule_filename == "sendLocation_SMS.json" assert rule.label == ["location", "collection"]
def quark_obj(simple_quark_obj): data = simple_quark_obj # rule rules = "quark/rules" rules_list = os.listdir(rules) for single_rule in rules_list: if single_rule.endswith("json"): rulepath = os.path.join(rules, single_rule) rule_checker = RuleObject(rulepath) # Run the checker data.run(rule_checker) data.generate_json_report(rule_checker) yield data
def rule_obj(scope="function"): rule_json = """ { "crime": "Send Location via SMS", "permission": [ "android.permission.SEND_SMS", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION" ], "api": [ { "class": "Landroid/telephony/TelephonyManager", "method": "getCellLocation", "descriptor": "(I Ljava/lang/String; [B J)V" }, { "class": "Landroid/telephony/SmsManager", "method": "sendTextMessage", "descriptor": "(ILjava/lang/String;[BJ)V" } ], "score": 4, "label": [ "location", "collection" ] } """ with open("sendLocation.json", "w") as f: f.write(rule_json) print("setup() begin") rule_obj = RuleObject("sendLocation.json") yield rule_obj del rule_obj os.remove("sendLocation.json")
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
def test_init_with_incomplete_rule(self, incomplete_rule): with pytest.raises(KeyError): _ = RuleObject(incomplete_rule)
def test_init_with_invalid_file(self, invalid_file): with pytest.raises(BaseException): _ = RuleObject(invalid_file)
def test_init_with_non_exist_file(self): with pytest.raises(FileNotFoundError): _ = RuleObject("NON_EXIST_FILE")
def test_init_with_invalid_path(self): with pytest.raises(TypeError): _ = RuleObject(["Not", "a", "file"])