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
@@ -261,6 +315,12 @@ def activities(self) -> List[XMLElement]:
261315
262316 @property
263317 def android_apis (self ) -> Set [MethodObject ]:
318+ """
319+ Inherited from baseapkinfo.py.
320+ Return all Android native APIs used by the sample.
321+
322+ :return: a set of MethodObjects
323+ """
264324 return {
265325 method
266326 for method in self .all_methods
@@ -269,10 +329,27 @@ def android_apis(self) -> Set[MethodObject]:
269329
270330 @property
271331 def custom_methods (self ) -> Set [MethodObject ]:
272- return {method for method in self .all_methods if not method .cache .is_imported }
332+ """_
333+ Inherited from baseapkinfo.py.
334+ Return all custom methods declared by the sample.
335+
336+ :return: a set of MethodObjects
337+ """
338+ return {
339+ method
340+ for method in self .all_methods
341+ if not method .cache .is_imported
342+ }
273343
274344 @functools .cached_property
275345 def all_methods (self ) -> Set [MethodObject ]:
346+ """_
347+ Inherited from baseapkinfo.py.
348+ Return all methods including Android native APIs and custom methods
349+ declared in the sample.
350+
351+ :return: a set of MethodObjects
352+ """
276353 method_set = set ()
277354 for dex_index in range (self ._number_of_dex ):
278355 for method_list in self ._get_methods_classified (dex_index ).values ():
@@ -286,6 +363,19 @@ def find_method(
286363 method_name : Optional [str ] = ".*" ,
287364 descriptor : Optional [str ] = ".*" ,
288365 ) -> MethodObject :
366+ """
367+ Inherited from baseapkinfo.py.
368+ Find a method with the given class name, method name, and descriptor.
369+
370+ :param class_name: the class name of the target method. Defaults to
371+ ".*"
372+ :param method_name: the method name of the target method. Defaults to
373+ ".*"
374+ :param descriptor: the descriptor of the target method. Defaults to
375+ ".*"
376+ :return: a MethodObject of the target method
377+ """
378+
289379 def method_filter (method ):
290380 return (not method_name or method_name == method .name ) and (
291381 not descriptor or descriptor == method .descriptor
@@ -303,6 +393,14 @@ def method_filter(method):
303393
304394 @functools .lru_cache
305395 def upperfunc (self , method_object : MethodObject ) -> Set [MethodObject ]:
396+ """
397+ Inherited from baseapkinfo.py.
398+ Find the xrefs from the specified method.
399+
400+ :param method_object: a target method which the returned methods
401+ should call
402+ :return: a set of MethodObjects
403+ """
306404 cache = method_object .cache
307405
308406 r2 = self ._get_rz (cache .dexindex )
@@ -332,7 +430,18 @@ def upperfunc(self, method_object: MethodObject) -> Set[MethodObject]:
332430 return upperfunc_set
333431
334432 @functools .lru_cache
335- def lowerfunc (self , method_object : MethodObject ) -> Set [MethodObject ]:
433+ def lowerfunc (
434+ self , method_object : MethodObject
435+ ) -> Set [Tuple [MethodObject , int ]]:
436+ """
437+ Inherited from baseapkinfo.py.
438+ Find the xrefs to the specified method.
439+
440+ :param method_object: a target method used to find what methods it
441+ calls
442+ :return: a set of tuples consisting of the called method and the
443+ offset of the invocation
444+ """
336445 cache = method_object .cache
337446
338447 rz = self ._get_rz (cache .dexindex )
@@ -365,6 +474,14 @@ def lowerfunc(self, method_object: MethodObject) -> Set[MethodObject]:
365474 def get_method_bytecode (
366475 self , method_object : MethodObject
367476 ) -> Generator [BytecodeObject , None , None ]:
477+ """
478+ Inherited from baseapkinfo.py.
479+ Return the bytecodes of the specified method.
480+
481+ :param method_object: a target method to get the corresponding
482+ bytecodes
483+ :yield: a generator of BytecodeObjects
484+ """
368485 cache = method_object .cache
369486
370487 if not cache .is_imported :
@@ -378,6 +495,12 @@ def get_method_bytecode(
378495 yield self ._parse_smali (ins ["disasm" ])
379496
380497 def get_strings (self ) -> Set [str ]:
498+ """
499+ Inherited from baseapkinfo.py.
500+ Return all strings in the sample.
501+
502+ :return: a set of strings
503+ """
381504 strings = set ()
382505 for dex_index in range (self ._number_of_dex ):
383506 rz = self ._get_rz (dex_index )
@@ -395,6 +518,19 @@ def get_wrapper_smali(
395518 first_method : MethodObject ,
396519 second_method : MethodObject ,
397520 ) -> Dict [str , Union [BytecodeObject , str ]]:
521+ """
522+ Inherited from baseapkinfo.py.
523+ Find the invocations that call two specified methods, first_method
524+ and second_method, respectively. Then, return a dictionary storing
525+ the corresponding bytecodes and hex values.
526+
527+ :param parent_method: a parent method to scan
528+ :param first_method: the first method called by the parent method
529+ :param second_method: the second method called by the parent method
530+ :return: a dictionary storing the corresponding bytecodes and hex
531+ values.
532+ """
533+
398534 def convert_bytecode_to_list (bytecode ):
399535 return [bytecode .mnemonic ] + bytecode .registers + [bytecode .parameter ]
400536
@@ -458,6 +594,15 @@ def convert_bytecode_to_list(bytecode):
458594
459595 @functools .cached_property
460596 def superclass_relationships (self ) -> Dict [str , Set [str ]]:
597+ """
598+ Inherited from baseapkinfo.py.
599+ Return a dictionary holding the inheritance relationship of classes in
600+ the sample. The dictionary takes a class name as the key and the
601+ corresponding superclass as the value.
602+
603+ :return: a dictionary taking a class name as the key and the
604+ corresponding superclass as the value.
605+ """
461606 hierarchy_dict = defaultdict (set )
462607
463608 for dex_index in range (self ._number_of_dex ):
@@ -475,6 +620,16 @@ def superclass_relationships(self) -> Dict[str, Set[str]]:
475620
476621 @functools .cached_property
477622 def subclass_relationships (self ) -> Dict [str , Set [str ]]:
623+ """
624+ Inherited from baseapkinfo.py.
625+ Return a dictionary holding the inheritance relationship of classes in
626+ the sample. Return a dictionary holding the inheritance relationship
627+ of classes in the sample. The dictionary takes a class name as the key
628+ and the corresponding subclasses as the value.
629+
630+ :return: a dictionary taking a class name as the key and the
631+ corresponding subclasses as the value.
632+ """
478633 hierarchy_dict = defaultdict (set )
479634
480635 for dex_index in range (self ._number_of_dex ):
@@ -491,6 +646,12 @@ def subclass_relationships(self) -> Dict[str, Set[str]]:
491646 return hierarchy_dict
492647
493648 def _get_method_by_address (self , address : int ) -> MethodObject :
649+ """
650+ Find a method via a specified address.
651+
652+ :param address: an address used to find the corresponding method
653+ :return: the MethodObject of the method in the given address
654+ """
494655 dexindex = 0
495656
496657 rz = self ._get_rz (dexindex )
@@ -522,6 +683,14 @@ def _parse_parameter(mnemonic: str, parameter: str) -> Any:
522683
523684 @staticmethod
524685 def _parse_smali (smali : str ) -> BytecodeObject :
686+ """
687+ Convert a Smali code provided by the Rizin command `pdfj` into a
688+ BytecodeObject.
689+
690+ :param smali: a Smali code provided by the Rizin command `pdfj`
691+ :raises ValueError: if the Smali code follows an unknown format
692+ :return: a BytecodeObject
693+ """
525694 if smali == "" :
526695 raise ValueError ("Argument cannot be empty." )
527696
0 commit comments