Skip to content

Commit 2039970

Browse files
haeter525sidra-asa
authored andcommitted
Update docstrings for rzapkinfo.py
1 parent 0acf35c commit 2039970

File tree

1 file changed

+172
-3
lines changed

1 file changed

+172
-3
lines changed

quark/core/rzapkinfo.py

Lines changed: 172 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import zipfile
1111
from collections import defaultdict, namedtuple
1212
from 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

1515
import 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

Comments
 (0)