1010import zipfile
1111from collections import defaultdict , namedtuple
1212from os import PathLike
13- from typing import Any , Dict , Generator , List , Optional , Set , Union
13+ from typing import Dict , Generator , List , Optional , Set , Tuple , Union
1414
1515import rzpipe
1616
@@ -78,11 +78,31 @@ def __init__(
7878
7979 @functools .lru_cache
8080 def _get_rz (self , index ):
81+ """
82+ Return a Rizin object that opens the specified Dex file.
83+
84+ :param index: an index indicating which Dex file should the returned
85+ object open
86+ :return: a Rizin object opening the specified Dex file
87+ """
8188 rz = rzpipe .open (self ._dex_list [index ])
8289 rz .cmd ("aa" )
8390 return rz
8491
8592 def _convert_type_to_type_signature (self , raw_type : str ):
93+ """
94+ Convert a Java type in the format of the Java language into the
95+ one in the format of the Java VM type signature.
96+
97+ For example,
98+ + `int` will be converted into the Java VM type signature `I`.
99+ + `long` will be converted into the Java VM type signature `L`.
100+ + `String...` will be converted into the Java VM type signature
101+ `[Ljava/lang/String;`.
102+
103+ :param raw_type: a type in the format of the Java language
104+ :return: a type in the format of the Java VM type signature
105+ """
86106 if not raw_type :
87107 return raw_type
88108
@@ -108,11 +128,29 @@ def _convert_type_to_type_signature(self, raw_type: str):
108128
109129 @staticmethod
110130 def _escape_str_in_rizin_manner (raw_str : str ):
131+ """
132+ Convert characters with special meanings in Rizin into `_`.
133+ For now, these characters are `<`, `>` and `$`.
134+
135+ :param raw_str: a string that may consist of characters with special
136+ meanings.
137+ :return: a new string contains no characters with special meanings.
138+ """
111139 for c in RIZIN_ESCAPE_CHAR_LIST :
112140 raw_str = raw_str .replace (c , "_" )
113141 return raw_str
114142
115143 def _parse_method_from_isj_obj (self , json_obj , dexindex ):
144+ """
145+ Parse a JSON object provided by the Rizin command `isj` or `is.j` into
146+ an instance of MethodObject.
147+
148+ :param json_obj: a JSON object provided by the Rizin command `isj` or
149+ `is.j`
150+ :param dexindex: an index indicating from which Dex file the JSON
151+ object is generated
152+ :return: an instance of MethodObject
153+ """
116154 if json_obj .get ("type" ) not in ["FUNC" , "METH" ]:
117155 return None
118156
@@ -215,6 +253,16 @@ def _parse_method_from_isj_obj(self, json_obj, dexindex):
215253
216254 @functools .lru_cache
217255 def _get_methods_classified (self , dexindex ):
256+ """
257+ Parse all methods in the specified Dex and convert them into a
258+ dictionary. The dictionary takes their belonging classes as the keys.
259+ Then, it categorizes them into lists.
260+
261+ :param dexindex: an index indicating which Dex file should this method
262+ parse
263+ :return: a dictionary taking a class name as the key and a list of
264+ MethodObject as the corresponding value.
265+ """
218266 rz = self ._get_rz (dexindex )
219267
220268 method_json_list = rz .cmdj ("isj" )
@@ -233,6 +281,12 @@ def _get_methods_classified(self, dexindex):
233281
234282 @functools .cached_property
235283 def permissions (self ) -> List [str ]:
284+ """
285+ Inherited from baseapkinfo.py.
286+ Return the permissions used by the sample.
287+
288+ :return: a list of permissions.
289+ """
236290 axml = AxmlReader (self ._manifest )
237291 permission_list = set ()
238292
@@ -273,6 +327,12 @@ def activities(self) -> List[XMLElement]:
273327
274328 @property
275329 def android_apis (self ) -> Set [MethodObject ]:
330+ """
331+ Inherited from baseapkinfo.py.
332+ Return all Android native APIs used by the sample.
333+
334+ :return: a set of MethodObjects
335+ """
276336 return {
277337 method
278338 for method in self .all_methods
@@ -281,10 +341,27 @@ def android_apis(self) -> Set[MethodObject]:
281341
282342 @property
283343 def custom_methods (self ) -> Set [MethodObject ]:
284- return {method for method in self .all_methods if not method .cache .is_imported }
344+ """_
345+ Inherited from baseapkinfo.py.
346+ Return all custom methods declared by the sample.
347+
348+ :return: a set of MethodObjects
349+ """
350+ return {
351+ method
352+ for method in self .all_methods
353+ if not method .cache .is_imported
354+ }
285355
286356 @functools .cached_property
287357 def all_methods (self ) -> Set [MethodObject ]:
358+ """_
359+ Inherited from baseapkinfo.py.
360+ Return all methods including Android native APIs and custom methods
361+ declared in the sample.
362+
363+ :return: a set of MethodObjects
364+ """
288365 method_set = set ()
289366 for dex_index in range (self ._number_of_dex ):
290367 for method_list in self ._get_methods_classified (dex_index ).values ():
@@ -298,6 +375,18 @@ def find_method(
298375 method_name : Optional [str ] = ".*" ,
299376 descriptor : Optional [str ] = ".*" ,
300377 ) -> List [MethodObject ]:
378+ """
379+ Inherited from baseapkinfo.py.
380+ Find a method with the given class name, method name, and descriptor.
381+
382+ :param class_name: the class name of the target method. Defaults to
383+ ".*"
384+ :param method_name: the method name of the target method. Defaults to
385+ ".*"
386+ :param descriptor: the descriptor of the target method. Defaults to
387+ ".*"
388+ :return: a list of the target MethodObject
389+ """
301390 if not class_name :
302391 class_name = ".*"
303392
@@ -339,6 +428,14 @@ def method_filter(method):
339428
340429 @functools .lru_cache
341430 def upperfunc (self , method_object : MethodObject ) -> Set [MethodObject ]:
431+ """
432+ Inherited from baseapkinfo.py.
433+ Find the xrefs from the specified method.
434+
435+ :param method_object: a target method which the returned methods
436+ should call
437+ :return: a set of MethodObjects
438+ """
342439 cache = method_object .cache
343440
344441 r2 = self ._get_rz (cache .dexindex )
@@ -368,7 +465,18 @@ def upperfunc(self, method_object: MethodObject) -> Set[MethodObject]:
368465 return upperfunc_set
369466
370467 @functools .lru_cache
371- def lowerfunc (self , method_object : MethodObject ) -> Set [MethodObject ]:
468+ def lowerfunc (
469+ self , method_object : MethodObject
470+ ) -> Set [Tuple [MethodObject , int ]]:
471+ """
472+ Inherited from baseapkinfo.py.
473+ Find the xrefs to the specified method.
474+
475+ :param method_object: a target method used to find what methods it
476+ calls
477+ :return: a set of tuples consisting of the called method and the
478+ offset of the invocation
479+ """
372480 cache = method_object .cache
373481
374482 rz = self ._get_rz (cache .dexindex )
@@ -401,6 +509,14 @@ def lowerfunc(self, method_object: MethodObject) -> Set[MethodObject]:
401509 def get_method_bytecode (
402510 self , method_object : MethodObject
403511 ) -> Generator [BytecodeObject , None , None ]:
512+ """
513+ Inherited from baseapkinfo.py.
514+ Return the bytecodes of the specified method.
515+
516+ :param method_object: a target method to get the corresponding
517+ bytecodes
518+ :yield: a generator of BytecodeObjects
519+ """
404520 cache = method_object .cache
405521
406522 if not cache .is_imported :
@@ -414,6 +530,12 @@ def get_method_bytecode(
414530 yield self ._parse_smali (ins ["disasm" ])
415531
416532 def get_strings (self ) -> Set [str ]:
533+ """
534+ Inherited from baseapkinfo.py.
535+ Return all strings in the sample.
536+
537+ :return: a set of strings
538+ """
417539 strings = set ()
418540 for dex_index in range (self ._number_of_dex ):
419541 rz = self ._get_rz (dex_index )
@@ -431,6 +553,19 @@ def get_wrapper_smali(
431553 first_method : MethodObject ,
432554 second_method : MethodObject ,
433555 ) -> Dict [str , Union [BytecodeObject , str ]]:
556+ """
557+ Inherited from baseapkinfo.py.
558+ Find the invocations that call two specified methods, first_method
559+ and second_method, respectively. Then, return a dictionary storing
560+ the corresponding bytecodes and hex values.
561+
562+ :param parent_method: a parent method to scan
563+ :param first_method: the first method called by the parent method
564+ :param second_method: the second method called by the parent method
565+ :return: a dictionary storing the corresponding bytecodes and hex
566+ values.
567+ """
568+
434569 def convert_bytecode_to_list (bytecode ):
435570 return [bytecode .mnemonic ] + bytecode .registers + [bytecode .parameter ]
436571
@@ -494,6 +629,15 @@ def convert_bytecode_to_list(bytecode):
494629
495630 @functools .cached_property
496631 def superclass_relationships (self ) -> Dict [str , Set [str ]]:
632+ """
633+ Inherited from baseapkinfo.py.
634+ Return a dictionary holding the inheritance relationship of classes in
635+ the sample. The dictionary takes a class name as the key and the
636+ corresponding superclass as the value.
637+
638+ :return: a dictionary taking a class name as the key and the
639+ corresponding superclass as the value.
640+ """
497641 hierarchy_dict = defaultdict (set )
498642
499643 for dex_index in range (self ._number_of_dex ):
@@ -511,6 +655,16 @@ def superclass_relationships(self) -> Dict[str, Set[str]]:
511655
512656 @functools .cached_property
513657 def subclass_relationships (self ) -> Dict [str , Set [str ]]:
658+ """
659+ Inherited from baseapkinfo.py.
660+ Return a dictionary holding the inheritance relationship of classes in
661+ the sample. Return a dictionary holding the inheritance relationship
662+ of classes in the sample. The dictionary takes a class name as the key
663+ and the corresponding subclasses as the value.
664+
665+ :return: a dictionary taking a class name as the key and the
666+ corresponding subclasses as the value.
667+ """
514668 hierarchy_dict = defaultdict (set )
515669
516670 for dex_index in range (self ._number_of_dex ):
@@ -527,6 +681,12 @@ def subclass_relationships(self) -> Dict[str, Set[str]]:
527681 return hierarchy_dict
528682
529683 def _get_method_by_address (self , address : int ) -> MethodObject :
684+ """
685+ Find a method via a specified address.
686+
687+ :param address: an address used to find the corresponding method
688+ :return: the MethodObject of the method in the given address
689+ """
530690 dexindex = 0
531691
532692 rz = self ._get_rz (dexindex )
@@ -558,6 +718,14 @@ def _parse_parameter(mnemonic: str, parameter: str) -> Any:
558718
559719 @staticmethod
560720 def _parse_smali (smali : str ) -> BytecodeObject :
721+ """
722+ Convert a Smali code provided by the Rizin command `pdfj` into a
723+ BytecodeObject.
724+
725+ :param smali: a Smali code provided by the Rizin command `pdfj`
726+ :raises ValueError: if the Smali code follows an unknown format
727+ :return: a BytecodeObject
728+ """
561729 if smali == "" :
562730 raise ValueError ("Argument cannot be empty." )
563731
0 commit comments