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
@@ -285,6 +339,12 @@ def receivers(self) -> List[XMLElement]:
285339
286340 @property
287341 def android_apis (self ) -> Set [MethodObject ]:
342+ """
343+ Inherited from baseapkinfo.py.
344+ Return all Android native APIs used by the sample.
345+
346+ :return: a set of MethodObjects
347+ """
288348 return {
289349 method
290350 for method in self .all_methods
@@ -293,10 +353,27 @@ def android_apis(self) -> Set[MethodObject]:
293353
294354 @property
295355 def custom_methods (self ) -> Set [MethodObject ]:
296- return {method for method in self .all_methods if not method .cache .is_imported }
356+ """_
357+ Inherited from baseapkinfo.py.
358+ Return all custom methods declared by the sample.
359+
360+ :return: a set of MethodObjects
361+ """
362+ return {
363+ method
364+ for method in self .all_methods
365+ if not method .cache .is_imported
366+ }
297367
298368 @functools .cached_property
299369 def all_methods (self ) -> Set [MethodObject ]:
370+ """_
371+ Inherited from baseapkinfo.py.
372+ Return all methods including Android native APIs and custom methods
373+ declared in the sample.
374+
375+ :return: a set of MethodObjects
376+ """
300377 method_set = set ()
301378 for dex_index in range (self ._number_of_dex ):
302379 for method_list in self ._get_methods_classified (dex_index ).values ():
@@ -310,6 +387,18 @@ def find_method(
310387 method_name : Optional [str ] = ".*" ,
311388 descriptor : Optional [str ] = ".*" ,
312389 ) -> List [MethodObject ]:
390+ """
391+ Inherited from baseapkinfo.py.
392+ Find a method with the given class name, method name, and descriptor.
393+
394+ :param class_name: the class name of the target method. Defaults to
395+ ".*"
396+ :param method_name: the method name of the target method. Defaults to
397+ ".*"
398+ :param descriptor: the descriptor of the target method. Defaults to
399+ ".*"
400+ :return: a MethodObject of the target method
401+ """
313402 if not class_name :
314403 class_name = ".*"
315404
@@ -351,6 +440,14 @@ def method_filter(method):
351440
352441 @functools .lru_cache
353442 def upperfunc (self , method_object : MethodObject ) -> Set [MethodObject ]:
443+ """
444+ Inherited from baseapkinfo.py.
445+ Find the xrefs from the specified method.
446+
447+ :param method_object: a target method which the returned methods
448+ should call
449+ :return: a set of MethodObjects
450+ """
354451 cache = method_object .cache
355452
356453 r2 = self ._get_rz (cache .dexindex )
@@ -380,7 +477,18 @@ def upperfunc(self, method_object: MethodObject) -> Set[MethodObject]:
380477 return upperfunc_set
381478
382479 @functools .lru_cache
383- def lowerfunc (self , method_object : MethodObject ) -> Set [MethodObject ]:
480+ def lowerfunc (
481+ self , method_object : MethodObject
482+ ) -> Set [Tuple [MethodObject , int ]]:
483+ """
484+ Inherited from baseapkinfo.py.
485+ Find the xrefs to the specified method.
486+
487+ :param method_object: a target method used to find what methods it
488+ calls
489+ :return: a set of tuples consisting of the called method and the
490+ offset of the invocation
491+ """
384492 cache = method_object .cache
385493
386494 rz = self ._get_rz (cache .dexindex )
@@ -413,6 +521,14 @@ def lowerfunc(self, method_object: MethodObject) -> Set[MethodObject]:
413521 def get_method_bytecode (
414522 self , method_object : MethodObject
415523 ) -> Generator [BytecodeObject , None , None ]:
524+ """
525+ Inherited from baseapkinfo.py.
526+ Return the bytecodes of the specified method.
527+
528+ :param method_object: a target method to get the corresponding
529+ bytecodes
530+ :yield: a generator of BytecodeObjects
531+ """
416532 cache = method_object .cache
417533
418534 if not cache .is_imported :
@@ -426,6 +542,12 @@ def get_method_bytecode(
426542 yield self ._parse_smali (ins ["disasm" ])
427543
428544 def get_strings (self ) -> Set [str ]:
545+ """
546+ Inherited from baseapkinfo.py.
547+ Return all strings in the sample.
548+
549+ :return: a set of strings
550+ """
429551 strings = set ()
430552 for dex_index in range (self ._number_of_dex ):
431553 rz = self ._get_rz (dex_index )
@@ -443,6 +565,19 @@ def get_wrapper_smali(
443565 first_method : MethodObject ,
444566 second_method : MethodObject ,
445567 ) -> Dict [str , Union [BytecodeObject , str ]]:
568+ """
569+ Inherited from baseapkinfo.py.
570+ Find the invocations that call two specified methods, first_method
571+ and second_method, respectively. Then, return a dictionary storing
572+ the corresponding bytecodes and hex values.
573+
574+ :param parent_method: a parent method to scan
575+ :param first_method: the first method called by the parent method
576+ :param second_method: the second method called by the parent method
577+ :return: a dictionary storing the corresponding bytecodes and hex
578+ values.
579+ """
580+
446581 def convert_bytecode_to_list (bytecode ):
447582 return [bytecode .mnemonic ] + bytecode .registers + [bytecode .parameter ]
448583
@@ -506,6 +641,15 @@ def convert_bytecode_to_list(bytecode):
506641
507642 @functools .cached_property
508643 def superclass_relationships (self ) -> Dict [str , Set [str ]]:
644+ """
645+ Inherited from baseapkinfo.py.
646+ Return a dictionary holding the inheritance relationship of classes in
647+ the sample. The dictionary takes a class name as the key and the
648+ corresponding superclass as the value.
649+
650+ :return: a dictionary taking a class name as the key and the
651+ corresponding superclass as the value.
652+ """
509653 hierarchy_dict = defaultdict (set )
510654
511655 for dex_index in range (self ._number_of_dex ):
@@ -523,6 +667,16 @@ def superclass_relationships(self) -> Dict[str, Set[str]]:
523667
524668 @functools .cached_property
525669 def subclass_relationships (self ) -> Dict [str , Set [str ]]:
670+ """
671+ Inherited from baseapkinfo.py.
672+ Return a dictionary holding the inheritance relationship of classes in
673+ the sample. Return a dictionary holding the inheritance relationship
674+ of classes in the sample. The dictionary takes a class name as the key
675+ and the corresponding subclasses as the value.
676+
677+ :return: a dictionary taking a class name as the key and the
678+ corresponding subclasses as the value.
679+ """
526680 hierarchy_dict = defaultdict (set )
527681
528682 for dex_index in range (self ._number_of_dex ):
@@ -539,6 +693,12 @@ def subclass_relationships(self) -> Dict[str, Set[str]]:
539693 return hierarchy_dict
540694
541695 def _get_method_by_address (self , address : int ) -> MethodObject :
696+ """
697+ Find a method via a specified address.
698+
699+ :param address: an address used to find the corresponding method
700+ :return: the MethodObject of the method in the given address
701+ """
542702 dexindex = 0
543703
544704 rz = self ._get_rz (dexindex )
@@ -570,6 +730,14 @@ def _parse_parameter(mnemonic: str, parameter: str) -> Any:
570730
571731 @staticmethod
572732 def _parse_smali (smali : str ) -> BytecodeObject :
733+ """
734+ Convert a Smali code provided by the Rizin command `pdfj` into a
735+ BytecodeObject.
736+
737+ :param smali: a Smali code provided by the Rizin command `pdfj`
738+ :raises ValueError: if the Smali code follows an unknown format
739+ :return: a BytecodeObject
740+ """
573741 if smali == "" :
574742 raise ValueError ("Argument cannot be empty." )
575743
0 commit comments