diff --git a/quark/core/rzapkinfo.py b/quark/core/rzapkinfo.py index 446ab39a..94219b45 100644 --- a/quark/core/rzapkinfo.py +++ b/quark/core/rzapkinfo.py @@ -112,8 +112,17 @@ def _escape_str_in_rizin_manner(raw_str: str): return raw_str @functools.lru_cache - def _get_methods_classified(self, dexindex): - rz = self._get_rz(dexindex) + def _get_methods_classified( + self, dex_index: int + ) -> Dict[str, List[MethodObject]]: + """ + Use command isj to get all the methods and categorize them into + a dictionary. + + :param dex_index: an index to the Dex file that need to be parsed. + :return: a dict that holds methods categorized by their class name + """ + rz = self._get_rz(dex_index) method_json_list = rz.cmdj("isj") method_dict = defaultdict(list) @@ -131,7 +140,7 @@ def _get_methods_classified(self, dexindex): raw_argument_str = raw_argument_str.group(0) if raw_argument_str.endswith(")"): - # Convert Java lauguage type to JVM type signature + # Convert Java language type to JVM type signature # Parse the arguments raw_argument_str = raw_argument_str[1:-1] @@ -169,53 +178,14 @@ def _get_methods_classified(self, dexindex): is_imported = json_obj["is_imported"] # -- Class name -- - # Test if the class name is truncated - escaped_method_name = self._escape_str_in_rizin_manner(method_name) - if escaped_method_name.endswith("_"): - escaped_method_name = escaped_method_name[:-1] - - flag_name = json_obj["flagname"] - - # sym.imp.clone doesn't belong to a class - if flag_name == "sym.imp.clone": - method = MethodObject( - class_name="", - name="clone", - descriptor="()Ljava/lang/Object;", - cache=RizinCache(json_obj["vaddr"], dexindex, is_imported), - ) - method_dict[""].append(method) - continue - - if escaped_method_name not in flag_name: - logging.warning( - f"The class name may be truncated: {json_obj['flagname']}" - ) - - # Drop the method name - match = None - for match in re.finditer("_+[A-Za-z]+", flag_name): - pass - if match is None: - logging.warning( - f"Skip the damaged flag: {json_obj['flagname']}" - ) - continue - match = match.group(0) - flag_name = flag_name[: flag_name.rfind(match)] - - # Drop the prefixes sym. and imp. - while flag_name.startswith("sym.") or flag_name.startswith("imp."): - flag_name = flag_name[4:] - - class_name = self._convert_type_to_type_signature(flag_name) + class_name = "L" + json_obj["lib"].replace(".", "/") + ";" # Append the method method = MethodObject( class_name=class_name, name=method_name, descriptor=descriptor, - cache=RizinCache(json_obj["vaddr"], dexindex, is_imported), + cache=RizinCache(json_obj["vaddr"], dex_index, is_imported), ) method_dict[class_name].append(method) diff --git a/tests/core/test_apkinfo.py b/tests/core/test_apkinfo.py index e8482fa2..e97caff5 100644 --- a/tests/core/test_apkinfo.py +++ b/tests/core/test_apkinfo.py @@ -163,7 +163,7 @@ def test_all_methods(self, apkinfo): assert test_custom_method.issubset(apkinfo.all_methods) - def test_find_method(self, apkinfo): + def test_find_method_in_regular_class(self, apkinfo): result = apkinfo.find_method( "Ljava/lang/reflect/Field;", "setAccessible", "(Z)V" ) @@ -173,6 +173,43 @@ def test_find_method(self, apkinfo): assert str(result.name) == "setAccessible" assert str(result.descriptor) == "(Z)V" + def test_find_method_in_inner_class(self, apkinfo): + result = apkinfo.find_method( + "Landroid/support/v4/accessibilityservice/Accessibility" + + "ServiceInfoCompat$AccessibilityServiceInfoVersionImpl;", + "getId", + "(Landroid/accessibilityservice/AccessibilityServiceInfo;)" + + "Ljava/lang/String;", + ) + + assert isinstance(result, MethodObject) + assert ( + str(result.class_name) + == "Landroid/support/v4/accessibilityservice/Accessibility" + + "ServiceInfoCompat$AccessibilityServiceInfoVersionImpl;" + ) + assert str(result.name) == "getId" + assert ( + str(result.descriptor) + == "(Landroid/accessibilityservice/AccessibilityServiceInfo;)" + + "Ljava/lang/String;" + ) + + def test_find_method_in_anonymous_class(self, apkinfo): + result = apkinfo.find_method( + "Landroid/support/v4/view/AccessibilityDelegateCompatIcs$1;", + "sendAccessibilityEvent", + "(Landroid/view/View; I)V", + ) + + assert isinstance(result, MethodObject) + assert ( + str(result.class_name) + == "Landroid/support/v4/view/AccessibilityDelegateCompatIcs$1;" + ) + assert str(result.name) == "sendAccessibilityEvent" + assert str(result.descriptor) == "(Landroid/view/View; I)V" + def test_upperfunc(self, apkinfo): api = apkinfo.find_method( "Lcom/example/google/service/ContactsHelper;",